diff options
author | 2018-06-25 16:15:56 -0700 | |
---|---|---|
committer | 2018-06-28 14:57:37 -0700 | |
commit | 13ba0a975115910cc535130dead36beb42c1c0b4 (patch) | |
tree | 5a05b7d64e22d48b9c90edbe8958fe621606b74f | |
parent | b72f19ee9f61bdee774aae0ce8c3a0e511d2ae30 (diff) |
lshal: add "Status" column and "Manifest HALs" section.
- Added "Status" column that has following values:
- alive: running hwbinder service
- registered;dead: registered, but service cannot accept calls
- declared: only in VINTF, not in hwservicemanager
- N/A: passthrough HALs
- Added a "Manifest HALs" section that lists all
HALs (hwbinder or passthrough) in device / framework manifest
Test: lshal_test
Bug: 71555570
Change-Id: I202b562ee73bcd49506bb43cc9af27b86f32651c
-rw-r--r-- | cmds/lshal/ListCommand.cpp | 99 | ||||
-rw-r--r-- | cmds/lshal/ListCommand.h | 18 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.cpp | 15 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.h | 11 | ||||
-rw-r--r-- | cmds/lshal/test.cpp | 69 | ||||
-rw-r--r-- | cmds/lshal/utils.h | 2 |
6 files changed, 185 insertions, 29 deletions
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index d497e5ccda..1a412b2db6 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -57,6 +57,19 @@ vintf::SchemaType toSchemaType(Partition p) { return (p == Partition::SYSTEM) ? vintf::SchemaType::FRAMEWORK : vintf::SchemaType::DEVICE; } +Partition toPartition(vintf::SchemaType t) { + switch (t) { + case vintf::SchemaType::FRAMEWORK: return Partition::SYSTEM; + // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. + case vintf::SchemaType::DEVICE: return Partition::VENDOR; + } + return Partition::UNKNOWN; +} + +std::string getPackageAndVersion(const std::string& fqInstance) { + return splitFirst(fqInstance, ':').first; +} + NullableOStream<std::ostream> ListCommand::out() const { return mLshal.out(); } @@ -77,6 +90,8 @@ std::string ListCommand::parseCmdline(pid_t pid) const { } const std::string &ListCommand::getCmdline(pid_t pid) { + static const std::string kEmptyString{}; + if (pid == NO_PID) return kEmptyString; auto pair = mCmdlines.find(pid); if (pair != mCmdlines.end()) { return pair->second; @@ -93,6 +108,7 @@ void ListCommand::removeDeadProcesses(Pids *pids) { } Partition ListCommand::getPartition(pid_t pid) { + if (pid == NO_PID) return Partition::UNKNOWN; auto it = mPartitions.find(pid); if (it != mPartitions.end()) { return it->second; @@ -176,7 +192,7 @@ VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName, FqInstance fqInstance; if (!fqInstance.setTo(fqInstanceName) && // Ignore interface / instance for passthrough libs - !fqInstance.setTo(splitFirst(fqInstanceName, ':').first)) { + !fqInstance.setTo(getPackageAndVersion(fqInstanceName))) { err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl; return VINTF_INFO_EMPTY; } @@ -283,8 +299,8 @@ const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) { return &pair.first->second; } -bool ListCommand::shouldReportHalType(const HalType &type) const { - return (std::find(mListTypes.begin(), mListTypes.end(), type) != mListTypes.end()); +bool ListCommand::shouldFetchHalType(const HalType &type) const { + return (std::find(mFetchTypes.begin(), mFetchTypes.end(), type) != mFetchTypes.end()); } Table* ListCommand::tableForType(HalType type) { @@ -295,6 +311,8 @@ Table* ListCommand::tableForType(HalType type) { return &mPassthroughRefTable; case HalType::PASSTHROUGH_LIBRARIES: return &mImplementationsTable; + case HalType::VINTF_MANIFEST: + return &mManifestHalsTable; default: LOG(FATAL) << "Unknown HAL type " << static_cast<int64_t>(type); return nullptr; @@ -328,7 +346,9 @@ void ListCommand::postprocess() { } } for (TableEntry& entry : table) { - entry.partition = getPartition(entry.serverPid); + if (entry.partition == Partition::UNKNOWN) { + entry.partition = getPartition(entry.serverPid); + } entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch}); } }); @@ -365,6 +385,8 @@ void ListCommand::postprocess() { mImplementationsTable.setDescription( "All available passthrough implementations (all -impl.so files).\n" "These may return subclasses through their respective HIDL_FETCH_I* functions."); + mManifestHalsTable.setDescription( + "All HALs that are in VINTF manifest."); } bool ListCommand::addEntryWithInstance(const TableEntry& entry, @@ -415,7 +437,7 @@ bool ListCommand::addEntryWithInstance(const TableEntry& entry, bool ListCommand::addEntryWithoutInstance(const TableEntry& entry, const vintf::HalManifest* manifest) const { - const auto& packageAndVersion = splitFirst(splitFirst(entry.interfaceName, ':').first, '@'); + const auto& packageAndVersion = splitFirst(getPackageAndVersion(entry.interfaceName), '@'); const auto& package = packageAndVersion.first; vintf::Version version; if (!vintf::parse(packageAndVersion.second, &version)) { @@ -445,6 +467,8 @@ void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const { if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); for (const TableEntry& entry : mPassthroughRefTable) if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); + for (const TableEntry& entry : mManifestHalsTable) + if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName); std::vector<std::string> passthrough; for (const TableEntry& entry : mImplementationsTable) @@ -559,7 +583,7 @@ void ListCommand::putEntry(HalType type, TableEntry &&entry) { } Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { - if (!shouldReportHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; } + if (!shouldFetchHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; } using namespace ::android::hardware; using namespace ::android::hidl::manager::V1_0; @@ -589,7 +613,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { } Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { - if (!shouldReportHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; } + if (!shouldFetchHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; } using namespace ::android::hardware; using namespace ::android::hardware::details; @@ -622,7 +646,7 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) { Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { using vintf::operator<<; - if (!shouldReportHalType(HalType::BINDERIZED_SERVICES)) { return OK; } + if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; } const vintf::Transport mode = vintf::Transport::HWBINDER; hidl_vec<hidl_string> fqInstanceNames; @@ -643,6 +667,7 @@ Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) { TableEntry& entry = allTableEntries[fqInstanceName]; entry.interfaceName = fqInstanceName; entry.transport = mode; + entry.serviceStatus = ServiceStatus::NON_RESPONSIVE; status |= fetchBinderizedEntry(manager, &entry); } @@ -748,6 +773,40 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager, handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description()); } } while (0); + if (status == OK) { + entry->serviceStatus = ServiceStatus::ALIVE; + } + return status; +} + +Status ListCommand::fetchManifestHals() { + if (!shouldFetchHalType(HalType::VINTF_MANIFEST)) { return OK; } + Status status = OK; + + for (auto manifest : {getDeviceManifest(), getFrameworkManifest()}) { + if (manifest == nullptr) { + status |= VINTF_ERROR; + continue; + } + + std::map<std::string, TableEntry> entries; + + manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) { + TableEntry entry{ + .interfaceName = manifestInstance.getFqInstance().string(), + .transport = manifestInstance.transport(), + .arch = manifestInstance.arch(), + // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM. + .partition = toPartition(manifest->type()), + .serviceStatus = ServiceStatus::DECLARED}; + std::string key = entry.interfaceName; + entries.emplace(std::move(key), std::move(entry)); + return true; + }); + + for (auto&& pair : entries) + mManifestHalsTable.add(std::move(pair.second)); + } return status; } @@ -770,9 +829,14 @@ Status ListCommand::fetch() { } else { status |= fetchAllLibraries(pManager); } + status |= fetchManifestHals(); return status; } +void ListCommand::initFetchTypes() { + mFetchTypes.insert(mListTypes.begin(), mListTypes.end()); +} + void ListCommand::registerAllOptions() { int v = mOptions.size(); // A list of acceptable command line options @@ -836,6 +900,14 @@ void ListCommand::registerAllOptions() { " - DC: device compatibility matrix\n" " - FM: framework manifest\n" " - FC: framework compatibility matrix"}); + mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) { + thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS); + return OK; + }, "print service status column. Possible values are:\n" + " - alive: alive and running hwbinder service;\n" + " - registered;dead: registered to hwservicemanager but is not responsive;\n" + " - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n" + " - N/A: no information for passthrough HALs."}); // long options without short alternatives mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { @@ -876,7 +948,9 @@ void ListCommand::registerAllOptions() { {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS}, {"c", HalType::PASSTHROUGH_CLIENTS}, {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES}, - {"l", HalType::PASSTHROUGH_LIBRARIES} + {"l", HalType::PASSTHROUGH_LIBRARIES}, + {"vintf", HalType::VINTF_MANIFEST}, + {"v", HalType::VINTF_MANIFEST}, }; std::vector<std::string> halTypesArgs = split(std::string(arg), ','); @@ -900,9 +974,9 @@ void ListCommand::registerAllOptions() { if (thiz->mListTypes.empty()) { return USAGE; } return OK; - }, "comma-separated list of one or more HAL types.\nThe output is restricted to the selected " - "association(s). Valid options\nare: (b|binderized), (c|passthrough_clients), and (l|" - "passthrough_libs).\nBy default, lists all available HALs."}); + }, "comma-separated list of one or more sections.\nThe output is restricted to the selected " + "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|" + "passthrough_libs), and (v|vintf).\nDefault is `bcl`."}); } // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining @@ -1019,6 +1093,7 @@ Status ListCommand::parseArgs(const Arg &arg) { mListTypes = {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS, HalType::PASSTHROUGH_LIBRARIES}; } + initFetchTypes(); forEachTable([this] (Table& table) { table.setSelectedColumns(this->mSelectedColumns); diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index b10901c01e..f4d3fdf67c 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -50,7 +50,8 @@ struct PidInfo { enum class HalType { BINDERIZED_SERVICES = 0, PASSTHROUGH_CLIENTS, - PASSTHROUGH_LIBRARIES + PASSTHROUGH_LIBRARIES, + VINTF_MANIFEST, }; class ListCommand : public Command { @@ -97,6 +98,7 @@ protected: 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 fetchManifestHals(); Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager, TableEntry *entry); @@ -146,12 +148,15 @@ protected: bool addEntryWithInstance(const TableEntry &entry, vintf::HalManifest *manifest) const; bool addEntryWithoutInstance(const TableEntry &entry, const vintf::HalManifest *manifest) const; - // Helper function. Whether to list entries corresponding to a given HAL type. - bool shouldReportHalType(const HalType &type) const; + // Helper function. Whether to fetch entries corresponding to a given HAL type. + bool shouldFetchHalType(const HalType &type) const; + + void initFetchTypes(); Table mServicesTable{}; Table mPassthroughRefTable{}; Table mImplementationsTable{}; + Table mManifestHalsTable{}; std::string mFileOutputPath; TableEntryCompare mSortColumn = nullptr; @@ -165,9 +170,10 @@ protected: // If true, explanatory text are not emitted. bool mNeat = false; - // Type(s) of HAL associations to list. By default, report all. - std::vector<HalType> mListTypes{HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS, - HalType::PASSTHROUGH_LIBRARIES}; + // Type(s) of HAL associations to list. + std::vector<HalType> mListTypes{}; + // Type(s) of HAL associations to fetch. + std::set<HalType> mFetchTypes{}; // If an entry does not exist, need to ask /proc/{pid}/cmdline to get it. // If an entry exist but is an empty string, process might have died. diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index 4ad3e929b1..8e21975efa 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp @@ -62,6 +62,7 @@ static std::string getTitle(TableColumnType type) { case TableColumnType::RELEASED: return "R"; case TableColumnType::HASH: return "Hash"; case TableColumnType::VINTF: return "VINTF"; + case TableColumnType::SERVICE_STATUS: return "Status"; default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -94,6 +95,8 @@ std::string TableEntry::getField(TableColumnType type) const { return hash; case TableColumnType::VINTF: return getVintfInfo(); + case TableColumnType::SERVICE_STATUS: + return lshal::to_string(serviceStatus); default: LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type); return ""; @@ -129,6 +132,18 @@ std::string TableEntry::getVintfInfo() const { return joined.empty() ? "X" : joined; } +std::string to_string(ServiceStatus s) { + switch (s) { + case ServiceStatus::ALIVE: return "alive"; + case ServiceStatus::NON_RESPONSIVE: return "non-responsive"; + case ServiceStatus::DECLARED: return "declared"; + case ServiceStatus::UNKNOWN: return "N/A"; + } + + LOG(FATAL) << __func__ << "Should not reach here." << static_cast<int>(s); + return ""; +} + TextTable Table::createTextTable(bool neat, const std::function<std::string(const std::string&)>& emitDebugInfo) const { diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index d68417bfca..7294b0a134 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -48,6 +48,7 @@ enum class TableColumnType : unsigned int { RELEASED, HASH, VINTF, + SERVICE_STATUS, }; enum : unsigned int { @@ -64,6 +65,14 @@ enum { NO_PTR = 0 }; +enum class ServiceStatus { + UNKNOWN, // For passthrough + ALIVE, + NON_RESPONSIVE, // registered but not respond to calls + DECLARED, // in VINTF manifest +}; +std::string to_string(ServiceStatus s); + struct TableEntry { std::string interfaceName{}; vintf::Transport transport{vintf::Transport::EMPTY}; @@ -79,6 +88,8 @@ struct TableEntry { std::string hash{}; Partition partition{Partition::UNKNOWN}; VintfInfo vintfInfo{VINTF_INFO_EMPTY}; + // true iff hwbinder and service started + ServiceStatus serviceStatus{ServiceStatus::UNKNOWN}; static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { return a.interfaceName < b.interfaceName; diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 096b67d24e..b05946bcc9 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -327,7 +327,7 @@ private: class ListTest : public ::testing::Test { public: - void SetUp() override { + virtual void SetUp() override { initMockServiceManager(); lshal = std::make_unique<Lshal>(out, err, serviceManager, passthruManager); initMockList(); @@ -351,13 +351,13 @@ public: ON_CALL(*mockList, getPartition(_)).WillByDefault(Return(Partition::VENDOR)); ON_CALL(*mockList, getDeviceManifest()) - .WillByDefault(Return(VintfObject::GetDeviceHalManifest())); + .WillByDefault(Return(std::make_shared<HalManifest>())); ON_CALL(*mockList, getDeviceMatrix()) - .WillByDefault(Return(VintfObject::GetDeviceCompatibilityMatrix())); + .WillByDefault(Return(std::make_shared<CompatibilityMatrix>())); ON_CALL(*mockList, getFrameworkManifest()) - .WillByDefault(Return(VintfObject::GetFrameworkHalManifest())); + .WillByDefault(Return(std::make_shared<HalManifest>())); ON_CALL(*mockList, getFrameworkMatrix()) - .WillByDefault(Return(VintfObject::GetFrameworkCompatibilityMatrix())); + .WillByDefault(Return(std::make_shared<CompatibilityMatrix>())); } void initMockServiceManager() { @@ -411,16 +411,22 @@ TEST_F(ListTest, GetPidInfoCached) { } TEST_F(ListTest, Fetch) { - EXPECT_EQ(0u, mockList->fetch()); + optind = 1; // mimic Lshal::parseArg() + ASSERT_EQ(0u, mockList->parseArgs(createArg({"lshal"}))); + ASSERT_EQ(0u, mockList->fetch()); vintf::TransportArch hwbinder{Transport::HWBINDER, Arch::ARCH_64}; vintf::TransportArch passthrough{Transport::PASSTHROUGH, Arch::ARCH_32}; std::array<vintf::TransportArch, 6> transportArchs{{hwbinder, hwbinder, passthrough, passthrough, passthrough, passthrough}}; - int id = 1; + int i = 0; mockList->forEachTable([&](const Table& table) { - ASSERT_EQ(2u, table.size()); for (const auto& entry : table) { - auto transport = transportArchs.at(id - 1).transport; + if (i >= transportArchs.size()) { + break; + } + + int id = i + 1; + auto transport = transportArchs.at(i).transport; TableEntry expected{ .interfaceName = getFqInstanceName(id), .transport = transport, @@ -433,14 +439,16 @@ TEST_F(ListTest, Fetch) { .serverObjectAddress = transport == Transport::HWBINDER ? getPtr(id) : NO_PTR, .clientPids = getClients(id), .clientCmdlines = {}, - .arch = transportArchs.at(id - 1).arch, + .arch = transportArchs.at(i).arch, }; EXPECT_EQ(expected, entry) << expected.to_string() << " vs. " << entry.to_string(); - ++id; + ++i; } }); + EXPECT_EQ(transportArchs.size(), i) << "Not all entries are tested."; + } TEST_F(ListTest, DumpVintf) { @@ -758,6 +766,45 @@ TEST_F(ListTest, Vintf) { EXPECT_EQ("", err.str()); } +class ListVintfTest : public ListTest { +public: + virtual void SetUp() override { + ListTest::SetUp(); + const std::string mockManifestXml = + "<manifest version=\"1.0\" type=\"device\">\n" + " <hal format=\"hidl\">\n" + " <name>a.h.foo1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IFoo/1</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.bar1</name>\n" + " <transport>hwbinder</transport>\n" + " <fqname>@1.0::IBar/1</fqname>\n" + " </hal>\n" + " <hal format=\"hidl\">\n" + " <name>a.h.bar2</name>\n" + " <transport arch=\"32+64\">passthrough</transport>\n" + " <fqname>@2.0::IBar/2</fqname>\n" + " </hal>\n" + "</manifest>"; + auto manifest = std::make_shared<HalManifest>(); + EXPECT_TRUE(gHalManifestConverter(manifest.get(), mockManifestXml)); + EXPECT_CALL(*mockList, getDeviceManifest()) + .Times(AnyNumber()) + .WillRepeatedly(Return(manifest)); + } +}; + +TEST_F(ListVintfTest, ManifestHals) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=v", "--neat"}))); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar1@1.0::IBar/1 declared hwbinder ?")); + EXPECT_THAT(out.str(), HasSubstr("a.h.bar2@2.0::IBar/2 declared passthrough 32+64")); + EXPECT_THAT(out.str(), HasSubstr("a.h.foo1@1.0::IFoo/1 declared hwbinder ?")); + EXPECT_EQ("", err.str()); +} + class HelpTest : public ::testing::Test { public: void SetUp() override { diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h index c09e8b1666..240155e4d0 100644 --- a/cmds/lshal/utils.h +++ b/cmds/lshal/utils.h @@ -46,6 +46,8 @@ enum : unsigned int { TRANSACTION_ERROR = 1 << 8, // No transaction error, but return value is unexpected. BAD_IMPL = 1 << 9, + // Cannot fetch VINTF data. + VINTF_ERROR = 1 << 10, }; using Status = unsigned int; |