Merge changes from topic "lshal_lazy_hals"

* changes:
  lshal: add --types=lazy|z
  lshal: add "Status" column and "Manifest HALs" section.
  lshal: --types works with --neat
  lshal: add ListCommand::tableForType
  lshal: Remove obsolete TableEntrySource type.
  lshal: test: make mocks more robust
  lshal: fix help message
This commit is contained in:
Yifan Hong 2018-06-29 17:39:31 +00:00 committed by Gerrit Code Review
commit e3a52f01e4
6 changed files with 323 additions and 81 deletions

View file

@ -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,36 +299,39 @@ 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) {
switch (type) {
case HalType::BINDERIZED_SERVICES:
return &mServicesTable;
case HalType::PASSTHROUGH_CLIENTS:
return &mPassthroughRefTable;
case HalType::PASSTHROUGH_LIBRARIES:
return &mImplementationsTable;
case HalType::VINTF_MANIFEST:
return &mManifestHalsTable;
case HalType::LAZY_HALS:
return &mLazyHalsTable;
default:
LOG(FATAL) << "Unknown HAL type " << static_cast<int64_t>(type);
return nullptr;
}
}
const Table* ListCommand::tableForType(HalType type) const {
return const_cast<ListCommand*>(this)->tableForType(type);
}
void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
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.";
}
f(*tableForType(type));
}
}
void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
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.";
}
f(*tableForType(type));
}
}
@ -329,7 +348,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});
}
});
@ -366,6 +387,12 @@ 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.");
mLazyHalsTable.setDescription(
"All HALs that are declared in VINTF manifest:\n"
" - as hwbinder HALs but are not registered to hwservicemanager, and\n"
" - as hwbinder/passthrough HALs with no implementation.");
}
bool ListCommand::addEntryWithInstance(const TableEntry& entry,
@ -416,7 +443,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)) {
@ -446,6 +473,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)
@ -503,8 +532,11 @@ static vintf::Arch fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::
void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
if (mNeat) {
MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
.createTextTable().dump(out.buf());
std::vector<const Table*> tables;
forEachTable([&tables](const Table &table) {
tables.push_back(&table);
});
MergedTable(std::move(tables)).createTextTable().dump(out.buf());
return;
}
@ -552,25 +584,12 @@ Status ListCommand::dump() {
return OK;
}
void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
Table *table = nullptr;
switch (source) {
case HWSERVICEMANAGER_LIST :
table = &mServicesTable; break;
case PTSERVICEMANAGER_REG_CLIENT :
table = &mPassthroughRefTable; break;
case LIST_DLLIB :
table = &mImplementationsTable; break;
default:
err() << "Error: Unknown source of entry " << source << std::endl;
}
if (table) {
table->add(std::forward<TableEntry>(entry));
}
void ListCommand::putEntry(HalType type, TableEntry &&entry) {
tableForType(type)->add(std::forward<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;
@ -588,7 +607,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
}).first->second.arch |= fromBaseArchitecture(info.arch);
}
for (auto &&pair : entries) {
putEntry(LIST_DLLIB, std::move(pair.second));
putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
}
});
if (!ret.isOk()) {
@ -600,7 +619,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;
@ -611,7 +630,7 @@ Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
if (info.clientPids.size() <= 0) {
continue;
}
putEntry(PTSERVICEMANAGER_REG_CLIENT, {
putEntry(HalType::PASSTHROUGH_CLIENTS, {
.interfaceName =
std::string{info.interfaceName.c_str()} + "/" +
std::string{info.instanceName.c_str()},
@ -633,7 +652,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;
@ -654,12 +673,13 @@ 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);
}
for (auto& pair : allTableEntries) {
putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second));
putEntry(HalType::BINDERIZED_SERVICES, std::move(pair.second));
}
return status;
}
@ -759,9 +779,100 @@ 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;
}
Status ListCommand::fetchLazyHals() {
using vintf::operator<<;
if (!shouldFetchHalType(HalType::LAZY_HALS)) { return OK; }
Status status = OK;
for (const TableEntry& manifestEntry : mManifestHalsTable) {
if (manifestEntry.transport == vintf::Transport::HWBINDER) {
if (!hasHwbinderEntry(manifestEntry)) {
mLazyHalsTable.add(TableEntry(manifestEntry));
}
continue;
}
if (manifestEntry.transport == vintf::Transport::PASSTHROUGH) {
if (!hasPassthroughEntry(manifestEntry)) {
mLazyHalsTable.add(TableEntry(manifestEntry));
}
continue;
}
err() << "Warning: unrecognized transport in VINTF manifest: "
<< manifestEntry.transport;
status |= VINTF_ERROR;
}
return status;
}
bool ListCommand::hasHwbinderEntry(const TableEntry& entry) const {
for (const TableEntry& existing : mServicesTable) {
if (existing.interfaceName == entry.interfaceName) {
return true;
}
}
return false;
}
bool ListCommand::hasPassthroughEntry(const TableEntry& entry) const {
FqInstance entryFqInstance;
if (!entryFqInstance.setTo(entry.interfaceName)) {
return false; // cannot parse, so add it anyway.
}
for (const TableEntry& existing : mImplementationsTable) {
FqInstance existingFqInstance;
if (!existingFqInstance.setTo(getPackageAndVersion(existing.interfaceName))) {
continue;
}
// For example, manifest may say graphics.mapper@2.1 but passthroughServiceManager
// can only list graphics.mapper@2.0.
if (entryFqInstance.getPackage() == existingFqInstance.getPackage() &&
vintf::Version{entryFqInstance.getVersion()}
.minorAtLeast(vintf::Version{existingFqInstance.getVersion()})) {
return true;
}
}
return false;
}
Status ListCommand::fetch() {
Status status = OK;
auto bManager = mLshal.serviceManager();
@ -781,9 +892,27 @@ Status ListCommand::fetch() {
} else {
status |= fetchAllLibraries(pManager);
}
status |= fetchManifestHals();
status |= fetchLazyHals();
return status;
}
void ListCommand::initFetchTypes() {
// TODO: refactor to do polymorphism on each table (so that dependency graph is not hardcoded).
static const std::map<HalType, std::set<HalType>> kDependencyGraph{
{HalType::LAZY_HALS, {HalType::BINDERIZED_SERVICES,
HalType::PASSTHROUGH_LIBRARIES,
HalType::VINTF_MANIFEST}},
};
mFetchTypes.insert(mListTypes.begin(), mListTypes.end());
for (HalType listType : mListTypes) {
auto it = kDependencyGraph.find(listType);
if (it != kDependencyGraph.end()) {
mFetchTypes.insert(it->second.begin(), it->second.end());
}
}
}
void ListCommand::registerAllOptions() {
int v = mOptions.size();
// A list of acceptable command line options
@ -847,6 +976,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) {
@ -887,7 +1024,11 @@ 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},
{"lazy", HalType::LAZY_HALS},
{"z", HalType::LAZY_HALS},
};
std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
@ -911,9 +1052,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
@ -1030,6 +1171,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);
@ -1068,7 +1210,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 -riepc`)" << std::endl
<< " List all hals with default ordering and columns (`lshal list -liepc`)" << 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;

View file

@ -50,7 +50,9 @@ struct PidInfo {
enum class HalType {
BINDERIZED_SERVICES = 0,
PASSTHROUGH_CLIENTS,
PASSTHROUGH_LIBRARIES
PASSTHROUGH_LIBRARIES,
VINTF_MANIFEST,
LAZY_HALS,
};
class ListCommand : public Command {
@ -93,10 +95,12 @@ protected:
// Retrieve derived information base on existing table
virtual void postprocess();
Status dump();
void putEntry(TableEntrySource source, TableEntry &&entry);
void putEntry(HalType type, 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 fetchManifestHals();
Status fetchLazyHals();
Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager,
TableEntry *entry);
@ -134,6 +138,8 @@ protected:
void forEachTable(const std::function<void(Table &)> &f);
void forEachTable(const std::function<void(const Table &)> &f) const;
Table* tableForType(HalType type);
const Table* tableForType(HalType type) const;
NullableOStream<std::ostream> err() const;
NullableOStream<std::ostream> out() const;
@ -144,12 +150,20 @@ 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();
// Helper functions ti add HALs that are listed in VINTF manifest to LAZY_HALS table.
bool hasHwbinderEntry(const TableEntry& entry) const;
bool hasPassthroughEntry(const TableEntry& entry) const;
Table mServicesTable{};
Table mPassthroughRefTable{};
Table mImplementationsTable{};
Table mManifestHalsTable{};
Table mLazyHalsTable{};
std::string mFileOutputPath;
TableEntryCompare mSortColumn = nullptr;
@ -163,9 +177,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.

View file

@ -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 {

View file

@ -35,13 +35,6 @@ namespace lshal {
using android::procpartition::Partition;
using Pids = std::vector<int32_t>;
enum : unsigned int {
HWSERVICEMANAGER_LIST, // through defaultServiceManager()->list()
PTSERVICEMANAGER_REG_CLIENT, // through registerPassthroughClient
LIST_DLLIB, // through listing dynamic libraries
};
using TableEntrySource = unsigned int;
enum class TableColumnType : unsigned int {
INTERFACE_NAME,
TRANSPORT,
@ -55,6 +48,7 @@ enum class TableColumnType : unsigned int {
RELEASED,
HASH,
VINTF,
SERVICE_STATUS,
};
enum : unsigned int {
@ -71,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};
@ -86,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;

View file

@ -226,12 +226,13 @@ public:
void SetUp() override {
mockLshal = std::make_unique<NiceMock<MockLshal>>();
mockList = std::make_unique<MockListCommand>(mockLshal.get());
ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(err)));
// ListCommand::parseArgs should parse arguments from the second element
optind = 1;
}
std::unique_ptr<MockLshal> mockLshal;
std::unique_ptr<MockListCommand> mockList;
std::stringstream output;
std::stringstream err;
};
TEST_F(ListParseArgsTest, Args) {
@ -241,6 +242,7 @@ TEST_F(ListParseArgsTest, Args) {
TableColumnType::SERVER_ADDR, TableColumnType::CLIENT_PIDS}),
table.getSelectedColumns());
});
EXPECT_EQ("", err.str());
}
TEST_F(ListParseArgsTest, Cmds) {
@ -255,12 +257,12 @@ TEST_F(ListParseArgsTest, Cmds) {
EXPECT_THAT(table.getSelectedColumns(), Contains(TableColumnType::CLIENT_CMDS))
<< "should print client cmds with -m";
});
EXPECT_EQ("", err.str());
}
TEST_F(ListParseArgsTest, DebugAndNeat) {
ON_CALL(*mockLshal, err()).WillByDefault(Return(NullableOStream<std::ostream>(output)));
EXPECT_NE(0u, mockList->parseArgs(createArg({"lshal", "--neat", "-d"})));
EXPECT_THAT(output.str(), StrNe(""));
EXPECT_THAT(err.str(), HasSubstr("--neat should not be used with --debug."));
}
/// Fetch Test
@ -325,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();
@ -349,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() {
@ -409,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,
@ -431,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) {
@ -756,6 +766,60 @@ 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());
}
TEST_F(ListVintfTest, Lazy) {
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=z", "--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_EQ("", err.str());
}
TEST_F(ListVintfTest, NoLazy) {
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-iStr", "--types=b,c,l", "--neat"})));
EXPECT_THAT(out.str(), Not(HasSubstr("IBar")));
EXPECT_EQ("", err.str());
}
class HelpTest : public ::testing::Test {
public:
void SetUp() override {

View file

@ -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;