diff options
author | 2021-08-05 19:05:45 +0000 | |
---|---|---|
committer | 2021-08-06 17:03:13 +0000 | |
commit | def9faea5ebf63e195f65949c4b6aae89cdb22d9 (patch) | |
tree | 05a791e1157e6d38575762161bf78d7ea99bf9d7 | |
parent | f6f2e64c29af2200e89fdf39ccc23d2bb57cce07 (diff) |
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
-rw-r--r-- | cmds/dumpsys/dumpsys.cpp | 97 | ||||
-rw-r--r-- | cmds/dumpsys/dumpsys.h | 9 | ||||
-rw-r--r-- | cmds/dumpsys/tests/dumpsys_test.cpp | 23 | ||||
-rw-r--r-- | libs/binderdebug/BinderDebug.cpp | 57 | ||||
-rw-r--r-- | libs/binderdebug/include/binderdebug/BinderDebug.h | 8 |
5 files changed, 160 insertions, 34 deletions
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index f8fdaa094e..3d2bdf1d6f 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 int sort_func(const String16* lhs, const String16* rhs) } 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 Dumpsys::main(int argc, char* const argv[]) { 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 @@ int Dumpsys::main(int argc, char* const argv[]) { 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 @@ static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) 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 Dumpsys::startDumpThread(int dumpTypeFlags, const String16& serviceName 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) { diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 05c5d5e9d1..6ab1a7dcb5 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -52,10 +52,11 @@ class Dumpsys { static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); enum Type { - TYPE_DUMP = 0x1, // dump using `dump` function - TYPE_PID = 0x2, // dump pid of server only - TYPE_STABILITY = 0x4, // dump stability information of server - TYPE_THREAD = 0x8, // dump thread usage of server only + TYPE_DUMP = 0x1, // dump using `dump` function + TYPE_PID = 0x2, // dump pid of server only + TYPE_STABILITY = 0x4, // dump stability information of server + TYPE_THREAD = 0x8, // dump thread usage of server only + TYPE_CLIENTS = 0x10, // dump pid of clients }; /** diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 312f4d714a..fa63db5533 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -627,6 +627,29 @@ TEST_F(DumpsysTest, ListServiceWithThread) { AssertOutputFormat(format); } +// Tests 'dumpsys --clients' +TEST_F(DumpsysTest, ListAllServicesWithClients) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--clients"}); + + AssertRunningServices({"Locksmith", "Valet"}); + + const std::string format("(.|\n)*((Client PIDs are not available for local binders.)(.|\n)*){2}"); + AssertOutputFormat(format); +} + +// Tests 'dumpsys --clients service_name' +TEST_F(DumpsysTest, ListServiceWithClients) { + ExpectCheckService("Locksmith"); + + CallMain({"--clients", "Locksmith"}); + + const std::string format("Client PIDs are not available for local binders.\n"); + AssertOutputFormat(format); +} // Tests 'dumpsys --thread --stability' TEST_F(DumpsysTest, ListAllServicesWithMultipleOptions) { ExpectListServices({"Locksmith", "Valet"}); diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp index b435dba197..d086b49a7b 100644 --- a/libs/binderdebug/BinderDebug.cpp +++ b/libs/binderdebug/BinderDebug.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android-base/logging.h> #include <android-base/parseint.h> #include <android-base/strings.h> #include <binder/Binder.h> @@ -116,4 +117,60 @@ status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* return ret; } +status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid, + int32_t handle, std::vector<pid_t>* pids) { + std::smatch match; + static const std::regex kNodeNumber("^\\s+ref \\d+:\\s+desc\\s+(\\d+)\\s+node\\s+(\\d+).*"); + std::string contextStr = contextToString(context); + int32_t node; + status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) { + if (std::regex_search(line, match, kNodeNumber)) { + const std::string& descString = match.str(1); + int32_t desc; + if (!::android::base::ParseInt(descString.c_str(), &desc)) { + LOG(ERROR) << "Failed to parse desc int: " << descString; + return; + } + if (handle != desc) { + return; + } + const std::string& nodeString = match.str(2); + if (!::android::base::ParseInt(nodeString.c_str(), &node)) { + LOG(ERROR) << "Failed to parse node int: " << nodeString; + return; + } + return; + } + return; + }); + if (ret != OK) { + return ret; + } + static const std::regex kClients("^\\s+node\\s+(\\d+).*proc\\s+([\\d+\\s*]*)"); + ret = scanBinderContext(servicePid, contextStr, [&](const std::string& line) { + if (std::regex_search(line, match, kClients)) { + const std::string nodeString = match.str(1); + int32_t matchedNode; + if (!::android::base::ParseInt(nodeString.c_str(), &matchedNode)) { + LOG(ERROR) << "Failed to parse node int: " << nodeString; + return; + } + if (node != matchedNode) { + return; + } + const std::string clients = match.str(2); + for (const std::string& pidStr : base::Split(clients, " ")) { + int32_t pid; + if (!::android::base::ParseInt(pidStr, &pid)) { + return; + } + pids->push_back(pid); + } + return; + } + return; + }); + return ret; +} + } // namespace android diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h index 14a0ef3672..dfd5a7cc3e 100644 --- a/libs/binderdebug/include/binderdebug/BinderDebug.h +++ b/libs/binderdebug/include/binderdebug/BinderDebug.h @@ -32,6 +32,14 @@ enum class BinderDebugContext { VNDBINDER, }; +/** + * pid is the pid of the service + */ status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo); +/** + * pid is typically the pid of this process that is making the query + */ +status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid, + int32_t handle, std::vector<pid_t>* pids); } // namespace android |