Refactor dumpsys to expose helper apis
- allow dumpstate to interact with services without executing dumpsys binary
- Remove "NORMAL" priority from section name for backwards compatibility when switching to version 2.0
Bug: 67716082
Test: mmm -j56 frameworks/native/cmds/dumpsys && \
adb sync data && \
adb shell /data/nativetest/dumpsys_test/dumpsys_test && \
adb shell /data/nativetest64/dumpsys_test/dumpsys_test && \
printf "\n\n#### ALL TESTS PASSED ####\n"
Test: manual tests with "adb bugreport"
Change-Id: I4198333a58ffe6cb521b5cb7066520c7a3ef0675
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index 0862a40..ae0cc01 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -43,9 +43,11 @@
#include "dumpsys.h"
using namespace android;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteFully;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+using ::android::base::WriteFully;
+using ::android::base::WriteStringToFd;
static int sort_func(const String16* lhs, const String16* rhs)
{
@@ -96,6 +98,19 @@
return false;
}
+String16 ConvertBitmaskToPriorityType(int bitmask) {
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
+ return String16(PriorityDumper::PRIORITY_ARG_CRITICAL);
+ }
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) {
+ return String16(PriorityDumper::PRIORITY_ARG_HIGH);
+ }
+ if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+ return String16(PriorityDumper::PRIORITY_ARG_NORMAL);
+ }
+ return String16("");
+}
+
int Dumpsys::main(int argc, char* const argv[]) {
Vector<String16> services;
Vector<String16> args;
@@ -104,9 +119,9 @@
Vector<String16> protoServices;
bool showListOnly = false;
bool skipServices = false;
- bool filterByProto = false;
+ bool asProto = false;
int timeoutArgMs = 10000;
- int dumpPriorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
+ int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
static struct option longOptions[] = {{"priority", required_argument, 0, 0},
{"proto", no_argument, 0, 0},
{"skip", no_argument, 0, 0},
@@ -131,13 +146,13 @@
if (!strcmp(longOptions[optionIndex].name, "skip")) {
skipServices = true;
} else if (!strcmp(longOptions[optionIndex].name, "proto")) {
- filterByProto = true;
+ asProto = true;
} else if (!strcmp(longOptions[optionIndex].name, "help")) {
usage();
return 0;
} else if (!strcmp(longOptions[optionIndex].name, "priority")) {
priorityType = String16(String8(optarg));
- if (!ConvertPriorityTypeToBitmask(priorityType, dumpPriorityFlags)) {
+ if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
fprintf(stderr, "\n");
usage();
return -1;
@@ -198,28 +213,11 @@
}
if (services.empty() || showListOnly) {
- // gets all services
- services = sm_->listServices(dumpPriorityFlags);
- services.sort(sort_func);
- if (filterByProto) {
- protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
- protoServices.sort(sort_func);
- Vector<String16> intersection;
- std::set_intersection(services.begin(), services.end(), protoServices.begin(),
- protoServices.end(), std::back_inserter(intersection));
- services = std::move(intersection);
- args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
- }
- if (dumpPriorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
- args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
- args.insertAt(priorityType, 1);
- } else {
- args.add(String16("-a"));
- }
+ services = listServices(priorityFlags, asProto);
+ setServiceArgs(args, asProto, priorityFlags);
}
const size_t N = services.size();
-
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
@@ -239,129 +237,204 @@
}
for (size_t i = 0; i < N; i++) {
- const String16& service_name = std::move(services[i]);
- if (IsSkipped(skippedServices, service_name)) continue;
+ const String16& serviceName = services[i];
+ if (IsSkipped(skippedServices, serviceName)) continue;
- sp<IBinder> service = sm_->checkService(service_name);
- if (service != nullptr) {
- int sfd[2];
-
- if (pipe(sfd) != 0) {
- aerr << "Failed to create pipe to dump service info for " << service_name
- << ": " << strerror(errno) << endl;
- continue;
+ if (startDumpThread(serviceName, args) == OK) {
+ bool addSeparator = (N > 1);
+ if (addSeparator) {
+ writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
}
+ std::chrono::duration<double> elapsedDuration;
+ size_t bytesWritten = 0;
+ status_t status =
+ writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
+ asProto, elapsedDuration, bytesWritten);
- unique_fd local_end(sfd[0]);
- unique_fd remote_end(sfd[1]);
- sfd[0] = sfd[1] = -1;
-
- if (N > 1) {
- aout << "------------------------------------------------------------"
- "-------------------" << endl;
- if (dumpPriorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
- aout << "DUMP OF SERVICE " << service_name << ":" << endl;
- } else {
- aout << "DUMP OF SERVICE " << priorityType << " " << 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.reset();
-
- if (err != 0) {
- aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
- << endl;
- }
- });
-
- auto timeout = std::chrono::milliseconds(timeoutArgMs);
- 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) {
+ if (status == TIMED_OUT) {
aout << endl
- << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArgMs
+ << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
<< "ms) EXPIRED ***" << endl
<< endl;
}
- if (timed_out || error) {
- dump_thread.detach();
- } else {
- dump_thread.join();
+ if (addSeparator) {
+ writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
}
-
- 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;
-
- using std::chrono::system_clock;
- const auto finish = system_clock::to_time_t(system_clock::now());
- std::tm finish_tm;
- localtime_r(&finish, &finish_tm);
- aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S")
- << endl;
- }
- } else {
- aerr << "Can't find service: " << service_name << endl;
+ bool dumpComplete = (status == OK);
+ stopDumpThread(dumpComplete);
}
}
return 0;
}
+
+Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const {
+ Vector<String16> services = sm_->listServices(priorityFilterFlags);
+ services.sort(sort_func);
+ if (filterByProto) {
+ Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO);
+ protoServices.sort(sort_func);
+ Vector<String16> intersection;
+ std::set_intersection(services.begin(), services.end(), protoServices.begin(),
+ protoServices.end(), std::back_inserter(intersection));
+ services = std::move(intersection);
+ }
+ return services;
+}
+
+void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const {
+ if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) ||
+ (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) {
+ args.add(String16("-a"));
+ }
+ if (asProto) {
+ args.insertAt(String16(PriorityDumper::PROTO_ARG), 0);
+ }
+ if (priorityFlags != IServiceManager::DUMP_FLAG_PRIORITY_ALL) {
+ String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+ args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0);
+ args.insertAt(priorityType, 1);
+ }
+}
+
+status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) {
+ sp<IBinder> service = sm_->checkService(serviceName);
+ if (service == nullptr) {
+ aerr << "Can't find service: " << serviceName << endl;
+ return NAME_NOT_FOUND;
+ }
+
+ int sfd[2];
+ if (pipe(sfd) != 0) {
+ aerr << "Failed to create pipe to dump service info for " << serviceName << ": "
+ << strerror(errno) << endl;
+ return -errno;
+ }
+
+ redirectFd_ = unique_fd(sfd[0]);
+ unique_fd remote_end(sfd[1]);
+ sfd[0] = sfd[1] = -1;
+
+ // dump blocks until completion, so spawn a thread..
+ activeThread_ = std::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.reset();
+
+ if (err != 0) {
+ aerr << "Error dumping service info: (" << strerror(err) << ") "
+ << serviceName << endl;
+ }
+ });
+ return OK;
+}
+
+void Dumpsys::stopDumpThread(bool dumpComplete) {
+ if (dumpComplete) {
+ activeThread_.join();
+ } else {
+ activeThread_.detach();
+ }
+ /* close read end of the dump output redirection pipe */
+ redirectFd_.reset();
+}
+
+void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const {
+ std::string msg(
+ "----------------------------------------"
+ "---------------------------------------\n");
+ if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL ||
+ priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) {
+ StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str());
+ } else {
+ String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags);
+ StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(),
+ String8(serviceName).c_str());
+ }
+ WriteStringToFd(msg, fd);
+}
+
+status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout,
+ bool asProto, std::chrono::duration<double>& elapsedDuration,
+ size_t& bytesWritten) const {
+ status_t status = OK;
+ size_t totalBytes = 0;
+ auto start = std::chrono::steady_clock::now();
+ auto end = start + timeout;
+
+ int serviceDumpFd = redirectFd_.get();
+ if (serviceDumpFd == -1) {
+ return INVALID_OPERATION;
+ }
+
+ struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN};
+
+ 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 " << serviceName << " : "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ } else if (rc == 0) {
+ status = TIMED_OUT;
+ break;
+ }
+
+ char buf[4096];
+ rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf)));
+ if (rc < 0) {
+ aerr << "Failed to read while dumping service " << serviceName << ": "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ } else if (rc == 0) {
+ // EOF.
+ break;
+ }
+
+ if (!WriteFully(fd, buf, rc)) {
+ aerr << "Failed to write while dumping service " << serviceName << ": "
+ << strerror(errno) << endl;
+ status = -errno;
+ break;
+ }
+ totalBytes += rc;
+ }
+
+ if ((status == TIMED_OUT) && (!asProto)) {
+ std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n",
+ String8(serviceName).string(), timeout.count());
+ WriteStringToFd(msg, fd);
+ }
+
+ elapsedDuration = std::chrono::steady_clock::now() - start;
+ bytesWritten = totalBytes;
+ return status;
+}
+
+void Dumpsys::writeDumpFooter(int fd, const String16& serviceName,
+ const std::chrono::duration<double>& elapsedDuration) const {
+ using std::chrono::system_clock;
+ const auto finish = system_clock::to_time_t(system_clock::now());
+ std::tm finish_tm;
+ localtime_r(&finish, &finish_tm);
+ std::stringstream oss;
+ oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S");
+ std::string msg =
+ StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n",
+ elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str());
+ WriteStringToFd(msg, fd);
+}