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