diff options
| author | 2017-09-15 21:47:28 +0000 | |
|---|---|---|
| committer | 2017-09-15 21:47:28 +0000 | |
| commit | 1776f7562e612f85a861a15af6572ec1927ec705 (patch) | |
| tree | 2301decead629b6b8d7963c24109db024eeb8153 | |
| parent | ffd8c0c8bd9c9b9ca24622550ae9280e08525266 (diff) | |
| parent | 0cc634548c34818ce9c2ad39f3a4a087c54814c0 (diff) | |
Merge changes from topic "lshal_released" am: 136c4bc7ab am: 3985d37c0d
am: 0cc634548c
Change-Id: Id981ebb3ec61209a4e4fe264ef6b1a193ebc0836
| -rw-r--r-- | cmds/lshal/Android.bp | 1 | ||||
| -rw-r--r-- | cmds/lshal/ListCommand.cpp | 206 | ||||
| -rw-r--r-- | cmds/lshal/ListCommand.h | 12 | ||||
| -rw-r--r-- | cmds/lshal/TableEntry.cpp | 21 | ||||
| -rw-r--r-- | cmds/lshal/TableEntry.h | 36 | ||||
| -rw-r--r-- | cmds/lshal/test.cpp | 144 | ||||
| -rw-r--r-- | cmds/lshal/utils.h | 2 |
7 files changed, 293 insertions, 129 deletions
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 5a87505113..6cbe7e24fa 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -20,6 +20,7 @@ cc_library_shared { "libutils", "libhidlbase", "libhidltransport", + "libhidl-gen-hash", "libhidl-gen-utils", "libvintf", ], diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 8b59fb867d..73996925b1 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -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> @@ -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 { @@ -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)); @@ -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); @@ -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) { @@ -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) }); @@ -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; @@ -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, '/'); - 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; - } - 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(); - } + // 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()) { + handleError(TRANSACTION_ERROR, + "cannot be fetched from service manager:" + getRet.description()); + return status; + } + sp<IBase> service = getRet; + if (service == nullptr) { + 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 } - } - 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; + 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; } - } - 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; + } while (0); + + // 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; + } + } + }); + if (!ifaceChainRet.isOk()) { + handleError(TRANSACTION_ERROR, + "interfaceChain fails: " + ifaceChainRet.description()); + break; // skip getHashChain } - 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), + 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; + } + + 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; } @@ -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; @@ -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; @@ -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}; } @@ -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; diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index 5bc834c320..7e252fc167 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -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; @@ -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; diff --git a/cmds/lshal/TableEntry.cpp b/cmds/lshal/TableEntry.cpp index cbcf979f90..e8792a4307 100644 --- a/cmds/lshal/TableEntry.cpp +++ b/cmds/lshal/TableEntry.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "lshal" #include <android-base/logging.h> +#include <hidl-hash/Hash.h> + #include "TableEntry.h" #include "TextTable.h" @@ -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 ""; } } @@ -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 { diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 7a3b22ea67..69206cc51b 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -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; @@ -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; @@ -129,11 +140,6 @@ private: std::vector<const Table*> mTables; }; -enum { - NO_PID = -1, - NO_PTR = 0 -}; - } // namespace lshal } // namespace android diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 06b68199cd..9220fc09fb 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -39,6 +39,7 @@ using ::android::hidl::base::V1_0::DebugInfo; using ::android::hidl::base::V1_0::IBase; using ::android::hidl::manager::V1_0::IServiceManager; using ::android::hidl::manager::V1_0::IServiceNotification; +using ::android::hardware::hidl_array; using ::android::hardware::hidl_death_recipient; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; @@ -46,6 +47,8 @@ using ::android::hardware::hidl_vec; using InstanceDebugInfo = IServiceManager::InstanceDebugInfo; +using hidl_hash = hidl_array<uint8_t, 32>; + namespace android { namespace hardware { namespace tests { @@ -185,6 +188,9 @@ public: Status parseArgs(const Arg& arg) { return ListCommand::parseArgs(arg); } Status main(const Arg& arg) { return ListCommand::main(arg); } + void forEachTable(const std::function<void(Table &)> &f) { + return ListCommand::forEachTable(f); + } void forEachTable(const std::function<void(const Table &)> &f) const { return ListCommand::forEachTable(f); } @@ -192,7 +198,12 @@ public: void dumpVintf(const NullableOStream<std::ostream>& out) { return ListCommand::dumpVintf(out); } + void internalPostprocess() { ListCommand::postprocess(); } + const PidInfo* getPidInfoCached(pid_t serverPid) { + return ListCommand::getPidInfoCached(serverPid); + } + MOCK_METHOD0(postprocess, void()); MOCK_CONST_METHOD2(getPidInfo, bool(pid_t, PidInfo*)); MOCK_CONST_METHOD1(parseCmdline, std::string(pid_t)); }; @@ -210,16 +221,6 @@ public: std::stringstream output; }; -TEST_F(ListParseArgsTest, Default) { - // default args - EXPECT_EQ(0u, mockList->parseArgs(createArg({}))); - mockList->forEachTable([](const Table& table) { - EXPECT_EQ(SelectedColumns({TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, - TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS}), - table.getSelectedColumns()); - }); -} - TEST_F(ListParseArgsTest, Args) { EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-p", "-i", "-a", "-c"}))); mockList->forEachTable([](const Table& table) { @@ -232,9 +233,14 @@ TEST_F(ListParseArgsTest, Args) { TEST_F(ListParseArgsTest, Cmds) { EXPECT_EQ(0u, mockList->parseArgs(createArg({"lshal", "-m"}))); mockList->forEachTable([](const Table& table) { - EXPECT_EQ(SelectedColumns({TableColumnType::INTERFACE_NAME, TableColumnType::THREADS, - TableColumnType::SERVER_CMD, TableColumnType::CLIENT_CMDS}), - table.getSelectedColumns()); + EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::SERVER_PID))) + << "should not print server PID with -m"; + EXPECT_THAT(table.getSelectedColumns(), Not(Contains(TableColumnType::CLIENT_PIDS))) + << "should not print client PIDs with -m"; + EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::SERVER_CMD)) + << "should print server cmd with -m"; + EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS)) + << "should print client cmds with -m"; }); } @@ -274,17 +280,34 @@ static std::string getCmdlineFromId(pid_t serverId) { if (serverId == NO_PID) return ""; return "command_line_" + std::to_string(serverId); } +static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; } +static hidl_hash getHashFromId(pid_t serverId) { + hidl_hash hash; + bool isReleased = getIsReleasedFromId(serverId); + for (size_t i = 0; i < hash.size(); ++i) { + hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u; + } + return hash; +} // Fake service returned by mocked IServiceManager::get. class TestService : public IBase { public: - TestService(DebugInfo&& info) : mInfo(std::move(info)) {} + TestService(pid_t id) : mId(id) {} hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override { - cb(mInfo); + cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT }); + return hardware::Void(); + } + hardware::Return<void> interfaceChain(interfaceChain_cb cb) override { + cb({getInterfaceName(mId), IBase::descriptor}); + return hardware::Void(); + } + hardware::Return<void> getHashChain(getHashChain_cb cb) override { + cb({getHashFromId(mId), getHashFromId(0xff)}); return hardware::Void(); } private: - DebugInfo mInfo; + pid_t mId; }; class ListTest : public ::testing::Test { @@ -303,6 +326,13 @@ public: return true; })); ON_CALL(*mockList, parseCmdline(_)).WillByDefault(Invoke(&getCmdlineFromId)); + ON_CALL(*mockList, postprocess()).WillByDefault(Invoke([&]() { + mockList->internalPostprocess(); + size_t i = 0; + mockList->forEachTable([&](Table& table) { + table.setDescription("[fake description " + std::to_string(i++) + "]"); + }); + })); } void initMockServiceManager() { @@ -318,7 +348,7 @@ public: ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( [&](const hidl_string&, const hidl_string& instance) { int id = getIdFromInstanceName(instance); - return sp<IBase>(new TestService({ id /* pid */, getPtr(id), A::IS_64BIT })); + return sp<IBase>(new TestService(id)); })); ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke( @@ -348,6 +378,13 @@ public: sp<MockServiceManager> passthruManager; }; +TEST_F(ListTest, GetPidInfoCached) { + EXPECT_CALL(*mockList, getPidInfo(5, _)).Times(1); + + EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); + EXPECT_NE(nullptr, mockList->getPidInfoCached(5)); +} + TEST_F(ListTest, Fetch) { EXPECT_EQ(0u, mockList->fetch()); std::array<std::string, 6> transports{{"hwbinder", "hwbinder", "passthrough", @@ -456,24 +493,68 @@ TEST_F(ListTest, DumpVintf) { << vintf::gHalManifestConverter.lastError(); } +// test default columns +TEST_F(ListTest, DumpDefault) { + const std::string expected = + "[fake description 0]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo1@1.0::IFoo/1 11/21 1 2 4\n" + "Y a.h.foo2@2.0::IFoo/2 12/22 2 3 5\n" + "\n" + "[fake description 1]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo3@3.0::IFoo/3 N/A N/A 4 6\n" + " a.h.foo4@4.0::IFoo/4 N/A N/A 5 7\n" + "\n" + "[fake description 2]\n" + "R Interface Thread Use Server Clients\n" + " a.h.foo5@5.0::IFoo/5 N/A N/A 6 8\n" + " a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, DumpHash) { + const std::string expected = + "[fake description 0]\n" + "Interface R Hash\n" + "a.h.foo1@1.0::IFoo/1 0000000000000000000000000000000000000000000000000000000000000000\n" + "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n" + "\n" + "[fake description 1]\n" + "Interface R Hash\n" + "a.h.foo3@3.0::IFoo/3 \n" + "a.h.foo4@4.0::IFoo/4 \n" + "\n" + "[fake description 2]\n" + "Interface R Hash\n" + "a.h.foo5@5.0::IFoo/5 \n" + "a.h.foo6@6.0::IFoo/6 \n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + TEST_F(ListTest, Dump) { const std::string expected = - "All binderized services (registered services through hwservicemanager)\n" + "[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" - "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.\n" + "[fake description 1]\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" - "All available passthrough implementations (all -impl.so files)\n" + "[fake description 2]\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" @@ -487,22 +568,17 @@ TEST_F(ListTest, Dump) { TEST_F(ListTest, DumpCmdline) { const std::string expected = - "All binderized services (registered services through hwservicemanager)\n" + "[fake description 0]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo1@1.0::IFoo/1 hwbinder 64 11/21 command_line_1 0000000000002711 command_line_2;command_line_4\n" "a.h.foo2@2.0::IFoo/2 hwbinder 64 12/22 command_line_2 0000000000002712 command_line_3;command_line_5\n" "\n" - "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.\n" + "[fake description 1]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo3@3.0::IFoo/3 passthrough 32 N/A N/A command_line_4;command_line_6\n" "a.h.foo4@4.0::IFoo/4 passthrough 32 N/A N/A command_line_5;command_line_7\n" "\n" - "All available passthrough implementations (all -impl.so files)\n" + "[fake description 2]\n" "Interface Transport Arch Thread Use Server CMD PTR Clients CMD\n" "a.h.foo5@5.0::IFoo/5 passthrough 32 N/A N/A command_line_6;command_line_8\n" "a.h.foo6@6.0::IFoo/6 passthrough 32 N/A N/A command_line_7;command_line_9\n" @@ -524,7 +600,7 @@ TEST_F(ListTest, DumpNeat) { "a.h.foo6@6.0::IFoo/6 N/A N/A 7 9\n"; optind = 1; // mimic Lshal::parseArg() - EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--neat"}))); + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iepc", "--neat"}))); EXPECT_EQ(expected, out.str()); EXPECT_EQ("", err.str()); } diff --git a/cmds/lshal/utils.h b/cmds/lshal/utils.h index 7eca14e0f7..c09e8b1666 100644 --- a/cmds/lshal/utils.h +++ b/cmds/lshal/utils.h @@ -44,6 +44,8 @@ enum : unsigned int { NO_INTERFACE = 1 << 7, // Transaction error from hwbinder transactions TRANSACTION_ERROR = 1 << 8, + // No transaction error, but return value is unexpected. + BAD_IMPL = 1 << 9, }; using Status = unsigned int; |