Merge "Filter lshal-reported HALs using CL arguments"
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index 4249165..c99b863 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -18,6 +18,7 @@
#include <getopt.h>
+#include <algorithm>
#include <fstream>
#include <functional>
#include <iomanip>
@@ -27,6 +28,7 @@
#include <sstream>
#include <android-base/file.h>
+#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl-hash/Hash.h>
@@ -220,16 +222,37 @@
return &pair.first->second;
}
-// Must process hwbinder services first, then passthrough services.
+bool ListCommand::shouldReportHalType(const HalType &type) const {
+ return (std::find(mListTypes.begin(), mListTypes.end(), type) != mListTypes.end());
+}
+
void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
- f(mServicesTable);
- f(mPassthroughRefTable);
- f(mImplementationsTable);
+ for (const auto& type : mListTypes) {
+ switch (type) {
+ case HalType::BINDERIZED_SERVICES:
+ f(mServicesTable); break;
+ case HalType::PASSTHROUGH_CLIENTS:
+ f(mPassthroughRefTable); break;
+ case HalType::PASSTHROUGH_LIBRARIES:
+ f(mImplementationsTable); break;
+ default:
+ LOG(FATAL) << __func__ << "Unknown HAL type.";
+ }
+ }
}
void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
- f(mServicesTable);
- f(mPassthroughRefTable);
- f(mImplementationsTable);
+ for (const auto& type : mListTypes) {
+ switch (type) {
+ case HalType::BINDERIZED_SERVICES:
+ f(mServicesTable); break;
+ case HalType::PASSTHROUGH_CLIENTS:
+ f(mPassthroughRefTable); break;
+ case HalType::PASSTHROUGH_LIBRARIES:
+ f(mImplementationsTable); break;
+ default:
+ LOG(FATAL) << __func__ << "Unknown HAL type.";
+ }
+ }
}
void ListCommand::postprocess() {
@@ -498,6 +521,8 @@
}
Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
+ if (!shouldReportHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; }
+
using namespace ::android::hardware;
using namespace ::android::hidl::manager::V1_0;
using namespace ::android::hidl::base::V1_0;
@@ -526,6 +551,8 @@
}
Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
+ if (!shouldReportHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; }
+
using namespace ::android::hardware;
using namespace ::android::hardware::details;
using namespace ::android::hidl::manager::V1_0;
@@ -555,8 +582,9 @@
}
Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
- const std::string mode = "hwbinder";
+ if (!shouldReportHalType(HalType::BINDERIZED_SERVICES)) { return OK; }
+ const std::string mode = "hwbinder";
hidl_vec<hidl_string> fqInstanceNames;
// copying out for timeoutIPC
auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
@@ -790,6 +818,42 @@
thiz->mNeat = true;
return OK;
}, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
+ mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
+ if (!arg) { return USAGE; }
+
+ static const std::map<std::string, HalType> kHalTypeMap {
+ {"binderized", HalType::BINDERIZED_SERVICES},
+ {"b", HalType::BINDERIZED_SERVICES},
+ {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
+ {"c", HalType::PASSTHROUGH_CLIENTS},
+ {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
+ {"l", HalType::PASSTHROUGH_LIBRARIES}
+ };
+
+ std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
+ for (const auto& halTypeArg : halTypesArgs) {
+ if (halTypeArg.empty()) continue;
+
+ const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
+ if (halTypeIter == kHalTypeMap.end()) {
+
+ thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
+ return USAGE;
+ }
+
+ // Append unique (non-repeated) HAL types to the reporting list
+ HalType halType = halTypeIter->second;
+ if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
+ thiz->mListTypes.end()) {
+ thiz->mListTypes.push_back(halType);
+ }
+ }
+
+ 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."});
}
// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
@@ -828,6 +892,7 @@
}
Status ListCommand::parseArgs(const Arg &arg) {
+ mListTypes.clear();
if (mOptions.empty()) {
registerAllOptions();
@@ -900,6 +965,12 @@
}
}
+ // By default, list all HAL types
+ if (mListTypes.empty()) {
+ mListTypes = {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS,
+ HalType::PASSTHROUGH_LIBRARIES};
+ }
+
forEachTable([this] (Table& table) {
table.setSelectedColumns(this->mSelectedColumns);
});
@@ -918,22 +989,6 @@
return status;
}
-static std::vector<std::string> splitString(const std::string &s, char c) {
- std::vector<std::string> components;
-
- size_t startPos = 0;
- size_t matchPos;
- while ((matchPos = s.find(c, startPos)) != std::string::npos) {
- components.push_back(s.substr(startPos, matchPos - startPos));
- startPos = matchPos + 1;
- }
-
- if (startPos <= s.length()) {
- components.push_back(s.substr(startPos));
- }
- return components;
-}
-
const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
static const std::string empty{};
static const std::string optional{"[=<arg>]"};
@@ -969,7 +1024,7 @@
if (!e.longOption.empty())
err() << "--" << e.longOption << e.getHelpMessageForArgument();
err() << ": ";
- std::vector<std::string> lines = splitString(e.help, '\n');
+ std::vector<std::string> lines = split(e.help, '\n');
for (const auto& line : lines) {
if (&line != &lines.front())
err() << " ";
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index 88faac1..c35561d 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -46,6 +46,12 @@
uint32_t threadCount; // number of threads total
};
+enum class HalType {
+ BINDERIZED_SERVICES = 0,
+ PASSTHROUGH_CLIENTS,
+ PASSTHROUGH_LIBRARIES
+};
+
class ListCommand : public Command {
public:
ListCommand(Lshal &lshal) : Command(lshal) {}
@@ -128,6 +134,9 @@
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;
+
Table mServicesTable{};
Table mPassthroughRefTable{};
Table mImplementationsTable{};
@@ -144,6 +153,10 @@
// 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};
+
// 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.
// If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index f23095e..3fc957b 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -567,6 +567,90 @@
EXPECT_EQ("", err.str());
}
+TEST_F(ListTest, DumpSingleHalType) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=binderized"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpReorderedHalTypes) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n"
+ "\n"
+ "[fake description 2]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 1 0000000000002711 2 4\n"
+ "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 2 0000000000002712 3 5\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=passthrough_clients",
+ "--types=passthrough_libs", "--types=binderized"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpAbbreviatedHalTypes) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, DumpEmptyAndDuplicateHalTypes) {
+ const std::string expected =
+ "[fake description 0]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A N/A 4 6\n"
+ "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A N/A 5 7\n"
+ "\n"
+ "[fake description 1]\n"
+ "Interface Transport Arch Thread Use Server PTR Clients\n"
+ "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A N/A 6 8\n"
+ "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A N/A 7 9\n"
+ "\n";
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,l,,,l,l,c,",
+ "--types=passthrough_libs,passthrough_clients"})));
+ EXPECT_EQ(expected, out.str());
+ EXPECT_EQ("", err.str());
+}
+
+TEST_F(ListTest, UnknownHalType) {
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"})));
+ EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a"));
+}
+
class HelpTest : public ::testing::Test {
public:
void SetUp() override {