dumpsys: display the client PIDs of all of the services
When gathering info on a service, get the BpBinder handle to look up the
node number of the binder service in binder_logs/proc/<pid>.
Look up the node number in binder_logs/state from the binder service.
That entry in the state file has the client PIDs.
Example output:
$ adb shell dumpsys --clients android.hardware.power.IPower/default
Client PIDs: 437, 708, 203
Test: adb root && adb shell dumpsys --clients
Test: adb shell dumpsys --clients power
Test: adb shell dumpsys --clients --pid
Change-Id: I0daebc18cb9f47925ccec5a3eb0b09c4b1e43fe4
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index f8fdaa0..3d2bdf1 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -21,7 +21,9 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <binder/BpBinder.h>
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
@@ -58,27 +60,30 @@
}
static void usage() {
- fprintf(stderr,
- "usage: dumpsys\n"
- " To dump all services.\n"
- "or:\n"
- " dumpsys [-t TIMEOUT] [--priority LEVEL] [--dump] [--pid] [--thread] [--help | "
- "-l | --skip SERVICES "
- "| SERVICE [ARGS]]\n"
- " --help: shows this help\n"
- " -l: only list services, do not dump them\n"
- " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
- " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
- " --dump: ask the service to dump itself (this is the default)\n"
- " --pid: dump PID instead of usual dump\n"
- " --proto: filter services that support dumping data in proto format. Dumps\n"
- " will be in proto format.\n"
- " --priority LEVEL: filter services based on specified priority\n"
- " LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
- " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
- " --stability: dump binder stability information instead of usual dump\n"
- " --thread: dump thread usage instead of usual dump\n"
- " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
+ fprintf(
+ stderr,
+ "usage: dumpsys\n"
+ " To dump all services.\n"
+ "or:\n"
+ " dumpsys [-t TIMEOUT] [--priority LEVEL] [--clients] [--dump] [--pid] [--thread] "
+ "[--help | "
+ "-l | --skip SERVICES "
+ "| SERVICE [ARGS]]\n"
+ " --help: shows this help\n"
+ " -l: only list services, do not dump them\n"
+ " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
+ " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
+ " --clients: dump client PIDs instead of usual dump\n"
+ " --dump: ask the service to dump itself (this is the default)\n"
+ " --pid: dump PID instead of usual dump\n"
+ " --proto: filter services that support dumping data in proto format. Dumps\n"
+ " will be in proto format.\n"
+ " --priority LEVEL: filter services based on specified priority\n"
+ " LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
+ " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
+ " --stability: dump binder stability information instead of usual dump\n"
+ " --thread: dump thread usage instead of usual dump\n"
+ " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}
static bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
@@ -131,15 +136,12 @@
int dumpTypeFlags = 0;
int timeoutArgMs = 10000;
int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
- static struct option longOptions[] = {{"help", no_argument, 0, 0},
- {"dump", no_argument, 0, 0},
- {"pid", no_argument, 0, 0},
- {"priority", required_argument, 0, 0},
- {"proto", no_argument, 0, 0},
- {"skip", no_argument, 0, 0},
- {"stability", no_argument, 0, 0},
- {"thread", no_argument, 0, 0},
- {0, 0, 0, 0}};
+ static struct option longOptions[] = {
+ {"help", no_argument, 0, 0}, {"clients", no_argument, 0, 0},
+ {"dump", no_argument, 0, 0}, {"pid", no_argument, 0, 0},
+ {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0},
+ {"skip", no_argument, 0, 0}, {"stability", no_argument, 0, 0},
+ {"thread", no_argument, 0, 0}, {0, 0, 0, 0}};
// Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but
// happens on test cases).
@@ -178,6 +180,8 @@
dumpTypeFlags |= TYPE_STABILITY;
} else if (!strcmp(longOptions[optionIndex].name, "thread")) {
dumpTypeFlags |= TYPE_THREAD;
+ } else if (!strcmp(longOptions[optionIndex].name, "clients")) {
+ dumpTypeFlags |= TYPE_CLIENTS;
}
break;
@@ -373,6 +377,35 @@
return OK;
}
+static status_t dumpClientsToFd(const sp<IBinder>& service, const unique_fd& fd) {
+ std::string clientPids;
+ const auto remoteBinder = service->remoteBinder();
+ if (remoteBinder == nullptr) {
+ WriteStringToFd("Client PIDs are not available for local binders.\n", fd.get());
+ return OK;
+ }
+ const auto handle = remoteBinder->getDebugBinderHandle();
+ if (handle == std::nullopt) {
+ return OK;
+ }
+ std::vector<pid_t> pids;
+ pid_t myPid = getpid();
+ pid_t servicePid;
+ status_t status = service->getDebugPid(&servicePid);
+ if (status != OK) {
+ return status;
+ }
+ status =
+ getBinderClientPids(BinderDebugContext::BINDER, myPid, servicePid, handle.value(), &pids);
+ if (status != OK) {
+ return status;
+ }
+ pids.erase(std::remove_if(pids.begin(), pids.end(), [&](pid_t pid) { return pid == myPid; }),
+ pids.end());
+ WriteStringToFd("Client PIDs: " + ::android::base::Join(pids, ", ") + "\n", fd.get());
+ return OK;
+}
+
static void reportDumpError(const String16& serviceName, status_t error, const char* context) {
if (error == OK) return;
@@ -413,6 +446,10 @@
status_t err = dumpThreadsToFd(service, remote_end);
reportDumpError(serviceName, err, "dumping thread info");
}
+ if (dumpTypeFlags & TYPE_CLIENTS) {
+ status_t err = dumpClientsToFd(service, remote_end);
+ reportDumpError(serviceName, err, "dumping clients info");
+ }
// other types always act as a header, this is usually longer
if (dumpTypeFlags & TYPE_DUMP) {