Android 调试系列-Dumpsys源码篇

Dumpsys这个命令了解或者用过的人肯定都知道,他可以提供很多有用的信息,供开发者来定位问题。是一个很强大的debug工具。接下来,会从源码的角度来介绍一下这个命令的实现方式。
注:已Android 8.0源码来分析
在系统编译时会编译到如下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
接下来会执行main.cpp的main函数

int main(int argc, char* const argv[]) {
    signal(SIGPIPE, SIG_IGN);
    //获取ServiceManager
    sp 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
最终会执行到dumpsys.cpp的main函数,如下:


int Dumpsys::main(int argc, char* const argv[]) {
    Vector services;
    Vector args;
    Vector 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,否则后续调用将失败(不会发生在main.cpp上,但会发生在测试用例上).
    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 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;
        //获取相关service
        sp 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 {
                //调用相关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(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 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;
}

从代码的执行顺序来看,dumpsys的主要工作顺序分为以下4个步骤:

  1. main.cpp中defaultServiceManager()函数用来获取ServiceManager对象,并传递到dumpsys.cpp中。
  2. sm_->listServices(),获取系统中所有向ServiceManager中注册过的服务。
  3. 如果命令加入了--skip SERVICES.则加入到skippedServices中,过滤service_name,最后sm_->checkService(service_name)用来获取指定的service。
  4. 最后调用service->dump()。这是最核心的方法,主要是service去掉用各自的dump()方法来获取相关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");
}

你可能感兴趣的:(Android 调试系列-Dumpsys源码篇)