Loading cmds/lshal/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ cc_library_shared { "libutils", "libhidlbase", "libhidltransport", "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", ], Loading cmds/lshal/ListCommand.cpp +129 −77 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/parseint.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-hash/Hash.h> #include <hidl-util/FQName.h> #include <private/android_filesystem_config.h> #include <sys/stat.h> Loading @@ -39,6 +40,9 @@ #include "utils.h" using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; namespace android { Loading Loading @@ -85,7 +89,7 @@ void ListCommand::removeDeadProcesses(Pids *pids) { }), pids->end()); } bool scanBinderContext(pid_t pid, static 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)); Loading Loading @@ -171,6 +175,16 @@ bool ListCommand::getPidInfo( }); } const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) { auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}}); if (pair.second /* did insertion take place? */) { if (!getPidInfo(serverPid, &pair.first->second)) { return nullptr; } } return &pair.first->second; } // Must process hwbinder services first, then passthrough services. void ListCommand::forEachTable(const std::function<void(Table &)> &f) { f(mServicesTable); Loading Loading @@ -445,10 +459,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { entries.emplace(interfaceName, TableEntry{ .interfaceName = interfaceName, .transport = "passthrough", .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = ARCH_UNKNOWN }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { Loading Loading @@ -479,7 +490,6 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { std::string{info.instanceName.c_str()}, .transport = "passthrough", .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) }); Loading @@ -494,10 +504,6 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { } Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { using namespace ::std; using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; const std::string mode = "hwbinder"; hidl_vec<hidl_string> fqInstanceNames; Loading @@ -512,80 +518,117 @@ 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, PidInfo> allPids; std::map<std::string, TableEntry> allTableEntries; for (const auto &fqInstanceName : fqInstanceNames) { const auto pair = splitFirst(fqInstanceName, '/'); // create entry and default assign all fields. TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; entry.transport = mode; status |= fetchBinderizedEntry(manager, &entry); } for (auto& pair : allTableEntries) { putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second)); } return status; } Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, TableEntry *entry) { Status status = OK; const auto handleError = [&](Status additionalError, const std::string& msg) { err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl; status |= DUMP_BINDERIZED_ERROR | additionalError; }; const auto pair = splitFirst(entry->interfaceName, '/'); const auto &serviceName = pair.first; const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager:" << getRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; handleError(TRANSACTION_ERROR, "cannot be fetched from service manager:" + getRet.description()); return status; } sp<IBase> service = getRet; if (service == nullptr) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager (null)" << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; } auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { allDebugInfos[fqInstanceName] = debugInfo; if (debugInfo.pid >= 0) { allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo(); handleError(NO_INTERFACE, "cannot be fetched from service manager (null)"); return status; } // getDebugInfo do { DebugInfo debugInfo; auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) { debugInfo = received; }); if (!debugRet.isOk()) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "debugging information cannot be retrieved:" << debugRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; handleError(TRANSACTION_ERROR, "debugging information cannot be retrieved: " + debugRet.description()); break; // skip getPidInfo } entry->serverPid = debugInfo.pid; entry->serverObjectAddress = debugInfo.ptr; entry->arch = fromBaseArchitecture(debugInfo.arch); if (debugInfo.pid != NO_PID) { const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid); if (pidInfo == nullptr) { handleError(IO_ERROR, "no information for PID " + std::to_string(debugInfo.pid) + ", are you root?"); break; } if (debugInfo.ptr != NO_PTR) { auto it = pidInfo->refPids.find(debugInfo.ptr); if (it != pidInfo->refPids.end()) { entry->clientPids = it->second; } } entry->threadUsage = pidInfo->threadUsage; entry->threadCount = pidInfo->threadCount; } } while (0); for (auto &pair : allPids) { pid_t serverPid = pair.first; if (!getPidInfo(serverPid, &allPids[serverPid])) { err() << "Warning: no information for PID " << serverPid << ", are you root?" << std::endl; status |= DUMP_BINDERIZED_ERROR; // hash do { ssize_t hashIndex = -1; auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) { for (size_t i = 0; i < c.size(); ++i) { if (serviceName == c[i]) { hashIndex = static_cast<ssize_t>(i); break; } } for (const auto &fqInstanceName : fqInstanceNames) { auto it = allDebugInfos.find(fqInstanceName); if (it == allDebugInfos.end()) { putEntry(HWSERVICEMANAGER_LIST, { .interfaceName = fqInstanceName, .transport = mode, .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = {}, .threadUsage = 0, .threadCount = 0, .arch = ARCH_UNKNOWN }); continue; if (!ifaceChainRet.isOk()) { handleError(TRANSACTION_ERROR, "interfaceChain fails: " + ifaceChainRet.description()); break; // skip getHashChain } if (hashIndex < 0) { handleError(BAD_IMPL, "Interface name does not exist in interfaceChain."); break; // skip getHashChain } auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) { if (static_cast<size_t>(hashIndex) >= hashChain.size()) { handleError(BAD_IMPL, "interfaceChain indicates position " + std::to_string(hashIndex) + " but getHashChain returns " + std::to_string(hashChain.size()) + " hashes"); return; } 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 = 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), auto&& hashArray = hashChain[hashIndex]; std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()}; entry->hash = Hash::hexString(hashVec); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } } while (0); return status; } Loading Loading @@ -623,6 +666,10 @@ void ListCommand::registerAllOptions() { thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); return OK; }, "print the instance name column"}); mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::RELEASED); return OK; }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"}); mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT); return OK; Loading @@ -631,6 +678,10 @@ void ListCommand::registerAllOptions() { thiz->mSelectedColumns.push_back(TableColumnType::ARCH); return OK; }, "print the bitness column"}); mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::HASH); return OK; }, "print hash of the interface"}); mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID); return OK; Loading Loading @@ -773,7 +824,8 @@ Status ListCommand::parseArgs(const Arg &arg) { } if (mSelectedColumns.empty()) { mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, mSelectedColumns = {TableColumnType::RELEASED, TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } Loading Loading @@ -841,7 +893,7 @@ void ListCommand::usage() const { err() << "list:" << std::endl << " lshal" << std::endl << " lshal list" << std::endl << " List all hals with default ordering and columns (`lshal list -iepc`)" << std::endl << " List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl << " lshal list [-h|--help]" << std::endl << " -h, --help: Print help message for list (`lshal help list`)" << std::endl << " lshal [list] [OPTIONS...]" << std::endl; Loading cmds/lshal/ListCommand.h +11 −1 Original line number Diff line number Diff line Loading @@ -78,14 +78,21 @@ public: protected: Status parseArgs(const Arg &arg); Status fetch(); void postprocess(); virtual void postprocess(); Status dump(); void putEntry(TableEntrySource source, TableEntry &&entry); 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); Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, TableEntry *entry); // Get relevant information for a PID by parsing files under /d/binder. // It is a virtual member function so that it can be mocked. virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; // Retrieve from mCachedPidInfos and call getPidInfo if necessary. const PidInfo* getPidInfoCached(pid_t serverPid); void dumpTable(const NullableOStream<std::ostream>& out) const; void dumpVintf(const NullableOStream<std::ostream>& out) const; Loading Loading @@ -129,6 +136,9 @@ protected: // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline. std::map<pid_t, std::string> mCmdlines; // Cache for getPidInfo. std::map<pid_t, PidInfo> mCachedPidInfos; RegisteredOptions mOptions; // All selected columns std::vector<TableColumnType> mSelectedColumns; Loading cmds/lshal/TableEntry.cpp +19 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #define LOG_TAG "lshal" #include <android-base/logging.h> #include <hidl-hash/Hash.h> #include "TableEntry.h" #include "TextTable.h" Loading Loading @@ -53,8 +55,10 @@ static std::string getTitle(TableColumnType type) { case TableColumnType::CLIENT_CMDS: return "Clients CMD"; case TableColumnType::ARCH: return "Arch"; case TableColumnType::THREADS: return "Thread Use"; case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; default: LOG(FATAL) << "Should not reach here."; LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; } } Loading @@ -79,12 +83,25 @@ std::string TableEntry::getField(TableColumnType type) const { return getArchString(arch); case TableColumnType::THREADS: return getThreadUsage(); case TableColumnType::RELEASED: return isReleased(); case TableColumnType::HASH: return hash; default: LOG(FATAL) << "Should not reach here."; LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; } } std::string TableEntry::isReleased() const { static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); if (hash.empty() || hash == unreleased) { return " "; // unknown or unreleased } return "Y"; // released } TextTable Table::createTextTable(bool neat, const std::function<std::string(const std::string&)>& emitDebugInfo) const { Loading cmds/lshal/TableEntry.h +21 −15 Original line number Diff line number Diff line Loading @@ -55,19 +55,28 @@ enum class TableColumnType : unsigned int { CLIENT_CMDS, ARCH, THREADS, RELEASED, HASH, }; enum { NO_PID = -1, NO_PTR = 0 }; 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; std::vector<std::string> clientCmdlines; Architecture arch; std::string interfaceName{}; std::string transport{}; int32_t serverPid{NO_PID}; uint32_t threadUsage{0}; uint32_t threadCount{0}; std::string serverCmdline{}; uint64_t serverObjectAddress{NO_PTR}; Pids clientPids{}; std::vector<std::string> clientCmdlines{}; Architecture arch{ARCH_UNKNOWN}; // empty: unknown, all zeros: unreleased, otherwise: released std::string hash{}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; Loading @@ -84,6 +93,8 @@ struct TableEntry { return std::to_string(threadUsage) + "/" + std::to_string(threadCount); } std::string isReleased() const; std::string getField(TableColumnType type) const; bool operator==(const TableEntry& other) const; Loading Loading @@ -129,11 +140,6 @@ private: std::vector<const Table*> mTables; }; enum { NO_PID = -1, NO_PTR = 0 }; } // namespace lshal } // namespace android Loading Loading
cmds/lshal/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ cc_library_shared { "libutils", "libhidlbase", "libhidltransport", "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", ], Loading
cmds/lshal/ListCommand.cpp +129 −77 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <android-base/parseint.h> #include <android/hidl/manager/1.0/IServiceManager.h> #include <hidl-hash/Hash.h> #include <hidl-util/FQName.h> #include <private/android_filesystem_config.h> #include <sys/stat.h> Loading @@ -39,6 +40,9 @@ #include "utils.h" using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; namespace android { Loading Loading @@ -85,7 +89,7 @@ void ListCommand::removeDeadProcesses(Pids *pids) { }), pids->end()); } bool scanBinderContext(pid_t pid, static 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)); Loading Loading @@ -171,6 +175,16 @@ bool ListCommand::getPidInfo( }); } const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) { auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}}); if (pair.second /* did insertion take place? */) { if (!getPidInfo(serverPid, &pair.first->second)) { return nullptr; } } return &pair.first->second; } // Must process hwbinder services first, then passthrough services. void ListCommand::forEachTable(const std::function<void(Table &)> &f) { f(mServicesTable); Loading Loading @@ -445,10 +459,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { entries.emplace(interfaceName, TableEntry{ .interfaceName = interfaceName, .transport = "passthrough", .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = ARCH_UNKNOWN }).first->second.arch |= fromBaseArchitecture(info.arch); } for (auto &&pair : entries) { Loading Loading @@ -479,7 +490,6 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { std::string{info.instanceName.c_str()}, .transport = "passthrough", .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, .serverObjectAddress = NO_PTR, .clientPids = info.clientPids, .arch = fromBaseArchitecture(info.arch) }); Loading @@ -494,10 +504,6 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { } Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { using namespace ::std; using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; const std::string mode = "hwbinder"; hidl_vec<hidl_string> fqInstanceNames; Loading @@ -512,80 +518,117 @@ 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, PidInfo> allPids; std::map<std::string, TableEntry> allTableEntries; for (const auto &fqInstanceName : fqInstanceNames) { const auto pair = splitFirst(fqInstanceName, '/'); // create entry and default assign all fields. TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; entry.transport = mode; status |= fetchBinderizedEntry(manager, &entry); } for (auto& pair : allTableEntries) { putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second)); } return status; } Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, TableEntry *entry) { Status status = OK; const auto handleError = [&](Status additionalError, const std::string& msg) { err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl; status |= DUMP_BINDERIZED_ERROR | additionalError; }; const auto pair = splitFirst(entry->interfaceName, '/'); const auto &serviceName = pair.first; const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager:" << getRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; handleError(TRANSACTION_ERROR, "cannot be fetched from service manager:" + getRet.description()); return status; } sp<IBase> service = getRet; if (service == nullptr) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager (null)" << std::endl; status |= DUMP_BINDERIZED_ERROR; continue; } auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { allDebugInfos[fqInstanceName] = debugInfo; if (debugInfo.pid >= 0) { allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo(); handleError(NO_INTERFACE, "cannot be fetched from service manager (null)"); return status; } // getDebugInfo do { DebugInfo debugInfo; auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) { debugInfo = received; }); if (!debugRet.isOk()) { err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "debugging information cannot be retrieved:" << debugRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; handleError(TRANSACTION_ERROR, "debugging information cannot be retrieved: " + debugRet.description()); break; // skip getPidInfo } entry->serverPid = debugInfo.pid; entry->serverObjectAddress = debugInfo.ptr; entry->arch = fromBaseArchitecture(debugInfo.arch); if (debugInfo.pid != NO_PID) { const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid); if (pidInfo == nullptr) { handleError(IO_ERROR, "no information for PID " + std::to_string(debugInfo.pid) + ", are you root?"); break; } if (debugInfo.ptr != NO_PTR) { auto it = pidInfo->refPids.find(debugInfo.ptr); if (it != pidInfo->refPids.end()) { entry->clientPids = it->second; } } entry->threadUsage = pidInfo->threadUsage; entry->threadCount = pidInfo->threadCount; } } while (0); for (auto &pair : allPids) { pid_t serverPid = pair.first; if (!getPidInfo(serverPid, &allPids[serverPid])) { err() << "Warning: no information for PID " << serverPid << ", are you root?" << std::endl; status |= DUMP_BINDERIZED_ERROR; // hash do { ssize_t hashIndex = -1; auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) { for (size_t i = 0; i < c.size(); ++i) { if (serviceName == c[i]) { hashIndex = static_cast<ssize_t>(i); break; } } for (const auto &fqInstanceName : fqInstanceNames) { auto it = allDebugInfos.find(fqInstanceName); if (it == allDebugInfos.end()) { putEntry(HWSERVICEMANAGER_LIST, { .interfaceName = fqInstanceName, .transport = mode, .serverPid = NO_PID, .serverObjectAddress = NO_PTR, .clientPids = {}, .threadUsage = 0, .threadCount = 0, .arch = ARCH_UNKNOWN }); continue; if (!ifaceChainRet.isOk()) { handleError(TRANSACTION_ERROR, "interfaceChain fails: " + ifaceChainRet.description()); break; // skip getHashChain } if (hashIndex < 0) { handleError(BAD_IMPL, "Interface name does not exist in interfaceChain."); break; // skip getHashChain } auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) { if (static_cast<size_t>(hashIndex) >= hashChain.size()) { handleError(BAD_IMPL, "interfaceChain indicates position " + std::to_string(hashIndex) + " but getHashChain returns " + std::to_string(hashChain.size()) + " hashes"); return; } 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 = 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), auto&& hashArray = hashChain[hashIndex]; std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()}; entry->hash = Hash::hexString(hashVec); }); if (!hashRet.isOk()) { handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } } while (0); return status; } Loading Loading @@ -623,6 +666,10 @@ void ListCommand::registerAllOptions() { thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); return OK; }, "print the instance name column"}); mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::RELEASED); return OK; }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"}); mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT); return OK; Loading @@ -631,6 +678,10 @@ void ListCommand::registerAllOptions() { thiz->mSelectedColumns.push_back(TableColumnType::ARCH); return OK; }, "print the bitness column"}); mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::HASH); return OK; }, "print hash of the interface"}); mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) { thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID); return OK; Loading Loading @@ -773,7 +824,8 @@ Status ListCommand::parseArgs(const Arg &arg) { } if (mSelectedColumns.empty()) { mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, mSelectedColumns = {TableColumnType::RELEASED, TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } Loading Loading @@ -841,7 +893,7 @@ void ListCommand::usage() const { err() << "list:" << std::endl << " lshal" << std::endl << " lshal list" << std::endl << " List all hals with default ordering and columns (`lshal list -iepc`)" << std::endl << " List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl << " lshal list [-h|--help]" << std::endl << " -h, --help: Print help message for list (`lshal help list`)" << std::endl << " lshal [list] [OPTIONS...]" << std::endl; Loading
cmds/lshal/ListCommand.h +11 −1 Original line number Diff line number Diff line Loading @@ -78,14 +78,21 @@ public: protected: Status parseArgs(const Arg &arg); Status fetch(); void postprocess(); virtual void postprocess(); Status dump(); void putEntry(TableEntrySource source, TableEntry &&entry); 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); Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, TableEntry *entry); // Get relevant information for a PID by parsing files under /d/binder. // It is a virtual member function so that it can be mocked. virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; // Retrieve from mCachedPidInfos and call getPidInfo if necessary. const PidInfo* getPidInfoCached(pid_t serverPid); void dumpTable(const NullableOStream<std::ostream>& out) const; void dumpVintf(const NullableOStream<std::ostream>& out) const; Loading Loading @@ -129,6 +136,9 @@ protected: // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline. std::map<pid_t, std::string> mCmdlines; // Cache for getPidInfo. std::map<pid_t, PidInfo> mCachedPidInfos; RegisteredOptions mOptions; // All selected columns std::vector<TableColumnType> mSelectedColumns; Loading
cmds/lshal/TableEntry.cpp +19 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #define LOG_TAG "lshal" #include <android-base/logging.h> #include <hidl-hash/Hash.h> #include "TableEntry.h" #include "TextTable.h" Loading Loading @@ -53,8 +55,10 @@ static std::string getTitle(TableColumnType type) { case TableColumnType::CLIENT_CMDS: return "Clients CMD"; case TableColumnType::ARCH: return "Arch"; case TableColumnType::THREADS: return "Thread Use"; case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; default: LOG(FATAL) << "Should not reach here."; LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; } } Loading @@ -79,12 +83,25 @@ std::string TableEntry::getField(TableColumnType type) const { return getArchString(arch); case TableColumnType::THREADS: return getThreadUsage(); case TableColumnType::RELEASED: return isReleased(); case TableColumnType::HASH: return hash; default: LOG(FATAL) << "Should not reach here."; LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; } } std::string TableEntry::isReleased() const { static const std::string unreleased = Hash::hexString(Hash::kEmptyHash); if (hash.empty() || hash == unreleased) { return " "; // unknown or unreleased } return "Y"; // released } TextTable Table::createTextTable(bool neat, const std::function<std::string(const std::string&)>& emitDebugInfo) const { Loading
cmds/lshal/TableEntry.h +21 −15 Original line number Diff line number Diff line Loading @@ -55,19 +55,28 @@ enum class TableColumnType : unsigned int { CLIENT_CMDS, ARCH, THREADS, RELEASED, HASH, }; enum { NO_PID = -1, NO_PTR = 0 }; 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; std::vector<std::string> clientCmdlines; Architecture arch; std::string interfaceName{}; std::string transport{}; int32_t serverPid{NO_PID}; uint32_t threadUsage{0}; uint32_t threadCount{0}; std::string serverCmdline{}; uint64_t serverObjectAddress{NO_PTR}; Pids clientPids{}; std::vector<std::string> clientCmdlines{}; Architecture arch{ARCH_UNKNOWN}; // empty: unknown, all zeros: unreleased, otherwise: released std::string hash{}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; Loading @@ -84,6 +93,8 @@ struct TableEntry { return std::to_string(threadUsage) + "/" + std::to_string(threadCount); } std::string isReleased() const; std::string getField(TableColumnType type) const; bool operator==(const TableEntry& other) const; Loading Loading @@ -129,11 +140,6 @@ private: std::vector<const Table*> mTables; }; enum { NO_PID = -1, NO_PTR = 0 }; } // namespace lshal } // namespace android Loading