美文网首页
Android 调试系列-Dumpsys源码篇

Android 调试系列-Dumpsys源码篇

作者: 凡星轨迹 | 来源:发表于2018-11-22 17:25 被阅读0次

    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<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
    最终会执行到dumpsys.cpp的main函数,如下:

    
    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,否则后续调用将失败(不会发生在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<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;
            //获取相关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 {
                    //调用相关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;
    }
    

    从代码的执行顺序来看,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源码篇

          本文链接:https://www.haomeiwen.com/subject/ucplqqtx.html