• 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 <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