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

Commit 4a3e0a97 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add thread usage output to lshal."

parents 7bb8779a d8e20191
Loading
Loading
Loading
Loading
+96 −33
Original line number Diff line number Diff line
@@ -73,29 +73,48 @@ void ListCommand::removeDeadProcesses(Pids *pids) {
    }), pids->end());
}

bool ListCommand::getReferencedPids(
        pid_t serverPid, std::map<uint64_t, Pids> *objects) const {

    std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
bool scanBinderContext(pid_t pid,
        const std::string &contextName,
        std::function<void(const std::string&)> eachLine) {
    std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
    if (!ifs.is_open()) {
        return false;
    }

    static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
    static const std::regex kContextLine("^context (\\w+)$");

    bool isDesiredContext = false;
    std::string line;
    std::smatch match;
    while(getline(ifs, line)) {
        if (!std::regex_search(line, match, prefix)) {
            // the line doesn't start with the correct prefix
        if (std::regex_search(line, match, kContextLine)) {
            isDesiredContext = match.str(1) == contextName;
            continue;
        }

        if (!isDesiredContext) {
            continue;
        }
        std::string ptrString = "0x" + match.str(2); // use number after c

        eachLine(line);
    }
    return true;
}

bool ListCommand::getPidInfo(
        pid_t serverPid, PidInfo *pidInfo) const {
    static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
    static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");

    std::smatch match;
    return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
        if (std::regex_search(line, match, kReferencePrefix)) {
            const std::string &ptrString = "0x" + match.str(2); // use number after c
            uint64_t ptr;
            if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
                // Should not reach here, but just be tolerant.
                mErr << "Could not parse number " << ptrString << std::endl;
            continue;
                return;
            }
            const std::string proc = " proc ";
            auto pos = line.rfind(proc);
@@ -104,13 +123,40 @@ bool ListCommand::getReferencedPids(
                    int32_t pid;
                    if (!::android::base::ParseInt(pidStr, &pid)) {
                        mErr << "Could not parse number " << pidStr << std::endl;
                    continue;
                        return;
                    }
                (*objects)[ptr].push_back(pid);
                    pidInfo->refPids[ptr].push_back(pid);
                }
            }

            return;
        }
    return true;

        if (std::regex_search(line, match, kThreadPrefix)) {
            // "1" is waiting in binder driver
            // "2" is poll. It's impossible to tell if these are in use.
            //     and HIDL default code doesn't use it.
            bool isInUse = match.str(1) != "1";
            // "0" is a thread that has called into binder
            // "1" is looper thread
            // "2" is main looper thread
            bool isHwbinderThread = match.str(2) != "0";

            if (!isHwbinderThread) {
                return;
            }

            if (isInUse) {
                pidInfo->threadUsage++;
            }

            pidInfo->threadCount++;
            return;
        }

        // not reference or thread line
        return;
    });
}

// Must process hwbinder services first, then passthrough services.
@@ -164,9 +210,11 @@ void ListCommand::printLine(
        const std::string &interfaceName,
        const std::string &transport,
        const std::string &arch,
        const std::string &threadUsage,
        const std::string &server,
        const std::string &serverCmdline,
        const std::string &address, const std::string &clients,
        const std::string &address,
        const std::string &clients,
        const std::string &clientCmdlines) const {
    if (mSelectedColumns & ENABLE_INTERFACE_NAME)
        mOut << std::setw(80) << interfaceName << "\t";
@@ -174,6 +222,9 @@ void ListCommand::printLine(
        mOut << std::setw(10) << transport << "\t";
    if (mSelectedColumns & ENABLE_ARCH)
        mOut << std::setw(5) << arch << "\t";
    if (mSelectedColumns & ENABLE_THREADS) {
        mOut << std::setw(8) << threadUsage << "\t";
    }
    if (mSelectedColumns & ENABLE_SERVER_PID) {
        if (mEnableCmdlines) {
            mOut << std::setw(15) << serverCmdline << "\t";
@@ -349,14 +400,15 @@ void ListCommand::dumpTable() {
        }
        mOut << std::left;
        if (!mNeat) {
            printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
                      "PTR", "Clients", "Clients CMD");
            printLine("Interface", "Transport", "Arch", "Thread Use", "Server",
                      "Server CMD", "PTR", "Clients", "Clients CMD");
        }

        for (const auto &entry : table) {
            printLine(entry.interfaceName,
                    entry.transport,
                    getArchString(entry.arch),
                    entry.getThreadUsage(),
                    entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
                    entry.serverCmdline,
                    entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
@@ -492,7 +544,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
    Status status = OK;
    // server pid, .ptr value of binder object, child pids
    std::map<std::string, DebugInfo> allDebugInfos;
    std::map<pid_t, std::map<uint64_t, Pids>> allPids;
    std::map<pid_t, PidInfo> allPids;
    for (const auto &fqInstanceName : fqInstanceNames) {
        const auto pair = splitFirst(fqInstanceName, '/');
        const auto &serviceName = pair.first;
@@ -516,7 +568,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
        auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
            allDebugInfos[fqInstanceName] = debugInfo;
            if (debugInfo.pid >= 0) {
                allPids[static_cast<pid_t>(debugInfo.pid)].clear();
                allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
            }
        });
        if (!debugRet.isOk()) {
@@ -526,9 +578,10 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
            status |= DUMP_BINDERIZED_ERROR;
        }
    }

    for (auto &pair : allPids) {
        pid_t serverPid = pair.first;
        if (!getReferencedPids(serverPid, &allPids[serverPid])) {
        if (!getPidInfo(serverPid, &allPids[serverPid])) {
            mErr << "Warning: no information for PID " << serverPid
                      << ", are you root?" << std::endl;
            status |= DUMP_BINDERIZED_ERROR;
@@ -543,18 +596,23 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
                .serverPid = NO_PID,
                .serverObjectAddress = NO_PTR,
                .clientPids = {},
                .threadUsage = 0,
                .threadCount = 0,
                .arch = ARCH_UNKNOWN
            });
            continue;
        }
        const DebugInfo &info = it->second;
        bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;

        putEntry(HWSERVICEMANAGER_LIST, {
            .interfaceName = fqInstanceName,
            .transport = mode,
            .serverPid = info.pid,
            .serverObjectAddress = info.ptr,
            .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
                    ? Pids{} : allPids[info.pid][info.ptr],
            .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
            .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
            .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
            .arch = fromBaseArchitecture(info.arch),
        });
    }
@@ -593,6 +651,7 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
        {"pid",       no_argument,       0, 'p' },
        {"address",   no_argument,       0, 'a' },
        {"clients",   no_argument,       0, 'c' },
        {"threads",   no_argument,       0, 'e' },
        {"cmdline",   no_argument,       0, 'm' },
        {"debug",     optional_argument, 0, 'd' },

@@ -609,7 +668,7 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
    for (;;) {
        // using getopt_long in case we want to add other options in the future
        c = getopt_long(arg.argc, arg.argv,
                "hitrpacmd", longOptions, &optionIndex);
                "hitrpacmde", longOptions, &optionIndex);
        if (c == -1) {
            break;
        }
@@ -661,6 +720,10 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
            mSelectedColumns |= ENABLE_CLIENT_PIDS;
            break;
        }
        case 'e': {
            mSelectedColumns |= ENABLE_THREADS;
            break;
        }
        case 'm': {
            mEnableCmdlines = true;
            break;
@@ -695,7 +758,7 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
    }

    if (mSelectedColumns == 0) {
        mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
        mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS;
    }
    return OK;
}
+12 −4
Original line number Diff line number Diff line
@@ -48,17 +48,25 @@ private:
    Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
    Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
    Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
    bool getReferencedPids(
        pid_t serverPid, std::map<uint64_t, Pids> *objects) const;

    struct PidInfo {
        std::map<uint64_t, Pids> refPids; // pids that are referenced
        uint32_t threadUsage; // number of threads in use
        uint32_t threadCount; // number of threads total
    };
    bool getPidInfo(pid_t serverPid, PidInfo *info) const;

    void dumpTable();
    void dumpVintf() const;
    void printLine(
            const std::string &interfaceName,
            const std::string &transport,
            const std::string &arch,
            const std::string &threadUsage,
            const std::string &server,
            const std::string &serverCmdline,
            const std::string &address, const std::string &clients,
            const std::string &address,
            const std::string &clients,
            const std::string &clientCmdlines) const;
    // Return /proc/{pid}/cmdline if it exists, else empty string.
    const std::string &getCmdline(pid_t pid);
+3 −1
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ void Lshal::usage(const std::string &command) const {
            "        List all hals with default ordering and columns (`lshal list -ipc`)\n"
            "    lshal list [-h|--help]\n"
            "        -h, --help: Print help message for list (`lshal help list`)\n"
            "    lshal [list] [--interface|-i] [--transport|-t] [-r|--arch]\n"
            "    lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n"
            "            [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]\n"
            "            [--sort={interface|i|pid|p}] [--init-vintf[=<output file>]]\n"
            "            [--debug|-d[=<output file>]]\n"
@@ -74,6 +74,8 @@ void Lshal::usage(const std::string &command) const {
            "        -n, --instance: print the instance name column\n"
            "        -t, --transport: print the transport mode column\n"
            "        -r, --arch: print if the HAL is in 64-bit or 32-bit\n"
            "        -e, --threads: print currently used/available threads\n"
            "                       (note, available threads created lazily)\n"
            "        -p, --pid: print the server PID, or server cmdline if -m is set\n"
            "        -a, --address: print the server object address column\n"
            "        -c, --clients: print the client PIDs, or client cmdlines if -m is set\n"
+12 −1
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ struct TableEntry {
    std::string interfaceName;
    std::string transport;
    int32_t serverPid;
    uint32_t threadUsage;
    uint32_t threadCount;
    std::string serverCmdline;
    uint64_t serverObjectAddress;
    Pids clientPids;
@@ -59,6 +61,14 @@ struct TableEntry {
    static bool sortByServerPid(const TableEntry &a, const TableEntry &b) {
        return a.serverPid < b.serverPid;
    };

    std::string getThreadUsage() const {
        if (threadCount == 0) {
            return "N/A";
        }

        return std::to_string(threadUsage) + "/" + std::to_string(threadCount);
    }
};

struct Table {
@@ -80,7 +90,8 @@ enum : unsigned int {
    ENABLE_SERVER_PID     = 1 << 2,
    ENABLE_SERVER_ADDR    = 1 << 3,
    ENABLE_CLIENT_PIDS    = 1 << 4,
    ENABLE_ARCH           = 1 << 5
    ENABLE_ARCH           = 1 << 5,
    ENABLE_THREADS        = 1 << 6,
};

using TableEntrySelect = unsigned int;