• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
43 using namespace android;
44 using android::base::StringPrintf;
45 using android::base::unique_fd;
46 using android::base::WriteFully;
47 
sort_func(const String16 * lhs,const String16 * rhs)48 static int sort_func(const String16* lhs, const String16* rhs)
49 {
50     return lhs->compare(*rhs);
51 }
52 
usage()53 static 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 
IsSkipped(const Vector<String16> & skipped,const String16 & service)66 static 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 
main(int argc,char * const argv[])75 int 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