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 <iomanip>
20 #include <thread>
21
22 #include <android-base/file.h>
23 #include <android-base/stringprintf.h>
24 #include <android-base/strings.h>
25 #include <android-base/unique_fd.h>
26 #include <binder/BpBinder.h>
27 #include <binder/Parcel.h>
28 #include <binder/ProcessState.h>
29 #include <binder/Stability.h>
30 #include <binder/TextOutput.h>
31 #include <binderdebug/BinderDebug.h>
32 #include <serviceutils/PriorityDumper.h>
33 #include <utils/Log.h>
34 #include <utils/Vector.h>
35
36 #include <iostream>
37 #include <fcntl.h>
38 #include <getopt.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/poll.h>
43 #include <sys/socket.h>
44 #include <sys/time.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47
48 #include "dumpsys.h"
49
50 using namespace android;
51 using ::android::base::StringAppendF;
52 using ::android::base::StringPrintf;
53 using ::android::base::unique_fd;
54 using ::android::base::WriteFully;
55 using ::android::base::WriteStringToFd;
56
sort_func(const String16 * lhs,const String16 * rhs)57 static int sort_func(const String16* lhs, const String16* rhs)
58 {
59 return lhs->compare(*rhs);
60 }
61
usage()62 static void usage() {
63 fprintf(
64 stderr,
65 "usage: dumpsys\n"
66 " To dump all services.\n"
67 "or:\n"
68 " dumpsys [-t TIMEOUT] [--priority LEVEL] [--clients] [--dump] [--pid] [--thread] "
69 "[--help | "
70 "-l | --skip SERVICES "
71 "| SERVICE [ARGS]]\n"
72 " --help: shows this help\n"
73 " -l: only list services, do not dump them\n"
74 " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
75 " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
76 " --clients: dump client PIDs instead of usual dump\n"
77 " --dump: ask the service to dump itself (this is the default)\n"
78 " --pid: dump PID instead of usual dump\n"
79 " --proto: filter services that support dumping data in proto format. Dumps\n"
80 " will be in proto format.\n"
81 " --priority LEVEL: filter services based on specified priority\n"
82 " LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
83 " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
84 " --stability: dump binder stability information instead of usual dump\n"
85 " --thread: dump thread usage instead of usual dump\n"
86 " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
87 }
88
IsSkipped(const Vector<String16> & skipped,const String16 & service)89 static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
90 for (const auto& candidate : skipped) {
91 if (candidate == service) {
92 return true;
93 }
94 }
95 return false;
96 }
97
ConvertPriorityTypeToBitmask(const String16 & type,int & bitmask)98 static bool ConvertPriorityTypeToBitmask(const String16& type, int& bitmask) {
99 if (type == PriorityDumper::PRIORITY_ARG_CRITICAL) {
100 bitmask = IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL;
101 return true;
102 }
103 if (type == PriorityDumper::PRIORITY_ARG_HIGH) {
104 bitmask = IServiceManager::DUMP_FLAG_PRIORITY_HIGH;
105 return true;
106 }
107 if (type == PriorityDumper::PRIORITY_ARG_NORMAL) {
108 bitmask = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL;
109 return true;
110 }
111 return false;
112 }
113
ConvertBitmaskToPriorityType(int bitmask)114 String16 ConvertBitmaskToPriorityType(int bitmask) {
115 if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
116 return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
117 }
118 if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
119 return String16(PriorityDumper::PRIORITY_ARG_HIGH);
120 }
121 if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
122 return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
123 }
124 return String16("");
125 }
126
main(int argc,char * const argv[])127 int Dumpsys::main(int argc, char* const argv[]) {
128 Vector<String16> services;
129 Vector<String16> args;
130 String16 priorityType;
131 Vector<String16> skippedServices;
132 Vector<String16> protoServices;
133 bool showListOnly = false;
134 bool skipServices = false;
135 bool asProto = false;
136 int dumpTypeFlags = 0;
137 int timeoutArgMs = 10000;
138 int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
139 static struct option longOptions[] = {
140 {"help", no_argument, 0, 0}, {"clients", no_argument, 0, 0},
141 {"dump", no_argument, 0, 0}, {"pid", no_argument, 0, 0},
142 {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0},
143 {"skip", no_argument, 0, 0}, {"stability", no_argument, 0, 0},
144 {"thread", no_argument, 0, 0}, {0, 0, 0, 0}};
145
146 // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
147 // happens on test cases).
148 optind = 1;
149 while (1) {
150 int c;
151 int optionIndex = 0;
152
153 c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex);
154
155 if (c == -1) {
156 break;
157 }
158
159 switch (c) {
160 case 0:
161 if (!strcmp(longOptions[optionIndex].name, "skip")) {
162 skipServices = true;
163 } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
164 asProto = true;
165 } else if (!strcmp(longOptions[optionIndex].name, "help")) {
166 usage();
167 return 0;
168 } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
169 priorityType = String16(String8(optarg));
170 if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
171 fprintf(stderr, "\n");
172 usage();
173 return -1;
174 }
175 } else if (!strcmp(longOptions[optionIndex].name, "dump")) {
176 dumpTypeFlags |= TYPE_DUMP;
177 } else if (!strcmp(longOptions[optionIndex].name, "pid")) {
178 dumpTypeFlags |= TYPE_PID;
179 } else if (!strcmp(longOptions[optionIndex].name, "stability")) {
180 dumpTypeFlags |= TYPE_STABILITY;
181 } else if (!strcmp(longOptions[optionIndex].name, "thread")) {
182 dumpTypeFlags |= TYPE_THREAD;
183 } else if (!strcmp(longOptions[optionIndex].name, "clients")) {
184 dumpTypeFlags |= TYPE_CLIENTS;
185 }
186 break;
187
188 case 't':
189 {
190 char* endptr;
191 timeoutArgMs = strtol(optarg, &endptr, 10);
192 timeoutArgMs = timeoutArgMs * 1000;
193 if (*endptr != '\0' || timeoutArgMs <= 0) {
194 fprintf(stderr, "Error: invalid timeout(seconds) number: '%s'\n", optarg);
195 return -1;
196 }
197 }
198 break;
199
200 case 'T':
201 {
202 char* endptr;
203 timeoutArgMs = strtol(optarg, &endptr, 10);
204 if (*endptr != '\0' || timeoutArgMs <= 0) {
205 fprintf(stderr, "Error: invalid timeout(milliseconds) number: '%s'\n", optarg);
206 return -1;
207 }
208 }
209 break;
210
211 case 'l':
212 showListOnly = true;
213 break;
214
215 default:
216 fprintf(stderr, "\n");
217 usage();
218 return -1;
219 }
220 }
221
222 if (dumpTypeFlags == 0) {
223 dumpTypeFlags = TYPE_DUMP;
224 }
225
226 for (int i = optind; i < argc; i++) {
227 if (skipServices) {
228 skippedServices.add(String16(argv[i]));
229 } else {
230 if (i == optind) {
231 services.add(String16(argv[i]));
232 } else {
233 const String16 arg(argv[i]);
234 args.add(arg);
235 // For backward compatible, if the proto argument is passed to the service, the
236 // dump request is also considered to use proto.
237 if (!asProto && !arg.compare(String16(PriorityDumper::PROTO_ARG))) {
238 asProto = true;
239 }
240 }
241 }
242 }
243
244 if ((skipServices && skippedServices.empty()) ||
245 (showListOnly && (!services.empty() || !skippedServices.empty()))) {
246 usage();
247 return -1;
248 }
249
250 if (services.empty() || showListOnly) {
251 services = listServices(priorityFlags, asProto);
252 setServiceArgs(args, asProto, priorityFlags);
253 }
254
255 const size_t N = services.size();
256 if (N > 1 || showListOnly) {
257 // first print a list of the current services
258 std::cout << "Currently running services:" << std::endl;
259
260 for (size_t i=0; i<N; i++) {
261 sp<IBinder> service = sm_->checkService(services[i]);
262
263 if (service != nullptr) {
264 bool skipped = IsSkipped(skippedServices, services[i]);
265 std::cout << " " << services[i] << (skipped ? " (skipped)" : "") << std::endl;
266 }
267 }
268 }
269
270 if (showListOnly) {
271 return 0;
272 }
273
274 for (size_t i = 0; i < N; i++) {
275 const String16& serviceName = services[i];
276 if (IsSkipped(skippedServices, serviceName)) continue;
277
278 if (startDumpThread(dumpTypeFlags, serviceName, args) == OK) {
279 bool addSeparator = (N > 1);
280 if (addSeparator) {
281 writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
282 }
283 std::chrono::duration<double> elapsedDuration;
284 size_t bytesWritten = 0;
285 status_t status =
286 writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
287 asProto, elapsedDuration, bytesWritten);
288
289 if (status == TIMED_OUT) {
290 std::cout << std::endl
291 << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
292 << "ms) EXPIRED ***" << std::endl
293 << std::endl;
294 }
295
296 if (addSeparator) {
297 writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
298 }
299 bool dumpComplete = (status == OK);
300 stopDumpThread(dumpComplete);
301 }
302 }
303
304 return 0;
305 }
306
listServices(int priorityFilterFlags,bool filterByProto) const307 Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
308 Vector<String16> services = sm_->listServices(priorityFilterFlags);
309 services.sort(sort_func);
310 if (filterByProto) {
311 Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
312 protoServices.sort(sort_func);
313 Vector<String16> intersection;
314 std::set_intersection(services.begin(), services.end(), protoServices.begin(),
315 protoServices.end(), std::back_inserter(intersection));
316 services = std::move(intersection);
317 }
318 return services;
319 }
320
setServiceArgs(Vector<String16> & args,bool asProto,int priorityFlags)321 void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) {
322 // Add proto flag if dumping service as proto.
323 if (asProto) {
324 args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
325 }
326
327 // Add -a (dump all) flag if dumping all services, dumping normal services or
328 // services not explicitly registered to a priority bucket (default services).
329 if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
330 (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) ||
331 (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT)) {
332 args.insertAt(String16("-a"), 0);
333 }
334
335 // Add priority flags when dumping services registered to a specific priority bucket.
336 if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) ||
337 (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) ||
338 (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
339 String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
340 args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
341 args.insertAt(priorityType, 1);
342 }
343 }
344
dumpPidToFd(const sp<IBinder> & service,const unique_fd & fd,bool exclusive)345 static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd, bool exclusive) {
346 pid_t pid;
347 status_t status = service->getDebugPid(&pid);
348 if (status != OK) {
349 return status;
350 }
351 if (!exclusive) {
352 WriteStringToFd("Service host process PID: ", fd.get());
353 }
354 WriteStringToFd(std::to_string(pid) + "\n", fd.get());
355 return OK;
356 }
357
dumpStabilityToFd(const sp<IBinder> & service,const unique_fd & fd)358 static status_t dumpStabilityToFd(const sp<IBinder>& service, const unique_fd& fd) {
359 WriteStringToFd("Stability: " + internal::Stability::debugToString(service) + "\n", fd);
360 return OK;
361 }
362
dumpThreadsToFd(const sp<IBinder> & service,const unique_fd & fd)363 static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) {
364 pid_t pid;
365 status_t status = service->getDebugPid(&pid);
366 if (status != OK) {
367 return status;
368 }
369 BinderPidInfo pidInfo;
370 status = getBinderPidInfo(BinderDebugContext::BINDER, pid, &pidInfo);
371 if (status != OK) {
372 return status;
373 }
374 WriteStringToFd("Threads in use: " + std::to_string(pidInfo.threadUsage) + "/" +
375 std::to_string(pidInfo.threadCount) + "\n",
376 fd.get());
377 return OK;
378 }
379
dumpClientsToFd(const sp<IBinder> & service,const unique_fd & fd)380 static status_t dumpClientsToFd(const sp<IBinder>& service, const unique_fd& fd) {
381 std::string clientPids;
382 const auto remoteBinder = service->remoteBinder();
383 if (remoteBinder == nullptr) {
384 WriteStringToFd("Client PIDs are not available for local binders.\n", fd.get());
385 return OK;
386 }
387 const auto handle = remoteBinder->getDebugBinderHandle();
388 if (handle == std::nullopt) {
389 return OK;
390 }
391 std::vector<pid_t> pids;
392 pid_t myPid = getpid();
393 pid_t servicePid;
394 status_t status = service->getDebugPid(&servicePid);
395 if (status != OK) {
396 return status;
397 }
398 status =
399 getBinderClientPids(BinderDebugContext::BINDER, myPid, servicePid, handle.value(), &pids);
400 if (status != OK) {
401 return status;
402 }
403 pids.erase(std::remove_if(pids.begin(), pids.end(), [&](pid_t pid) { return pid == myPid; }),
404 pids.end());
405 WriteStringToFd("Client PIDs: " + ::android::base::Join(pids, ", ") + "\n", fd.get());
406 return OK;
407 }
408
reportDumpError(const String16 & serviceName,status_t error,const char * context)409 static void reportDumpError(const String16& serviceName, status_t error, const char* context) {
410 if (error == OK) return;
411
412 std::cerr << "Error with service '" << serviceName << "' while " << context << ": "
413 << statusToString(error) << std::endl;
414 }
415
startDumpThread(int dumpTypeFlags,const String16 & serviceName,const Vector<String16> & args)416 status_t Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName,
417 const Vector<String16>& args) {
418 sp<IBinder> service = sm_->checkService(serviceName);
419 if (service == nullptr) {
420 std::cerr << "Can't find service: " << serviceName << std::endl;
421 return NAME_NOT_FOUND;
422 }
423
424 int sfd[2];
425 if (pipe(sfd) != 0) {
426 std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": "
427 << strerror(errno) << std::endl;
428 return -errno;
429 }
430
431 redirectFd_ = unique_fd(sfd[0]);
432 unique_fd remote_end(sfd[1]);
433 sfd[0] = sfd[1] = -1;
434
435 // dump blocks until completion, so spawn a thread..
436 activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
437 if (dumpTypeFlags & TYPE_PID) {
438 status_t err = dumpPidToFd(service, remote_end, dumpTypeFlags == TYPE_PID);
439 reportDumpError(serviceName, err, "dumping PID");
440 }
441 if (dumpTypeFlags & TYPE_STABILITY) {
442 status_t err = dumpStabilityToFd(service, remote_end);
443 reportDumpError(serviceName, err, "dumping stability");
444 }
445 if (dumpTypeFlags & TYPE_THREAD) {
446 status_t err = dumpThreadsToFd(service, remote_end);
447 reportDumpError(serviceName, err, "dumping thread info");
448 }
449 if (dumpTypeFlags & TYPE_CLIENTS) {
450 status_t err = dumpClientsToFd(service, remote_end);
451 reportDumpError(serviceName, err, "dumping clients info");
452 }
453
454 // other types always act as a header, this is usually longer
455 if (dumpTypeFlags & TYPE_DUMP) {
456 status_t err = service->dump(remote_end.get(), args);
457 reportDumpError(serviceName, err, "dumping");
458 }
459 });
460 return OK;
461 }
462
stopDumpThread(bool dumpComplete)463 void Dumpsys::stopDumpThread(bool dumpComplete) {
464 if (dumpComplete) {
465 activeThread_.join();
466 } else {
467 activeThread_.detach();
468 }
469 /* close read end of the dump output redirection pipe */
470 redirectFd_.reset();
471 }
472
writeDumpHeader(int fd,const String16 & serviceName,int priorityFlags) const473 void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
474 std::string msg(
475 "----------------------------------------"
476 "---------------------------------------\n");
477 if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
478 priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL ||
479 priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
480 StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
481 } else {
482 String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
483 StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
484 String8(serviceName).c_str());
485 }
486 WriteStringToFd(msg, fd);
487 }
488
writeDump(int fd,const String16 & serviceName,std::chrono::milliseconds timeout,bool asProto,std::chrono::duration<double> & elapsedDuration,size_t & bytesWritten) const489 status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
490 bool asProto, std::chrono::duration<double>& elapsedDuration,
491 size_t& bytesWritten) const {
492 status_t status = OK;
493 size_t totalBytes = 0;
494 auto start = std::chrono::steady_clock::now();
495 auto end = start + timeout;
496
497 int serviceDumpFd = redirectFd_.get();
498 if (serviceDumpFd == -1) {
499 return INVALID_OPERATION;
500 }
501
502 struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
503
504 while (true) {
505 // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
506 auto time_left_ms = [end]() {
507 auto now = std::chrono::steady_clock::now();
508 auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
509 return std::max(diff.count(), 0LL);
510 };
511
512 int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
513 if (rc < 0) {
514 std::cerr << "Error in poll while dumping service " << serviceName << " : "
515 << strerror(errno) << std::endl;
516 status = -errno;
517 break;
518 } else if (rc == 0 || time_left_ms() == 0) {
519 status = TIMED_OUT;
520 break;
521 }
522
523 char buf[4096];
524 rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
525 if (rc < 0) {
526 std::cerr << "Failed to read while dumping service " << serviceName << ": "
527 << strerror(errno) << std::endl;
528 status = -errno;
529 break;
530 } else if (rc == 0) {
531 // EOF.
532 break;
533 }
534
535 if (!WriteFully(fd, buf, rc)) {
536 std::cerr << "Failed to write while dumping service " << serviceName << ": "
537 << strerror(errno) << std::endl;
538 status = -errno;
539 break;
540 }
541 totalBytes += rc;
542 }
543
544 if ((status == TIMED_OUT) && (!asProto)) {
545 std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
546 String8(serviceName).string(), timeout.count());
547 WriteStringToFd(msg, fd);
548 }
549
550 elapsedDuration = std::chrono::steady_clock::now() - start;
551 bytesWritten = totalBytes;
552 return status;
553 }
554
writeDumpFooter(int fd,const String16 & serviceName,const std::chrono::duration<double> & elapsedDuration) const555 void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
556 const std::chrono::duration<double>& elapsedDuration) const {
557 using std::chrono::system_clock;
558 const auto finish = system_clock::to_time_t(system_clock::now());
559 std::tm finish_tm;
560 localtime_r(&finish, &finish_tm);
561 std::stringstream oss;
562 oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
563 std::string msg =
564 StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
565 elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
566 WriteStringToFd(msg, fd);
567 }
568