lshal: Add --all --types=all, and use in bugreport

Also clang-format.
When --all is specified, put "clients" at the end because it is usually
the longest.

Test: lshal --all
Test: lshal --all --cmdline
Test: lshal --types=all
Test: adb bugreport
Bug: 163060248
Change-Id: If95a18cff50d3b6ef3f70b300f16105b41ca28ed
This commit is contained in:
Yifan Hong 2020-08-07 18:24:06 -07:00
parent 52e3b47cae
commit 30528a2d91
5 changed files with 147 additions and 43 deletions

View file

@ -1237,12 +1237,12 @@ static Dumpstate::RunStatus RunDumpsysNormal() {
static void DumpHals() {
if (!ds.IsZipping()) {
RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
return;
}
DurationReporter duration_reporter("DUMP HALS");
RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"},
CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
using android::hidl::manager::V1_0::IServiceManager;

View file

@ -916,6 +916,18 @@ void ListCommand::initFetchTypes() {
}
}
// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST.
// T::LAST is not included in the returned list.
template <typename T>
std::vector<T> GetAllValues() {
using BaseType = std::underlying_type_t<T>;
std::vector<T> ret;
for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) {
ret.push_back(static_cast<T>(i));
}
return ret;
}
void ListCommand::registerAllOptions() {
int v = mOptions.size();
// A list of acceptable command line options
@ -989,6 +1001,15 @@ void ListCommand::registerAllOptions() {
" - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
" - N/A: no information for passthrough HALs."});
mOptions.push_back({'A', "all", no_argument, v++,
[](ListCommand* thiz, const char*) {
auto allColumns = GetAllValues<TableColumnType>();
thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(),
allColumns.begin(), allColumns.end());
return OK;
},
"print all columns"});
// long options without short alternatives
mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
thiz->mVintf = true;
@ -1019,46 +1040,55 @@ void ListCommand::registerAllOptions() {
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; }
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},
{"vintf", HalType::VINTF_MANIFEST},
{"v", HalType::VINTF_MANIFEST},
{"lazy", HalType::LAZY_HALS},
{"z", HalType::LAZY_HALS},
};
static const std::map<std::string, std::vector<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}},
{"vintf", {HalType::VINTF_MANIFEST}},
{"v", {HalType::VINTF_MANIFEST}},
{"lazy", {HalType::LAZY_HALS}},
{"z", {HalType::LAZY_HALS}},
{"all", GetAllValues<HalType>()},
{"a", GetAllValues<HalType>()},
};
std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
for (const auto& halTypeArg : halTypesArgs) {
if (halTypeArg.empty()) continue;
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()) {
const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
if (halTypeIter == kHalTypeMap.end()) {
thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
return USAGE;
}
thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
return USAGE;
}
// Append unique (non-repeated) HAL types to the reporting list
for (auto halType : halTypeIter->second) {
if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
thiz->mListTypes.end()) {
thiz->mListTypes.push_back(halType);
}
}
}
// 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 sections.\nThe output is restricted to the selected "
"section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
"passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
if (thiz->mListTypes.empty()) {
return USAGE;
}
return OK;
},
"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), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."});
}
// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining

View file

@ -52,6 +52,9 @@ enum class HalType {
PASSTHROUGH_LIBRARIES,
VINTF_MANIFEST,
LAZY_HALS,
// Not a real HalType. Used to determine all HalTypes.
LAST,
};
class ListCommand : public Command {

View file

@ -35,19 +35,25 @@ using android::procpartition::Partition;
using Pids = std::vector<int32_t>;
enum class TableColumnType : unsigned int {
INTERFACE_NAME,
INTERFACE_NAME = 0,
TRANSPORT,
SERVER_PID,
SERVER_CMD,
SERVER_ADDR,
CLIENT_PIDS,
CLIENT_CMDS,
ARCH,
THREADS,
RELEASED,
HASH,
VINTF,
SERVICE_STATUS,
CLIENT_PIDS,
// Not a real TableColumnType. Used to determine all TableColumnTypes.
LAST,
// Not included in all TableColumnTypes because they replace *PID(S) when the
// --cmdline option is set.
SERVER_CMD,
CLIENT_CMDS,
};
enum : unsigned int {

View file

@ -708,8 +708,8 @@ TEST_F(ListTest, DumpEmptyAndDuplicateHalTypes) {
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"));
EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"})));
EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r"));
}
TEST_F(ListTest, Vintf) {
@ -793,6 +793,71 @@ TEST_F(ListTest, Vintf) {
EXPECT_EQ("", err.str());
}
TEST_F(ListTest, AllColumns) {
// clang-format off
const std::string expected =
"[fake description 0]\n"
"Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
"a.h.foo1@1.0::IFoo/1 hwbinder 1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive 2 4\n"
"a.h.foo2@2.0::IFoo/2 hwbinder 2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive 3 5\n"
"\n"
"[fake description 1]\n"
"Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
"a.h.foo3@3.0::IFoo/3 passthrough N/A N/A 32 N/A ? X N/A 4 6\n"
"a.h.foo4@4.0::IFoo/4 passthrough N/A N/A 32 N/A ? X N/A 5 7\n"
"\n"
"[fake description 2]\n"
"Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n"
"a.h.foo5@5.0::IFoo/5 passthrough N/A N/A 32 N/A ? X N/A 6 8\n"
"a.h.foo6@6.0::IFoo/6 passthrough N/A N/A 32 N/A ? X N/A 7 9\n"
"\n";
// clang-format on
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"})));
EXPECT_EQ(expected, out.str());
EXPECT_EQ("", err.str());
}
TEST_F(ListTest, AllColumnsWithCmd) {
// clang-format off
const std::string expected =
"[fake description 0]\n"
"Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
"a.h.foo1@1.0::IFoo/1 hwbinder command_line_1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive command_line_2;command_line_4\n"
"a.h.foo2@2.0::IFoo/2 hwbinder command_line_2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive command_line_3;command_line_5\n"
"\n"
"[fake description 1]\n"
"Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
"a.h.foo3@3.0::IFoo/3 passthrough N/A 32 N/A ? X N/A command_line_4;command_line_6\n"
"a.h.foo4@4.0::IFoo/4 passthrough N/A 32 N/A ? X N/A command_line_5;command_line_7\n"
"\n"
"[fake description 2]\n"
"Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n"
"a.h.foo5@5.0::IFoo/5 passthrough N/A 32 N/A ? X N/A command_line_6;command_line_8\n"
"a.h.foo6@6.0::IFoo/6 passthrough N/A 32 N/A ? X N/A command_line_7;command_line_9\n"
"\n";
// clang-format on
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"})));
EXPECT_EQ(expected, out.str());
EXPECT_EQ("", err.str());
}
TEST_F(ListTest, AllSections) {
optind = 1; // mimic Lshal::parseArg()
EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"})));
using HalTypeBase = std::underlying_type_t<HalType>;
for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) {
EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]"));
}
EXPECT_THAT(out.str(),
Not(HasSubstr("[fake description " +
std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]")));
EXPECT_EQ("", err.str());
}
// Fake service returned by mocked IServiceManager::get for DumpDebug.
// The interfaceChain and getHashChain functions returns
// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase.