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:
commit
e3a52f01e4
6 changed files with 323 additions and 81 deletions
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue