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
sort_func(const String16 * lhs,const String16 * rhs)38 static int sort_func(const String16* lhs, const String16* rhs)
39 {
40 return lhs->compare(*rhs);
41 }
42
usage()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
IsSkipped(const Vector<String16> & skipped,const String16 & service)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
main(int argc,char * const argv[])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