Dumpsys這個(gè)命令了解或者用過的人肯定都知道,他可以提供很多有用的信息,供開發(fā)者來定位問題。是一個(gè)很強(qiáng)大的debug工具。接下來埠偿,會從源碼的角度來介紹一下這個(gè)命令的實(shí)現(xiàn)方式透罢。
注:已Android 8.0源碼來分析
在系統(tǒng)編譯時(shí)會編譯到如下Android.bp文件。
frameworks/native/cmds/dumpsys/Android.bp
cc_defaults {
name: "dumpsys_defaults",
cflags: [
"-Wall",
"-Werror",
],
srcs: [
"dumpsys.cpp",
],
shared_libs: [
"libbase",
"libutils",
"liblog",
"libbinder",
],
clang: true,
}
//
// Static library used in testing and executable
//
cc_library_static {
name: "libdumpsys",
defaults: ["dumpsys_defaults"],
export_include_dirs: ["."],
}
//
// Executable
//
cc_binary {
name: "dumpsys",
defaults: ["dumpsys_defaults"],
srcs: [
"main.cpp",
],
}
subdirs = ["tests"]
/frameworks/native/cmds/dumpsys/main.cpp
接下來會執(zhí)行main.cpp的main函數(shù)
int main(int argc, char* const argv[]) {
signal(SIGPIPE, SIG_IGN);
//獲取ServiceManager
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == nullptr) {
ALOGE("Unable to get default service manager!");
aerr << "dumpsys: Unable to get default service manager!" << endl;
return 20;
}
Dumpsys dumpsys(sm.get());
return dumpsys.main(argc, argv);
}
/frameworks/native/cmds/dumpsys/dumpsys.cpp
最終會執(zhí)行到dumpsys.cpp的main函數(shù)冠蒋,如下:
int Dumpsys::main(int argc, char* const argv[]) {
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 }
};
// 必須重置optind羽圃,否則后續(xù)調(diào)用將失敗(不會發(fā)生在main.cpp上抖剿,但會發(fā)生在測試用例上).
optind = 1;
while (1) {
int c;
int optionIndex = 0;
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; //skip some service
} 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) {
//如果命令里跟了--skip some service朽寞,則加到skippedServices里
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
services = sm_->listServices();
services.sort(sort_func);
args.add(String16("-a"));
}
const size_t N = services.size();
if (N > 1) {
// 開始打印第一行日志
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm_->checkService(services[i]);
if (service != nullptr) {
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;
//獲取相關(guān)service
sp<IBinder> service = sm_->checkService(service_name);
if (service != nullptr) {
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 {
//調(diào)用相關(guān)service的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.reset();
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 '" << service_name << "' DUMP TIMEOUT (" << timeoutArg
<< "s) 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;
}
從代碼的執(zhí)行順序來看斩郎,dumpsys的主要工作順序分為以下4個(gè)步驟:
- main.cpp中defaultServiceManager()函數(shù)用來獲取ServiceManager對象脑融,并傳遞到dumpsys.cpp中。
- sm_->listServices()缩宜,獲取系統(tǒng)中所有向ServiceManager中注冊過的服務(wù)肘迎。
- 如果命令加入了--skip SERVICES.則加入到skippedServices中,過濾service_name锻煌,最后sm_->checkService(service_name)用來獲取指定的service妓布。
- 最后調(diào)用service->dump()。這是最核心的方法宋梧,主要是service去掉用各自的dump()方法來獲取相關(guān)dump信息匣沼。
最后再附上dumpsys的命令 --help信息定義
static void usage() {
fprintf(stderr,
"usage: dumpsys\n"
" To dump all services.\n"
"or:\n"
" dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
" --help: shows this help\n"
" -l: only list services, do not dump them\n"
" -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
" --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
" SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}