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