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