Loading cmds/lshal/ListCommand.cpp +96 −33 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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. Loading Loading @@ -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"; Loading @@ -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"; Loading Loading @@ -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), Loading Loading @@ -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; Loading @@ -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()) { Loading @@ -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; Loading @@ -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), }); } Loading Loading @@ -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' }, Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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; } Loading cmds/lshal/ListCommand.h +12 −4 Original line number Diff line number Diff line Loading @@ -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); Loading cmds/lshal/Lshal.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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" Loading cmds/lshal/TableEntry.h +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading Loading
cmds/lshal/ListCommand.cpp +96 −33 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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. Loading Loading @@ -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"; Loading @@ -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"; Loading Loading @@ -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), Loading Loading @@ -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; Loading @@ -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()) { Loading @@ -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; Loading @@ -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), }); } Loading Loading @@ -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' }, Loading @@ -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; } Loading Loading @@ -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; Loading Loading @@ -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; } Loading
cmds/lshal/ListCommand.h +12 −4 Original line number Diff line number Diff line Loading @@ -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); Loading
cmds/lshal/Lshal.cpp +3 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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" Loading
cmds/lshal/TableEntry.h +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 { Loading @@ -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; Loading