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