Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit def9faea authored by Devin Moore's avatar Devin Moore
Browse files

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
parent f6f2e64c
Loading
Loading
Loading
Loading
+67 −30
Original line number Diff line number Diff line
@@ -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,17 +60,20 @@ static int sort_func(const String16* lhs, const String16* rhs)
}

static void usage() {
    fprintf(stderr,
    fprintf(
        stderr,
        "usage: dumpsys\n"
        "         To dump all services.\n"
        "or:\n"
            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--dump] [--pid] [--thread] [--help | "
        "       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"
@@ -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) {
+5 −4
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ class Dumpsys {
        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
    };

    /**
+23 −0
Original line number Diff line number Diff line
@@ -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"});
+57 −0
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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