From d8e20191c2fa10a458b29bb06502487d704ba076 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Wed, 24 May 2017 11:23:08 -0700 Subject: Add thread usage output to lshal. This output shows how many threads are in use at a give time. Test: lshal, manual Bug: 35099601 Change-Id: I3a22bb131b828cdd77e73e7810229d9c68a496d3 --- cmds/lshal/ListCommand.cpp | 129 +++++++++++++++++++++++++++++++++------------ cmds/lshal/ListCommand.h | 16 ++++-- cmds/lshal/Lshal.cpp | 4 +- cmds/lshal/TableEntry.h | 13 ++++- 4 files changed, 123 insertions(+), 39 deletions(-) diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 38b406ce86..2eb58ae0a5 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -73,44 +73,90 @@ void ListCommand::removeDeadProcesses(Pids *pids) { }), pids->end()); } -bool ListCommand::getReferencedPids( - pid_t serverPid, std::map *objects) const { - - std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid)); +bool scanBinderContext(pid_t pid, + const std::string &contextName, + std::function 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; } - 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; + + if (!isDesiredContext) { continue; } - const std::string proc = " proc "; - auto pos = line.rfind(proc); - if (pos != std::string::npos) { - for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) { - int32_t pid; - if (!::android::base::ParseInt(pidStr, &pid)) { - mErr << "Could not parse number " << pidStr << std::endl; - continue; + + 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; + return; + } + const std::string proc = " proc "; + auto pos = line.rfind(proc); + if (pos != std::string::npos) { + for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) { + int32_t pid; + if (!::android::base::ParseInt(pidStr, &pid)) { + mErr << "Could not parse number " << pidStr << std::endl; + return; + } + pidInfo->refPids[ptr].push_back(pid); } - (*objects)[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 &manager) { Status status = OK; // server pid, .ptr value of binder object, child pids std::map allDebugInfos; - std::map> allPids; + std::map 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 &manager) { auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { allDebugInfos[fqInstanceName] = debugInfo; if (debugInfo.pid >= 0) { - allPids[static_cast(debugInfo.pid)].clear(); + allPids[static_cast(debugInfo.pid)] = PidInfo(); } }); if (!debugRet.isOk()) { @@ -526,9 +578,10 @@ Status ListCommand::fetchBinderized(const sp &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 &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; } diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index f367d7f1cf..a75db04960 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -48,18 +48,26 @@ 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 *objects) const; + + struct PidInfo { + std::map 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 &clientCmdlines) const ; + 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); // Call getCmdline on all pid in pids. If it returns empty string, the process might diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index 9db42f10ca..e2d5f6df0a 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -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[=]]\n" " [--debug|-d[=]]\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" diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 9ae8f7863a..e04c3ca252 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -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; -- cgit v1.2.3-59-g8ed1b