Home | History | Annotate | Download | only in dumpsys
      1 /*
      2  * Command that dumps interesting system state to the log.
      3  *
      4  */
      5 
      6 #define LOG_TAG "dumpsys"
      7 
      8 #include <algorithm>
      9 #include <chrono>
     10 #include <thread>
     11 
     12 #include <android-base/file.h>
     13 #include <android-base/stringprintf.h>
     14 #include <android-base/unique_fd.h>
     15 #include <binder/IServiceManager.h>
     16 #include <binder/Parcel.h>
     17 #include <binder/ProcessState.h>
     18 #include <binder/TextOutput.h>
     19 #include <utils/Log.h>
     20 #include <utils/Vector.h>
     21 
     22 #include <fcntl.h>
     23 #include <getopt.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <sys/poll.h>
     28 #include <sys/socket.h>
     29 #include <sys/time.h>
     30 #include <sys/types.h>
     31 #include <unistd.h>
     32 
     33 using namespace android;
     34 using android::base::StringPrintf;
     35 using android::base::unique_fd;
     36 using android::base::WriteFully;
     37 
     38 static int sort_func(const String16* lhs, const String16* rhs)
     39 {
     40     return lhs->compare(*rhs);
     41 }
     42 
     43 static void usage() {
     44     fprintf(stderr,
     45         "usage: dumpsys\n"
     46             "         To dump all services.\n"
     47             "or:\n"
     48             "       dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
     49             "         --help: shows this help\n"
     50             "         -l: only list services, do not dump them\n"
     51             "         -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
     52             "         --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
     53             "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
     54 }
     55 
     56 bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
     57     for (const auto& candidate : skipped) {
     58         if (candidate == service) {
     59             return true;
     60         }
     61     }
     62     return false;
     63 }
     64 
     65 int main(int argc, char* const argv[])
     66 {
     67     signal(SIGPIPE, SIG_IGN);
     68     sp<IServiceManager> sm = defaultServiceManager();
     69     fflush(stdout);
     70     if (sm == NULL) {
     71         ALOGE("Unable to get default service manager!");
     72         aerr << "dumpsys: Unable to get default service manager!" << endl;
     73         return 20;
     74     }
     75 
     76     Vector<String16> services;
     77     Vector<String16> args;
     78     Vector<String16> skippedServices;
     79     bool showListOnly = false;
     80     bool skipServices = false;
     81     int timeoutArg = 10;
     82     static struct option longOptions[] = {
     83         {"skip", no_argument, 0,  0 },
     84         {"help", no_argument, 0,  0 },
     85         {     0,           0, 0,  0 }
     86     };
     87 
     88     while (1) {
     89         int c;
     90         int optionIndex = 0;
     91 
     92         c = getopt_long(argc, argv, "+t:l", longOptions, &optionIndex);
     93 
     94         if (c == -1) {
     95             break;
     96         }
     97 
     98         switch (c) {
     99         case 0:
    100             if (!strcmp(longOptions[optionIndex].name, "skip")) {
    101                 skipServices = true;
    102             } else if (!strcmp(longOptions[optionIndex].name, "help")) {
    103                 usage();
    104                 return 0;
    105             }
    106             break;
    107 
    108         case 't':
    109             {
    110                 char *endptr;
    111                 timeoutArg = strtol(optarg, &endptr, 10);
    112                 if (*endptr != '\0' || timeoutArg <= 0) {
    113                     fprintf(stderr, "Error: invalid timeout number: '%s'\n", optarg);
    114                     return -1;
    115                 }
    116             }
    117             break;
    118 
    119         case 'l':
    120             showListOnly = true;
    121             break;
    122 
    123         default:
    124             fprintf(stderr, "\n");
    125             usage();
    126             return -1;
    127         }
    128     }
    129 
    130     for (int i = optind; i < argc; i++) {
    131         if (skipServices) {
    132             skippedServices.add(String16(argv[i]));
    133         } else {
    134             if (i == optind) {
    135                 services.add(String16(argv[i]));
    136             } else {
    137                 args.add(String16(argv[i]));
    138             }
    139         }
    140     }
    141 
    142     if ((skipServices && skippedServices.empty()) ||
    143             (showListOnly && (!services.empty() || !skippedServices.empty()))) {
    144         usage();
    145         return -1;
    146     }
    147 
    148     if (services.empty() || showListOnly) {
    149         // gets all services
    150         services = sm->listServices();
    151         services.sort(sort_func);
    152         args.add(String16("-a"));
    153     }
    154 
    155     const size_t N = services.size();
    156 
    157     if (N > 1) {
    158         // first print a list of the current services
    159         aout << "Currently running services:" << endl;
    160 
    161         for (size_t i=0; i<N; i++) {
    162             sp<IBinder> service = sm->checkService(services[i]);
    163             if (service != NULL) {
    164                 bool skipped = IsSkipped(skippedServices, services[i]);
    165                 aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
    166             }
    167         }
    168     }
    169 
    170     if (showListOnly) {
    171         return 0;
    172     }
    173 
    174     for (size_t i = 0; i < N; i++) {
    175         String16 service_name = std::move(services[i]);
    176         if (IsSkipped(skippedServices, service_name)) continue;
    177 
    178         sp<IBinder> service = sm->checkService(service_name);
    179         if (service != NULL) {
    180             int sfd[2];
    181 
    182             if (pipe(sfd) != 0) {
    183                 aerr << "Failed to create pipe to dump service info for " << service_name
    184                      << ": " << strerror(errno) << endl;
    185                 continue;
    186             }
    187 
    188             unique_fd local_end(sfd[0]);
    189             unique_fd remote_end(sfd[1]);
    190             sfd[0] = sfd[1] = -1;
    191 
    192             if (N > 1) {
    193                 aout << "------------------------------------------------------------"
    194                         "-------------------" << endl;
    195                 aout << "DUMP OF SERVICE " << service_name << ":" << endl;
    196             }
    197 
    198             // dump blocks until completion, so spawn a thread..
    199             std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
    200                 int err = service->dump(remote_end.get(), args);
    201 
    202                 // It'd be nice to be able to close the remote end of the socketpair before the dump
    203                 // call returns, to terminate our reads if the other end closes their copy of the
    204                 // file descriptor, but then hangs for some reason. There doesn't seem to be a good
    205                 // way to do this, though.
    206                 remote_end.clear();
    207 
    208                 if (err != 0) {
    209                     aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
    210                          << endl;
    211                 }
    212             });
    213 
    214             auto timeout = std::chrono::seconds(timeoutArg);
    215             auto start = std::chrono::steady_clock::now();
    216             auto end = start + timeout;
    217 
    218             struct pollfd pfd = {
    219                 .fd = local_end.get(),
    220                 .events = POLLIN
    221             };
    222 
    223             bool timed_out = false;
    224             bool error = false;
    225             while (true) {
    226                 // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
    227                 auto time_left_ms = [end]() {
    228                     auto now = std::chrono::steady_clock::now();
    229                     auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
    230                     return std::max(diff.count(), 0ll);
    231                 };
    232 
    233                 int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
    234                 if (rc < 0) {
    235                     aerr << "Error in poll while dumping service " << service_name << " : "
    236                          << strerror(errno) << endl;
    237                     error = true;
    238                     break;
    239                 } else if (rc == 0) {
    240                     timed_out = true;
    241                     break;
    242                 }
    243 
    244                 char buf[4096];
    245                 rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
    246                 if (rc < 0) {
    247                     aerr << "Failed to read while dumping service " << service_name << ": "
    248                          << strerror(errno) << endl;
    249                     error = true;
    250                     break;
    251                 } else if (rc == 0) {
    252                     // EOF.
    253                     break;
    254                 }
    255 
    256                 if (!WriteFully(STDOUT_FILENO, buf, rc)) {
    257                     aerr << "Failed to write while dumping service " << service_name << ": "
    258                          << strerror(errno) << endl;
    259                     error = true;
    260                     break;
    261                 }
    262             }
    263 
    264             if (timed_out) {
    265                 aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
    266             }
    267 
    268             if (timed_out || error) {
    269                 dump_thread.detach();
    270             } else {
    271                 dump_thread.join();
    272             }
    273 
    274             if (N > 1) {
    275               std::chrono::duration<double> elapsed_seconds =
    276                   std::chrono::steady_clock::now() - start;
    277               aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
    278                    << "was the duration of dumpsys " << service_name << endl;
    279             }
    280         } else {
    281             aerr << "Can't find service: " << service_name << endl;
    282         }
    283     }
    284 
    285     return 0;
    286 }
    287