286 lines
8.8 KiB
C++
286 lines
8.8 KiB
C++
/*
|
|
* Command that dumps interesting system state to the log.
|
|
*
|
|
*/
|
|
|
|
#define LOG_TAG "dumpsys"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <thread>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/unique_fd.h>
|
|
#include <binder/IServiceManager.h>
|
|
#include <binder/Parcel.h>
|
|
#include <binder/ProcessState.h>
|
|
#include <binder/TextOutput.h>
|
|
#include <utils/Log.h>
|
|
#include <utils/Vector.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
using namespace android;
|
|
using android::base::StringPrintf;
|
|
using android::base::unique_fd;
|
|
using android::base::WriteFully;
|
|
|
|
static int sort_func(const String16* lhs, const String16* rhs)
|
|
{
|
|
return lhs->compare(*rhs);
|
|
}
|
|
|
|
static void usage() {
|
|
fprintf(stderr,
|
|
"usage: dumpsys\n"
|
|
" To dump all services.\n"
|
|
"or:\n"
|
|
" dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
|
|
" --help: shows this help\n"
|
|
" -l: only list services, do not dump them\n"
|
|
" -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n"
|
|
" --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
|
|
" SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
|
|
}
|
|
|
|
bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
|
|
for (const auto& candidate : skipped) {
|
|
if (candidate == service) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int main(int argc, char* const argv[])
|
|
{
|
|
signal(SIGPIPE, SIG_IGN);
|
|
sp<IServiceManager> sm = defaultServiceManager();
|
|
fflush(stdout);
|
|
if (sm == NULL) {
|
|
ALOGE("Unable to get default service manager!");
|
|
aerr << "dumpsys: Unable to get default service manager!" << endl;
|
|
return 20;
|
|
}
|
|
|
|
Vector<String16> services;
|
|
Vector<String16> args;
|
|
Vector<String16> skippedServices;
|
|
bool showListOnly = false;
|
|
bool skipServices = false;
|
|
int timeoutArg = 10;
|
|
static struct option longOptions[] = {
|
|
{"skip", no_argument, 0, 0 },
|
|
{"help", no_argument, 0, 0 },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
while (1) {
|
|
int c;
|
|
int optionIndex = 0;
|
|
|
|
c = getopt_long(argc, argv, "+t:l", longOptions, &optionIndex);
|
|
|
|
if (c == -1) {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 0:
|
|
if (!strcmp(longOptions[optionIndex].name, "skip")) {
|
|
skipServices = true;
|
|
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
|
|
usage();
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case 't':
|
|
{
|
|
char *endptr;
|
|
timeoutArg = strtol(optarg, &endptr, 10);
|
|
if (*endptr != '\0' || timeoutArg <= 0) {
|
|
fprintf(stderr, "Error: invalid timeout number: '%s'\n", optarg);
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'l':
|
|
showListOnly = true;
|
|
break;
|
|
|
|
default:
|
|
fprintf(stderr, "\n");
|
|
usage();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (int i = optind; i < argc; i++) {
|
|
if (skipServices) {
|
|
skippedServices.add(String16(argv[i]));
|
|
} else {
|
|
if (i == optind) {
|
|
services.add(String16(argv[i]));
|
|
} else {
|
|
args.add(String16(argv[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((skipServices && skippedServices.empty()) ||
|
|
(showListOnly && (!services.empty() || !skippedServices.empty()))) {
|
|
usage();
|
|
return -1;
|
|
}
|
|
|
|
if (services.empty() || showListOnly) {
|
|
// gets all services
|
|
services = sm->listServices();
|
|
services.sort(sort_func);
|
|
args.add(String16("-a"));
|
|
}
|
|
|
|
const size_t N = services.size();
|
|
|
|
if (N > 1) {
|
|
// first print a list of the current services
|
|
aout << "Currently running services:" << endl;
|
|
|
|
for (size_t i=0; i<N; i++) {
|
|
sp<IBinder> service = sm->checkService(services[i]);
|
|
if (service != NULL) {
|
|
bool skipped = IsSkipped(skippedServices, services[i]);
|
|
aout << " " << services[i] << (skipped ? " (skipped)" : "") << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (showListOnly) {
|
|
return 0;
|
|
}
|
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
String16 service_name = std::move(services[i]);
|
|
if (IsSkipped(skippedServices, service_name)) continue;
|
|
|
|
sp<IBinder> service = sm->checkService(service_name);
|
|
if (service != NULL) {
|
|
int sfd[2];
|
|
|
|
if (pipe(sfd) != 0) {
|
|
aerr << "Failed to create pipe to dump service info for " << service_name
|
|
<< ": " << strerror(errno) << endl;
|
|
continue;
|
|
}
|
|
|
|
unique_fd local_end(sfd[0]);
|
|
unique_fd remote_end(sfd[1]);
|
|
sfd[0] = sfd[1] = -1;
|
|
|
|
if (N > 1) {
|
|
aout << "------------------------------------------------------------"
|
|
"-------------------" << endl;
|
|
aout << "DUMP OF SERVICE " << service_name << ":" << endl;
|
|
}
|
|
|
|
// dump blocks until completion, so spawn a thread..
|
|
std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
|
|
int err = service->dump(remote_end.get(), args);
|
|
|
|
// It'd be nice to be able to close the remote end of the socketpair before the dump
|
|
// call returns, to terminate our reads if the other end closes their copy of the
|
|
// file descriptor, but then hangs for some reason. There doesn't seem to be a good
|
|
// way to do this, though.
|
|
remote_end.clear();
|
|
|
|
if (err != 0) {
|
|
aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
|
|
<< endl;
|
|
}
|
|
});
|
|
|
|
auto timeout = std::chrono::seconds(timeoutArg);
|
|
auto start = std::chrono::steady_clock::now();
|
|
auto end = start + timeout;
|
|
|
|
struct pollfd pfd = {
|
|
.fd = local_end.get(),
|
|
.events = POLLIN
|
|
};
|
|
|
|
bool timed_out = false;
|
|
bool error = false;
|
|
while (true) {
|
|
// Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
|
|
auto time_left_ms = [end]() {
|
|
auto now = std::chrono::steady_clock::now();
|
|
auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
|
|
return std::max(diff.count(), 0ll);
|
|
};
|
|
|
|
int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
|
|
if (rc < 0) {
|
|
aerr << "Error in poll while dumping service " << service_name << " : "
|
|
<< strerror(errno) << endl;
|
|
error = true;
|
|
break;
|
|
} else if (rc == 0) {
|
|
timed_out = true;
|
|
break;
|
|
}
|
|
|
|
char buf[4096];
|
|
rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
|
|
if (rc < 0) {
|
|
aerr << "Failed to read while dumping service " << service_name << ": "
|
|
<< strerror(errno) << endl;
|
|
error = true;
|
|
break;
|
|
} else if (rc == 0) {
|
|
// EOF.
|
|
break;
|
|
}
|
|
|
|
if (!WriteFully(STDOUT_FILENO, buf, rc)) {
|
|
aerr << "Failed to write while dumping service " << service_name << ": "
|
|
<< strerror(errno) << endl;
|
|
error = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (timed_out) {
|
|
aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
|
|
}
|
|
|
|
if (timed_out || error) {
|
|
dump_thread.detach();
|
|
} else {
|
|
dump_thread.join();
|
|
}
|
|
|
|
if (N > 1) {
|
|
std::chrono::duration<double> elapsed_seconds =
|
|
std::chrono::steady_clock::now() - start;
|
|
aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str()
|
|
<< "was the duration of dumpsys " << service_name << endl;
|
|
}
|
|
} else {
|
|
aerr << "Can't find service: " << service_name << endl;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|