/* * Command that dumps interesting system state to the log. * */ #define LOG_TAG "dumpsys" #include <algorithm> #include <chrono> #include <thread> #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/unique_fd.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/TextOutput.h> #include <utils/Log.h> #include <utils/Vector.h> #include <fcntl.h> #include <getopt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/poll.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> using namespace android; using android::base::StringPrintf; using android::base::unique_fd; using android::base::WriteFully; static int sort_func(const String16* lhs, const String16* rhs) { return lhs->compare(*rhs); } 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"); } bool IsSkipped(const Vector<String16>& skipped, const String16& service) { for (const auto& candidate : skipped) { if (candidate == service) { return true; } } return false; } int main(int argc, char* const argv[]) { signal(SIGPIPE, SIG_IGN); sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); if (sm == NULL) { ALOGE("Unable to get default service manager!"); 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; 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 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 { 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; }