Loading cmds/lshal/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ cc_library_shared { "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", "TableEntry.cpp", "TextTable.cpp", "utils.cpp", ], Loading Loading @@ -60,6 +61,7 @@ cc_test { "libgmock" ], shared_libs: [ "libvintf", "android.hardware.tests.baz@1.0" ], srcs: [ Loading cmds/lshal/ListCommand.cpp +117 −178 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ #include "Lshal.h" #include "PipeRelay.h" #include "TextTable.h" #include "Timeout.h" #include "utils.h" Loading @@ -45,10 +44,18 @@ using ::android::hidl::manager::V1_0::IServiceManager; namespace android { namespace lshal { ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) { ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal) { } std::string getCmdline(pid_t pid) { NullableOStream<std::ostream> ListCommand::out() const { return mLshal.out(); } NullableOStream<std::ostream> ListCommand::err() const { return mLshal.err(); } std::string ListCommand::parseCmdline(pid_t pid) const { std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); std::string cmdline; if (!ifs.is_open()) { Loading @@ -63,7 +70,7 @@ const std::string &ListCommand::getCmdline(pid_t pid) { if (pair != mCmdlines.end()) { return pair->second; } mCmdlines[pid] = ::android::lshal::getCmdline(pid); mCmdlines[pid] = parseCmdline(pid); return mCmdlines[pid]; } Loading Loading @@ -114,7 +121,7 @@ bool ListCommand::getPidInfo( 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; err() << "Could not parse number " << ptrString << std::endl; return; } const std::string proc = " proc "; Loading @@ -123,7 +130,7 @@ bool ListCommand::getPidInfo( 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; err() << "Could not parse number " << pidStr << std::endl; return; } pidInfo->refPids[ptr].push_back(pid); Loading Loading @@ -205,48 +212,18 @@ void ListCommand::postprocess() { } } } } void ListCommand::addLine(TextTable *textTable, 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 { std::vector<std::string> columns; for (TableColumnType type : mSelectedColumns) { switch (type) { case TableColumnType::INTERFACE_NAME: { columns.push_back(interfaceName); } break; case TableColumnType::TRANSPORT: { columns.push_back(transport); } break; case TableColumnType::ARCH: { columns.push_back(arch); } break; case TableColumnType::THREADS: { columns.push_back(threadUsage); } break; case TableColumnType::SERVER_ADDR: { columns.push_back(address); } break; case TableColumnType::SERVER_PID: { if (mEnableCmdlines) { columns.push_back(serverCmdline); } else { columns.push_back(server); } } break; case TableColumnType::CLIENT_PIDS: { if (mEnableCmdlines) { columns.push_back(clientCmdlines); } else { columns.push_back(clients); } } break; } } textTable->add(std::move(columns)); mServicesTable.setDescription( "All binderized services (registered services through hwservicemanager)"); mPassthroughRefTable.setDescription( "All interfaces that getService() has ever return as a passthrough interface;\n" "PIDs / processes shown below might be inaccurate because the process\n" "might have relinquished the interface or might have died.\n" "The Server / Server CMD column can be ignored.\n" "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" "the library and successfully fetched the passthrough implementation."); mImplementationsTable.setDescription( "All available passthrough implementations (all -impl.so files)"); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { Loading @@ -259,9 +236,9 @@ static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Vers return false; } void ListCommand::dumpVintf() const { void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; mOut << "<!-- " << std::endl out << "<!-- " << std::endl << " This is a skeleton device manifest. Notes: " << std::endl << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl Loading @@ -288,7 +265,7 @@ void ListCommand::dumpVintf() const { auto splittedFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName(splittedFqInstanceName.first); if (!fqName.isValid()) { mErr << "Warning: '" << splittedFqInstanceName.first err() << "Warning: '" << splittedFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } Loading Loading @@ -321,12 +298,12 @@ void ListCommand::dumpVintf() const { arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: mErr << "Warning: '" << fqName.package() err() << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; } } else { mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; } Loading @@ -334,7 +311,7 @@ void ListCommand::dumpVintf() const { for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { mErr << "Fatal: should not reach here. Generated result may be wrong for '" err() << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } Loading Loading @@ -365,29 +342,11 @@ void ListCommand::dumpVintf() const { .versions = {version}, .transportArch = {transport, arch}, .interfaces = interfaces})) { mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; } } }); mOut << vintf::gHalManifestConverter(manifest); } static const std::string &getArchString(Architecture arch) { static const std::string sStr64 = "64"; static const std::string sStr32 = "32"; static const std::string sStrBoth = "32+64"; static const std::string sStrUnknown = ""; switch (arch) { case ARCH64: return sStr64; case ARCH32: return sStr32; case ARCH_BOTH: return sStrBoth; case ARCH_UNKNOWN: // fall through default: return sStrUnknown; } out << vintf::gHalManifestConverter(manifest); } static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { Loading @@ -402,78 +361,54 @@ static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo: } } void ListCommand::addLine(TextTable *table, const TableEntry &entry) { addLine(table, 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), join(entry.clientPids, " "), join(entry.clientCmdlines, ";")); } void ListCommand::dumpTable() { void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const { if (mNeat) { TextTable textTable; forEachTable([this, &textTable](const Table &table) { for (const auto &entry : table) addLine(&textTable, entry); }); textTable.dump(mOut.buf()); MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable}) .createTextTable().dump(out.buf()); return; } mServicesTable.description = "All binderized services (registered services through hwservicemanager)"; mPassthroughRefTable.description = "All interfaces that getService() has ever return as a passthrough interface;\n" "PIDs / processes shown below might be inaccurate because the process\n" "might have relinquished the interface or might have died.\n" "The Server / Server CMD column can be ignored.\n" "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" "the library and successfully fetched the passthrough implementation."; mImplementationsTable.description = "All available passthrough implementations (all -impl.so files)"; forEachTable([this](const Table &table) { TextTable textTable; textTable.add(table.description); addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD", "PTR", "Clients", "Clients CMD"); forEachTable([this, &out](const Table &table) { for (const auto &entry : table) { addLine(&textTable, entry); // We're only interested in dumping debug info for already // instantiated services. There's little value in dumping the // debug info for a service we create on the fly, so we only operate // on the "mServicesTable". std::function<std::string(const std::string&)> emitDebugInfo = nullptr; if (mEmitDebugInfo && &table == &mServicesTable) { std::stringstream out; auto pair = splitFirst(entry.interfaceName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, out, emitDebugInfo = [this](const auto& iName) { std::stringstream ss; auto pair = splitFirst(iName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, ss, NullableOStream<std::ostream>(nullptr)); textTable.add(out.str()); return ss.str(); }; } table.createTextTable(mNeat, emitDebugInfo).dump(out.buf()); out << std::endl; }); } // Add empty line after each table textTable.add(); Status ListCommand::dump() { auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable; textTable.dump(mOut.buf()); }); if (mFileOutputPath.empty()) { (*this.*dump)(out()); return OK; } void ListCommand::dump() { if (mVintf) { dumpVintf(); if (!!mFileOutput) { mFileOutput.buf().close(); delete &mFileOutput.buf(); mFileOutput = nullptr; } mOut = std::cout; } else { dumpTable(); std::ofstream fileOutput(mFileOutputPath); if (!fileOutput.is_open()) { err() << "Could not open file '" << mFileOutputPath << "'." << std::endl; return IO_ERROR; } chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL); (*this.*dump)(NullableOStream<std::ostream>(fileOutput)); fileOutput.flush(); fileOutput.close(); return OK; } void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { Loading @@ -486,10 +421,10 @@ void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { case LIST_DLLIB : table = &mImplementationsTable; break; default: mErr << "Error: Unknown source of entry " << source << std::endl; err() << "Error: Unknown source of entry " << source << std::endl; } if (table) { table->entries.push_back(std::forward<TableEntry>(entry)); table->add(std::forward<TableEntry>(entry)); } } Loading Loading @@ -517,7 +452,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { } }); if (!ret.isOk()) { mErr << "Error: Failed to call list on getPassthroughServiceManager(): " err() << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } Loading Loading @@ -547,7 +482,7 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { } }); if (!ret.isOk()) { mErr << "Error: Failed to call debugDump on defaultServiceManager(): " err() << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } Loading @@ -567,7 +502,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { fqInstanceNames = names; }); if (!listRet.isOk()) { mErr << "Error: Failed to list services for " << mode << ": " err() << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; return DUMP_BINDERIZED_ERROR; } Loading @@ -582,7 +517,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager:" << getRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -590,7 +525,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { } sp<IBase> service = getRet; if (service == nullptr) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager (null)" << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -603,7 +538,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { } }); if (!debugRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "debugging information cannot be retrieved:" << debugRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -613,7 +548,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { for (auto &pair : allPids) { pid_t serverPid = pair.first; if (!getPidInfo(serverPid, &allPids[serverPid])) { mErr << "Warning: no information for PID " << serverPid err() << "Warning: no information for PID " << serverPid << ", are you root?" << std::endl; status |= DUMP_BINDERIZED_ERROR; } Loading Loading @@ -654,7 +589,7 @@ Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); if (bManager == nullptr) { mErr << "Failed to get defaultServiceManager()!" << std::endl; err() << "Failed to get defaultServiceManager()!" << std::endl; status |= NO_BINDERIZED_MANAGER; } else { status |= fetchBinderized(bManager); Loading @@ -664,7 +599,7 @@ Status ListCommand::fetch() { auto pManager = mLshal.passthroughManager(); if (pManager == nullptr) { mErr << "Failed to get getPassthroughServiceManager()!" << std::endl; err() << "Failed to get getPassthroughServiceManager()!" << std::endl; status |= NO_PASSTHROUGH_MANAGER; } else { status |= fetchAllLibraries(pManager); Loading Loading @@ -693,6 +628,9 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { { 0, 0, 0, 0 } }; std::vector<TableColumnType> selectedColumns; bool enableCmdlines = false; int optionIndex; int c; // Lshal::parseArgs has set optind to the next option to parse Loading @@ -710,67 +648,52 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) { mSortColumn = TableEntry::sortByServerPid; } else { mErr << "Unrecognized sorting column: " << optarg << std::endl; err() << "Unrecognized sorting column: " << optarg << std::endl; mLshal.usage(command); return USAGE; } break; } case 'v': { if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } } mVintf = true; if (optarg) mFileOutputPath = optarg; break; } case 'i': { mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); selectedColumns.push_back(TableColumnType::INTERFACE_NAME); break; } case 't': { mSelectedColumns.push_back(TableColumnType::TRANSPORT); selectedColumns.push_back(TableColumnType::TRANSPORT); break; } case 'r': { mSelectedColumns.push_back(TableColumnType::ARCH); selectedColumns.push_back(TableColumnType::ARCH); break; } case 'p': { mSelectedColumns.push_back(TableColumnType::SERVER_PID); selectedColumns.push_back(TableColumnType::SERVER_PID); break; } case 'a': { mSelectedColumns.push_back(TableColumnType::SERVER_ADDR); selectedColumns.push_back(TableColumnType::SERVER_ADDR); break; } case 'c': { mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS); selectedColumns.push_back(TableColumnType::CLIENT_PIDS); break; } case 'e': { mSelectedColumns.push_back(TableColumnType::THREADS); selectedColumns.push_back(TableColumnType::THREADS); break; } case 'm': { mEnableCmdlines = true; enableCmdlines = true; break; } case 'd': { mEmitDebugInfo = true; if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } chown(optarg, AID_SHELL, AID_SHELL); } if (optarg) mFileOutputPath = optarg; break; } case 'n': { Loading @@ -785,21 +708,37 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { } if (optind < arg.argc) { // see non option mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; err() << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; mLshal.usage(command); return USAGE; } if (mNeat && mEmitDebugInfo) { mErr << "Error: --neat should not be used with --debug." << std::endl; err() << "Error: --neat should not be used with --debug." << std::endl; mLshal.usage(command); return USAGE; } if (mSelectedColumns.empty()) { mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, if (selectedColumns.empty()) { selectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } if (enableCmdlines) { for (size_t i = 0; i < selectedColumns.size(); ++i) { if (selectedColumns[i] == TableColumnType::SERVER_PID) { selectedColumns[i] = TableColumnType::SERVER_CMD; } if (selectedColumns[i] == TableColumnType::CLIENT_PIDS) { selectedColumns[i] = TableColumnType::CLIENT_CMDS; } } } forEachTable([&selectedColumns] (Table& table) { table.setSelectedColumns(selectedColumns); }); return OK; } Loading @@ -810,7 +749,7 @@ Status ListCommand::main(const std::string &command, const Arg &arg) { } status = fetch(); postprocess(); dump(); status |= dump(); return status; } Loading cmds/lshal/ListCommand.h +18 −17 Original line number Diff line number Diff line Loading @@ -36,34 +36,38 @@ namespace lshal { class Lshal; 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 }; class ListCommand { public: ListCommand(Lshal &lshal); virtual ~ListCommand() = default; Status main(const std::string &command, const Arg &arg); private: protected: Status parseArgs(const std::string &command, const Arg &arg); Status fetch(); void postprocess(); void dump(); 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); 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; virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; void dumpTable(); void dumpVintf() const; void dumpTable(const NullableOStream<std::ostream>& out) const; void dumpVintf(const NullableOStream<std::ostream>& out) const; void addLine(TextTable *table, 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; void addLine(TextTable *table, const TableEntry &entry); // Read and return /proc/{pid}/cmdline. virtual std::string parseCmdline(pid_t pid) 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 Loading @@ -72,21 +76,18 @@ private: void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; NullableOStream<std::ostream> err() const; NullableOStream<std::ostream> out() const; Lshal &mLshal; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; NullableOStream<std::ostream> mErr; NullableOStream<std::ostream> mOut; NullableOStream<std::ofstream> mFileOutput = nullptr; std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; std::vector<TableColumnType> mSelectedColumns; // If true, cmdlines will be printed instead of pid. bool mEnableCmdlines = false; // If true, calls IBase::debug(...) on each service. bool mEmitDebugInfo = false; // If true, output in VINTF format. Loading cmds/lshal/Lshal.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -109,15 +109,15 @@ void Lshal::usage(const std::string &command) const { " Print help message for debug\n"; if (command == "list") { mErr << list; err() << list; return; } if (command == "debug") { mErr << debug; err() << debug; return; } mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help; err() << helpSummary << "\n" << list << "\n" << debug << "\n" << help; } // A unique_ptr type using a custom deleter function. Loading Loading @@ -206,7 +206,7 @@ Status Lshal::parseArgs(const Arg &arg) { return OK; } mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; usage(); return USAGE; } Loading cmds/lshal/Lshal.h +3 −2 Original line number Diff line number Diff line Loading @@ -33,13 +33,14 @@ namespace lshal { class Lshal { public: Lshal(); virtual ~Lshal() {} Lshal(std::ostream &out, std::ostream &err, sp<hidl::manager::V1_0::IServiceManager> serviceManager, sp<hidl::manager::V1_0::IServiceManager> passthroughManager); Status main(const Arg &arg); void usage(const std::string &command = "") const; NullableOStream<std::ostream> err() const; NullableOStream<std::ostream> out() const; virtual NullableOStream<std::ostream> err() const; virtual NullableOStream<std::ostream> out() const; const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const; const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const; Loading Loading
cmds/lshal/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ cc_library_shared { "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", "TableEntry.cpp", "TextTable.cpp", "utils.cpp", ], Loading Loading @@ -60,6 +61,7 @@ cc_test { "libgmock" ], shared_libs: [ "libvintf", "android.hardware.tests.baz@1.0" ], srcs: [ Loading
cmds/lshal/ListCommand.cpp +117 −178 Original line number Diff line number Diff line Loading @@ -35,7 +35,6 @@ #include "Lshal.h" #include "PipeRelay.h" #include "TextTable.h" #include "Timeout.h" #include "utils.h" Loading @@ -45,10 +44,18 @@ using ::android::hidl::manager::V1_0::IServiceManager; namespace android { namespace lshal { ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) { ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal) { } std::string getCmdline(pid_t pid) { NullableOStream<std::ostream> ListCommand::out() const { return mLshal.out(); } NullableOStream<std::ostream> ListCommand::err() const { return mLshal.err(); } std::string ListCommand::parseCmdline(pid_t pid) const { std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); std::string cmdline; if (!ifs.is_open()) { Loading @@ -63,7 +70,7 @@ const std::string &ListCommand::getCmdline(pid_t pid) { if (pair != mCmdlines.end()) { return pair->second; } mCmdlines[pid] = ::android::lshal::getCmdline(pid); mCmdlines[pid] = parseCmdline(pid); return mCmdlines[pid]; } Loading Loading @@ -114,7 +121,7 @@ bool ListCommand::getPidInfo( 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; err() << "Could not parse number " << ptrString << std::endl; return; } const std::string proc = " proc "; Loading @@ -123,7 +130,7 @@ bool ListCommand::getPidInfo( 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; err() << "Could not parse number " << pidStr << std::endl; return; } pidInfo->refPids[ptr].push_back(pid); Loading Loading @@ -205,48 +212,18 @@ void ListCommand::postprocess() { } } } } void ListCommand::addLine(TextTable *textTable, 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 { std::vector<std::string> columns; for (TableColumnType type : mSelectedColumns) { switch (type) { case TableColumnType::INTERFACE_NAME: { columns.push_back(interfaceName); } break; case TableColumnType::TRANSPORT: { columns.push_back(transport); } break; case TableColumnType::ARCH: { columns.push_back(arch); } break; case TableColumnType::THREADS: { columns.push_back(threadUsage); } break; case TableColumnType::SERVER_ADDR: { columns.push_back(address); } break; case TableColumnType::SERVER_PID: { if (mEnableCmdlines) { columns.push_back(serverCmdline); } else { columns.push_back(server); } } break; case TableColumnType::CLIENT_PIDS: { if (mEnableCmdlines) { columns.push_back(clientCmdlines); } else { columns.push_back(clients); } } break; } } textTable->add(std::move(columns)); mServicesTable.setDescription( "All binderized services (registered services through hwservicemanager)"); mPassthroughRefTable.setDescription( "All interfaces that getService() has ever return as a passthrough interface;\n" "PIDs / processes shown below might be inaccurate because the process\n" "might have relinquished the interface or might have died.\n" "The Server / Server CMD column can be ignored.\n" "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" "the library and successfully fetched the passthrough implementation."); mImplementationsTable.setDescription( "All available passthrough implementations (all -impl.so files)"); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { Loading @@ -259,9 +236,9 @@ static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Vers return false; } void ListCommand::dumpVintf() const { void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { using vintf::operator|=; mOut << "<!-- " << std::endl out << "<!-- " << std::endl << " This is a skeleton device manifest. Notes: " << std::endl << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl Loading @@ -288,7 +265,7 @@ void ListCommand::dumpVintf() const { auto splittedFqInstanceName = splitFirst(fqInstanceName, '/'); FQName fqName(splittedFqInstanceName.first); if (!fqName.isValid()) { mErr << "Warning: '" << splittedFqInstanceName.first err() << "Warning: '" << splittedFqInstanceName.first << "' is not a valid FQName." << std::endl; continue; } Loading Loading @@ -321,12 +298,12 @@ void ListCommand::dumpVintf() const { arch = vintf::Arch::ARCH_32_64; break; case lshal::ARCH_UNKNOWN: // fallthrough default: mErr << "Warning: '" << fqName.package() err() << "Warning: '" << fqName.package() << "' doesn't have bitness info, assuming 32+64." << std::endl; arch = vintf::Arch::ARCH_32_64; } } else { mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl; continue; } Loading @@ -334,7 +311,7 @@ void ListCommand::dumpVintf() const { for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) { if (hal->transport() != transport) { if (transport != vintf::Transport::PASSTHROUGH) { mErr << "Fatal: should not reach here. Generated result may be wrong for '" err() << "Fatal: should not reach here. Generated result may be wrong for '" << hal->name << "'." << std::endl; } Loading Loading @@ -365,29 +342,11 @@ void ListCommand::dumpVintf() const { .versions = {version}, .transportArch = {transport, arch}, .interfaces = interfaces})) { mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl; } } }); mOut << vintf::gHalManifestConverter(manifest); } static const std::string &getArchString(Architecture arch) { static const std::string sStr64 = "64"; static const std::string sStr32 = "32"; static const std::string sStrBoth = "32+64"; static const std::string sStrUnknown = ""; switch (arch) { case ARCH64: return sStr64; case ARCH32: return sStr32; case ARCH_BOTH: return sStrBoth; case ARCH_UNKNOWN: // fall through default: return sStrUnknown; } out << vintf::gHalManifestConverter(manifest); } static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) { Loading @@ -402,78 +361,54 @@ static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo: } } void ListCommand::addLine(TextTable *table, const TableEntry &entry) { addLine(table, 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), join(entry.clientPids, " "), join(entry.clientCmdlines, ";")); } void ListCommand::dumpTable() { void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const { if (mNeat) { TextTable textTable; forEachTable([this, &textTable](const Table &table) { for (const auto &entry : table) addLine(&textTable, entry); }); textTable.dump(mOut.buf()); MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable}) .createTextTable().dump(out.buf()); return; } mServicesTable.description = "All binderized services (registered services through hwservicemanager)"; mPassthroughRefTable.description = "All interfaces that getService() has ever return as a passthrough interface;\n" "PIDs / processes shown below might be inaccurate because the process\n" "might have relinquished the interface or might have died.\n" "The Server / Server CMD column can be ignored.\n" "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n" "the library and successfully fetched the passthrough implementation."; mImplementationsTable.description = "All available passthrough implementations (all -impl.so files)"; forEachTable([this](const Table &table) { TextTable textTable; textTable.add(table.description); addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD", "PTR", "Clients", "Clients CMD"); forEachTable([this, &out](const Table &table) { for (const auto &entry : table) { addLine(&textTable, entry); // We're only interested in dumping debug info for already // instantiated services. There's little value in dumping the // debug info for a service we create on the fly, so we only operate // on the "mServicesTable". std::function<std::string(const std::string&)> emitDebugInfo = nullptr; if (mEmitDebugInfo && &table == &mServicesTable) { std::stringstream out; auto pair = splitFirst(entry.interfaceName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, out, emitDebugInfo = [this](const auto& iName) { std::stringstream ss; auto pair = splitFirst(iName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, ss, NullableOStream<std::ostream>(nullptr)); textTable.add(out.str()); return ss.str(); }; } table.createTextTable(mNeat, emitDebugInfo).dump(out.buf()); out << std::endl; }); } // Add empty line after each table textTable.add(); Status ListCommand::dump() { auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable; textTable.dump(mOut.buf()); }); if (mFileOutputPath.empty()) { (*this.*dump)(out()); return OK; } void ListCommand::dump() { if (mVintf) { dumpVintf(); if (!!mFileOutput) { mFileOutput.buf().close(); delete &mFileOutput.buf(); mFileOutput = nullptr; } mOut = std::cout; } else { dumpTable(); std::ofstream fileOutput(mFileOutputPath); if (!fileOutput.is_open()) { err() << "Could not open file '" << mFileOutputPath << "'." << std::endl; return IO_ERROR; } chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL); (*this.*dump)(NullableOStream<std::ostream>(fileOutput)); fileOutput.flush(); fileOutput.close(); return OK; } void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { Loading @@ -486,10 +421,10 @@ void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) { case LIST_DLLIB : table = &mImplementationsTable; break; default: mErr << "Error: Unknown source of entry " << source << std::endl; err() << "Error: Unknown source of entry " << source << std::endl; } if (table) { table->entries.push_back(std::forward<TableEntry>(entry)); table->add(std::forward<TableEntry>(entry)); } } Loading Loading @@ -517,7 +452,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { } }); if (!ret.isOk()) { mErr << "Error: Failed to call list on getPassthroughServiceManager(): " err() << "Error: Failed to call list on getPassthroughServiceManager(): " << ret.description() << std::endl; return DUMP_ALL_LIBS_ERROR; } Loading Loading @@ -547,7 +482,7 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { } }); if (!ret.isOk()) { mErr << "Error: Failed to call debugDump on defaultServiceManager(): " err() << "Error: Failed to call debugDump on defaultServiceManager(): " << ret.description() << std::endl; return DUMP_PASSTHROUGH_ERROR; } Loading @@ -567,7 +502,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { fqInstanceNames = names; }); if (!listRet.isOk()) { mErr << "Error: Failed to list services for " << mode << ": " err() << "Error: Failed to list services for " << mode << ": " << listRet.description() << std::endl; return DUMP_BINDERIZED_ERROR; } Loading @@ -582,7 +517,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { const auto &instanceName = pair.second; auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); if (!getRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager:" << getRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -590,7 +525,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { } sp<IBase> service = getRet; if (service == nullptr) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "cannot be fetched from service manager (null)" << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -603,7 +538,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { } }); if (!debugRet.isOk()) { mErr << "Warning: Skipping \"" << fqInstanceName << "\": " err() << "Warning: Skipping \"" << fqInstanceName << "\": " << "debugging information cannot be retrieved:" << debugRet.description() << std::endl; status |= DUMP_BINDERIZED_ERROR; Loading @@ -613,7 +548,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { for (auto &pair : allPids) { pid_t serverPid = pair.first; if (!getPidInfo(serverPid, &allPids[serverPid])) { mErr << "Warning: no information for PID " << serverPid err() << "Warning: no information for PID " << serverPid << ", are you root?" << std::endl; status |= DUMP_BINDERIZED_ERROR; } Loading Loading @@ -654,7 +589,7 @@ Status ListCommand::fetch() { Status status = OK; auto bManager = mLshal.serviceManager(); if (bManager == nullptr) { mErr << "Failed to get defaultServiceManager()!" << std::endl; err() << "Failed to get defaultServiceManager()!" << std::endl; status |= NO_BINDERIZED_MANAGER; } else { status |= fetchBinderized(bManager); Loading @@ -664,7 +599,7 @@ Status ListCommand::fetch() { auto pManager = mLshal.passthroughManager(); if (pManager == nullptr) { mErr << "Failed to get getPassthroughServiceManager()!" << std::endl; err() << "Failed to get getPassthroughServiceManager()!" << std::endl; status |= NO_PASSTHROUGH_MANAGER; } else { status |= fetchAllLibraries(pManager); Loading Loading @@ -693,6 +628,9 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { { 0, 0, 0, 0 } }; std::vector<TableColumnType> selectedColumns; bool enableCmdlines = false; int optionIndex; int c; // Lshal::parseArgs has set optind to the next option to parse Loading @@ -710,67 +648,52 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) { mSortColumn = TableEntry::sortByServerPid; } else { mErr << "Unrecognized sorting column: " << optarg << std::endl; err() << "Unrecognized sorting column: " << optarg << std::endl; mLshal.usage(command); return USAGE; } break; } case 'v': { if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } } mVintf = true; if (optarg) mFileOutputPath = optarg; break; } case 'i': { mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME); selectedColumns.push_back(TableColumnType::INTERFACE_NAME); break; } case 't': { mSelectedColumns.push_back(TableColumnType::TRANSPORT); selectedColumns.push_back(TableColumnType::TRANSPORT); break; } case 'r': { mSelectedColumns.push_back(TableColumnType::ARCH); selectedColumns.push_back(TableColumnType::ARCH); break; } case 'p': { mSelectedColumns.push_back(TableColumnType::SERVER_PID); selectedColumns.push_back(TableColumnType::SERVER_PID); break; } case 'a': { mSelectedColumns.push_back(TableColumnType::SERVER_ADDR); selectedColumns.push_back(TableColumnType::SERVER_ADDR); break; } case 'c': { mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS); selectedColumns.push_back(TableColumnType::CLIENT_PIDS); break; } case 'e': { mSelectedColumns.push_back(TableColumnType::THREADS); selectedColumns.push_back(TableColumnType::THREADS); break; } case 'm': { mEnableCmdlines = true; enableCmdlines = true; break; } case 'd': { mEmitDebugInfo = true; if (optarg) { mFileOutput = new std::ofstream{optarg}; mOut = mFileOutput; if (!mFileOutput.buf().is_open()) { mErr << "Could not open file '" << optarg << "'." << std::endl; return IO_ERROR; } chown(optarg, AID_SHELL, AID_SHELL); } if (optarg) mFileOutputPath = optarg; break; } case 'n': { Loading @@ -785,21 +708,37 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { } if (optind < arg.argc) { // see non option mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; err() << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; mLshal.usage(command); return USAGE; } if (mNeat && mEmitDebugInfo) { mErr << "Error: --neat should not be used with --debug." << std::endl; err() << "Error: --neat should not be used with --debug." << std::endl; mLshal.usage(command); return USAGE; } if (mSelectedColumns.empty()) { mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, if (selectedColumns.empty()) { selectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}; } if (enableCmdlines) { for (size_t i = 0; i < selectedColumns.size(); ++i) { if (selectedColumns[i] == TableColumnType::SERVER_PID) { selectedColumns[i] = TableColumnType::SERVER_CMD; } if (selectedColumns[i] == TableColumnType::CLIENT_PIDS) { selectedColumns[i] = TableColumnType::CLIENT_CMDS; } } } forEachTable([&selectedColumns] (Table& table) { table.setSelectedColumns(selectedColumns); }); return OK; } Loading @@ -810,7 +749,7 @@ Status ListCommand::main(const std::string &command, const Arg &arg) { } status = fetch(); postprocess(); dump(); status |= dump(); return status; } Loading
cmds/lshal/ListCommand.h +18 −17 Original line number Diff line number Diff line Loading @@ -36,34 +36,38 @@ namespace lshal { class Lshal; 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 }; class ListCommand { public: ListCommand(Lshal &lshal); virtual ~ListCommand() = default; Status main(const std::string &command, const Arg &arg); private: protected: Status parseArgs(const std::string &command, const Arg &arg); Status fetch(); void postprocess(); void dump(); 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); 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; virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const; void dumpTable(); void dumpVintf() const; void dumpTable(const NullableOStream<std::ostream>& out) const; void dumpVintf(const NullableOStream<std::ostream>& out) const; void addLine(TextTable *table, 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; void addLine(TextTable *table, const TableEntry &entry); // Read and return /proc/{pid}/cmdline. virtual std::string parseCmdline(pid_t pid) 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 Loading @@ -72,21 +76,18 @@ private: void forEachTable(const std::function<void(Table &)> &f); void forEachTable(const std::function<void(const Table &)> &f) const; NullableOStream<std::ostream> err() const; NullableOStream<std::ostream> out() const; Lshal &mLshal; Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; NullableOStream<std::ostream> mErr; NullableOStream<std::ostream> mOut; NullableOStream<std::ofstream> mFileOutput = nullptr; std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; std::vector<TableColumnType> mSelectedColumns; // If true, cmdlines will be printed instead of pid. bool mEnableCmdlines = false; // If true, calls IBase::debug(...) on each service. bool mEmitDebugInfo = false; // If true, output in VINTF format. Loading
cmds/lshal/Lshal.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -109,15 +109,15 @@ void Lshal::usage(const std::string &command) const { " Print help message for debug\n"; if (command == "list") { mErr << list; err() << list; return; } if (command == "debug") { mErr << debug; err() << debug; return; } mErr << helpSummary << "\n" << list << "\n" << debug << "\n" << help; err() << helpSummary << "\n" << list << "\n" << debug << "\n" << help; } // A unique_ptr type using a custom deleter function. Loading Loading @@ -206,7 +206,7 @@ Status Lshal::parseArgs(const Arg &arg) { return OK; } mErr << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; err() << arg.argv[0] << ": unrecognized option `" << arg.argv[optind] << "`" << std::endl; usage(); return USAGE; } Loading
cmds/lshal/Lshal.h +3 −2 Original line number Diff line number Diff line Loading @@ -33,13 +33,14 @@ namespace lshal { class Lshal { public: Lshal(); virtual ~Lshal() {} Lshal(std::ostream &out, std::ostream &err, sp<hidl::manager::V1_0::IServiceManager> serviceManager, sp<hidl::manager::V1_0::IServiceManager> passthroughManager); Status main(const Arg &arg); void usage(const std::string &command = "") const; NullableOStream<std::ostream> err() const; NullableOStream<std::ostream> out() const; virtual NullableOStream<std::ostream> err() const; virtual NullableOStream<std::ostream> out() const; const sp<hidl::manager::V1_0::IServiceManager> &serviceManager() const; const sp<hidl::manager::V1_0::IServiceManager> &passthroughManager() const; Loading