文章大綱
- 引言
- 一、Dumpsys 命令概述
- 二、 Dumpsys命令語法詳解
- 1、Package資訊查詢
- 2、Activity資訊查詢
- 3、網路資訊查詢
- 4、其他常用服務資訊查詢
- 三、Dumpsys命令實作原理
- 1、安卓binder服務管理
- 2、Dumpsys原始碼分析
引言
Dumpsys是安卓系統提供用來動態查看系統內部服務運行狀態的命令,了解dumpsys命令的使用方法,可以方便我們查看系統中各個行程記憶體使用情況,各個行程CPU占用率情況,各個應用apk運行情況,系統喚醒源情況等,同時,了解dumpsys命令使用方法的程序也是我們深入了解安卓各個服務的程序,
一、Dumpsys 命令概述
在Android系統中,各個系統服務行程之間主要是通過binder或者socket實作跨行程通信,大部分的服務程式使用binder通信的方式提供API介面,所有使用binder通信方式提供API介面的服務均由一個系統核心的服務管理行程——ServiceManager行程進行同一管理,其中Dumpsys命令就是Android系統提供用來查詢ServiceManager中所有服務的狀態以及服務內部詳細資訊的工具,其對應的Dumpsys原始碼檔案——frameworks/native/cmds/dumpsys/dumpsys.cpp,在Android系統編譯完成后,dumpsys命令會被打包在system.img中,Dumpsys命令可以理解成Android 獨有的shell 程式,使用是 首先adb shell 進入到Android 系統環境,再根據以下發育執行相關命令:
dumpsys [-l][service]
| 命令 | 說明 |
|---|---|
| dumpsys | 輸出設備中所有服務以及服務內部詳細資訊 |
| dumpsys -l | 輸出設備中所有的服務名稱 |
| dumpsys service | 輸出名字為service的服務內部詳細資訊 |
二、 Dumpsys命令語法詳解
1、Package資訊查詢
命令格式:
dumpsys package [-h][-f][--checkin][cmd]…
| 引數 | 說明 |
|---|---|
| -h | 輸出package子命令幫助資訊 |
| -f | 輸出intent filter資訊 |
| –checkin | 輸出已經登記的安裝包 |
| cmd | 子命令(可以在-h中查看有哪些子命令) |
其中子命令:
| cmd子命令 | 說明 |
|---|---|
| prov[iders] | 獲取content providers資訊 |
| p[ackages] | 獲取安裝包資訊 |
| s[hared-user] | 獲取共享用戶ID的應用 |
| m[essages] | 列印運行時收集的資訊 |
| v[erifiers] | 列印包校驗資訊 |
| version | 列印資料庫版本資訊 |
| write | 寫當前位置 |
| <package.name> | 輸出給定包的資訊 |
| installs | 安裝會話的詳細資訊 |
| l[ibraries] | 列出已知的共享庫 |
| f[ibraries] | 列出手機的功能 |
| k[eysets] | 列出各個包的Signing KeySets |
| r[esolvers] | 獲取intent filter |
2、Activity資訊查詢
命令格式:
dumpsys activity [-h][-a][-c][-p]…
| 引數 | 說明 |
|---|---|
| -h | 輸出activity子命令幫助資訊 |
| -a | 輸出所有可用服務端狀態 |
| -c | 輸出客戶端狀態 |
| -p | 輸出指定包的activity詳細資訊 |
| cmd | 子命令(可以在-h中查看有哪些子命令) |
子命令:
| cmd子命令 | 說明 |
|---|---|
| a[ctivities] | activity堆疊狀態 |
| r[recents] | 最近activity的狀態 |
| b[rodacasts] [package_name] [history [-s]] | 廣播狀態 |
| i[ntents] [package_name] | 掛起的intent狀態 |
| p[rocesses] [package_name] | 行程狀態 |
| o[om] | oom管理 |
| perm[issions] | URI權限授權狀態 |
| prov[iders] [comp_spec…] | content provider狀態 |
| provider [comp_spec] | provider客戶端狀態 |
| s[ervices] [comp_spec…] | 服務狀態 |
| as[sociations] | 跟蹤應用程式的關聯 |
| service [comp_spec] | 服務客戶端狀態 |
| package [package_name] | 指定包的所有狀態 |
| all | dump所有activities資訊 |
3、網路資訊查詢
安卓系統網路連接和管理服務由四個系統服務ConnectivityService,NetworkPolicyManagerService,NetworkManagementService,NetworkStatsService共同配合完成網路連接和管理功能,因此,要查看設備網路相關資訊,就需要使用dumpsys命令分別查看設備中這些服務的詳細資訊:
| 命令 | 說明 |
|---|---|
| dumpsys connectivity | 查看設備當前網路連接狀態 |
| dumpsys netpolicy | 查看設備網路策略 |
| dumpsys netstats | 查看設備網路狀態 |
| dumpsys network_management | 查看設備網路管理服務資訊 |
4、其他常用服務資訊查詢
| 命令 | 說明 |
|---|---|
| dumpsys meminfo | 查看系統各個行程(某一個行程)記憶體使用情況 |
| dumpsys cpuinfo | 查看系統各個行程CPU占用情況 |
| dumpsys power | 查看系統應用申請wakelock情況 |
| dumpsys batterstats | 查看系統各個應用(行程)耗電情況 |
| dumpsys alarm | 查看系統RTC定時器使用情況 |
| dumpsys wifi | 查看wifi網路使用情況 |
| dumpsys media.player | 查看系統native播放器以及recorder創建以及使用情況 |
三、Dumpsys命令實作原理
1、安卓binder服務管理
Dumpsys基于安卓的binder通信機制實作,因此了解安卓binder通信機制是基礎,安卓binder通信框架如圖1所示(Binder通信機制詳細內容請參考[1]&[2]),它由客戶端程式,服務端程式,ServiceManager行程,binder驅動四大部分組成,Binder即可作為行程間通信也可以作為行程內通信,它的優勢在于客戶端與服務端通信只需要一次資料拷貝,

如上圖所示Binder通信是一個C/S架構,它們通過kernel的binder driver建立通信橋梁,ServiceManager在此框架中的作用相當于一個“路由器”,所有的服務通過addService介面把服務的“句柄”和名字注冊到ServiceManager中,應用程式(客戶端)呼叫服務介面時,首先使用ServiceManager的checkService/getService介面通過服務名字獲取該服務的“句柄”,然后通過服務“句柄”呼叫相應的服務介面(BpBinder是client端創建的用于訊息發送的代理,而BBinder是server端用于接收訊息的通道),其互動程序如圖2所示服務“句柄”管理機制:

2、Dumpsys原始碼分析
Dumpsys命令在原始碼位于:frameworks/native/cmds/dumpsys/dumpsys.cpp,
int main(int argc, char* const argv[])
{
//捕捉信號
signal(SIGPIPE, SIG_IGN);
//獲取 ServiceManager服務的binder代理物件
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == NULL) {
aerr << "dumpsys: Unable to get default service manager!" << endl;
return 20;
}
Vector<String16> services;
Vector<String16> args;
Vector<String16> skippedServices;
bool showListOnly = false;
bool skipServices = false;
int timeoutArg = 10;
static struct option longOptions[] = {
{"skip", no_argument, 0, 0 },
{"help", no_argument, 0, 0 },
{ 0, 0, 0, 0 }
};
while (1) {
int c;
int optionIndex = 0;
//傳入的引數轉為case 的值
c = getopt_long(argc, argv, "+t:l", longOptions, &optionIndex);
if (c == -1) {
break;
}
switch (c) {
case 0:
if (!strcmp(longOptions[optionIndex].name, "skip")) {
skipServices = true;
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
usage();
return 0;
}
break;
case 't':
{
char *endptr;
timeoutArg = strtol(optarg, &endptr, 10);
if (*endptr != '\0' || timeoutArg <= 0) {
fprintf(stderr, "Error: invalid timeout number: '%s'\n", optarg);
return -1;
}
}
break;
case 'l':
showListOnly = true;
break;
default:
fprintf(stderr, "\n");
usage();
return -1;
}
}
for (int i = optind; i < argc; i++) {
if (skipServices) {
skippedServices.add(String16(argv[i]));
} else {
if (i == optind) {
services.add(String16(argv[i]));
} else {
args.add(String16(argv[i]));
}
}
}
if ((skipServices && skippedServices.empty()) ||
(showListOnly && (!services.empty() || !skippedServices.empty()))) {
usage();
return -1;
}
if (services.empty() || showListOnly) {
// gets all services 通過ServiceManager獲取所有的服務
services = sm->listServices();
services.sort(sort_func);
args.add(String16("-a"));
}
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
bool skipped = IsSkipped(skippedServices, services[i]);
aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl;
}
}
}
if (showListOnly) {
return 0;
}
for (size_t i = 0; i < N; i++) {
String16 service_name = std::move(services[i]);
if (IsSkipped(skippedServices, service_name)) continue;
sp<IBinder> service = sm->checkService(service_name);
if (service != NULL) {
int sfd[2];
if (pipe(sfd) != 0) {
aerr << "Failed to create pipe to dump service info for " << service_name
<< ": " << strerror(errno) << endl;
continue;
}
unique_fd local_end(sfd[0]);
unique_fd remote_end(sfd[1]);
sfd[0] = sfd[1] = -1;
if (N > 1) {
aout << "-------------" << endl;
aout << "DUMP OF SERVICE " << service_name << ":" << endl;
}
// dump blocks until completion, so spawn a thread..
std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
//呼叫各自服務里的dump函式
int err = service->dump(remote_end.get(), args);
// It'd be nice to be able to close the remote end of the socketpair before the dump
// call returns, to terminate our reads if the other end closes their copy of the
// file descriptor, but then hangs for some reason. There doesn't seem to be a good
// way to do this, though.
remote_end.clear();
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
<< endl;
}
});
auto timeout = std::chrono::seconds(timeoutArg);
auto start = std::chrono::steady_clock::now();
auto end = start + timeout;
struct pollfd pfd = {
.fd = local_end.get(),
.events = POLLIN
};
bool timed_out = false;
bool error = false;
while (true) {
// Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
auto time_left_ms = [end]() {
auto now = std::chrono::steady_clock::now();
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
return std::max(diff.count(), 0ll);
};
int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
if (rc < 0) {
aerr << "Error in poll while dumping service " << service_name << " : "
<< strerror(errno) << endl;
error = true;
break;
} else if (rc == 0) {
timed_out = true;
break;
}
char buf[4096];
rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
if (rc < 0) {
aerr << "Failed to read while dumping service " << service_name << ": "
<< strerror(errno) << endl;
error = true;
break;
} else if (rc == 0) {
// EOF.
break;
}
if (!WriteFully(STDOUT_FILENO, buf, rc)) {
aerr << "Failed to write while dumping service " << service_name << ": "
<< strerror(errno) << endl;
error = true;
break;
}
}
if (timed_out) {
aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
}
if (timed_out || error) {
dump_thread.detach();
} else {
dump_thread.join();
}
if (N > 1) {
std::chrono::duration<double> elapsed_seconds =
std::chrono::steady_clock::now() - start;
aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
<< "was the duration of dumpsys " << service_name << endl;
}
} else {
aerr << "Can't find service: " << service_name << endl;
}
}
return 0;
}
從上面原始碼中可知
- sp< IServiceManager > sm = defaultServiceManager(); 獲取ServiceManager Binder代理物件
- services = sm->listServices(); 通過ServiceManager獲取所有的服務名稱
- sp< IBinder > service = sm->checkService(service_name); 通過服務名稱獲取對應服務的Binder代理物件
- 通過服務的Binder代理物件呼叫各自的dump 函式
dumpsys呼叫ServiceManager服務的listServices來查詢系統中的所有服務的名稱,并且通過checkService方法獲取對應服務的binder代理物件,然后通過每個服務binder代理物件呼叫dump函式來輸出該服務的詳細資訊,因此dumpsys命令輸出各個服務的詳細資訊是沒有統一格式的,另外,dump方法提供一個用戶輸入引數的通道,利用此特性,可以動態修改服務中的某些引數,例如,安卓系統中native層的camera服務,其服務內部的除錯列印資訊由一個全域變數gLogLevel控制(原始碼:frameworks/av/services/camera/libcameraservice/CameraService.cpp),在dump方法中提供了設定gLogLevel的介面:
status_t CameraService::dump(int fd, const Vector<String16>& args) {
//輸出CameraService內部詳細狀態資訊
int n = args.size();
for (int i = 0; i + 1 < n; i++) {
//-v 引數用于修改gLogLevel變數
String16 verboseOption("-v");
if (args[i] == verboseOption) {
String8 levelStr(args[i+1]);
int level = atoi(levelStr.string());
result = String8::format("\nSetting log level to %d.\n", level);
setLogLevel(level);
write(fd, result.string(), result.size());
}
}
return NO_ERROR;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292989.html
標籤:其他
