diff options
author | 2017-08-29 17:28:12 -0700 | |
---|---|---|
committer | 2017-08-30 10:45:42 -0700 | |
commit | 1bc1e9ffe224e4938d667a567a99619d7dd66229 (patch) | |
tree | f4a930397bf17f2dd0421fae3e8d26b7d193b56e | |
parent | 74a871ae12c00b10daffca7621922ae8f55a209c (diff) |
lshal: pretty print table.
Table column length is not hardcoded, but computed
from length of each cell.
Without --neat, table column length varies for each
table.
As an effect, --neat does not emit debug info. Update
warning messages to reflect this.
Test: lshal
Test: lshal_test
Test: lshal --neat
Bug: 35389839
Change-Id: Id1d626a10ba58e20d2799854432dba74cfeaae6f
-rw-r--r-- | cmds/lshal/Android.bp | 1 | ||||
-rw-r--r-- | cmds/lshal/ListCommand.cpp | 106 | ||||
-rw-r--r-- | cmds/lshal/ListCommand.h | 16 | ||||
-rw-r--r-- | cmds/lshal/Lshal.cpp | 5 | ||||
-rw-r--r-- | cmds/lshal/TextTable.cpp | 58 | ||||
-rw-r--r-- | cmds/lshal/TextTable.h | 84 |
6 files changed, 212 insertions, 58 deletions
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 67b5b46829..2eed1cebc2 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -28,6 +28,7 @@ cc_library_shared { "Lshal.cpp", "ListCommand.cpp", "PipeRelay.cpp", + "TextTable.cpp", "utils.cpp", ], } diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index 7c6cfd94ab..7fb8083aa7 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -35,6 +35,7 @@ #include "Lshal.h" #include "PipeRelay.h" +#include "TextTable.h" #include "Timeout.h" #include "utils.h" @@ -206,42 +207,38 @@ void ListCommand::postprocess() { } } -void ListCommand::printLine( - const std::string &interfaceName, - const std::string &transport, - const std::string &arch, - const std::string &threadUsage, - const std::string &server, - const std::string &serverCmdline, - const std::string &address, - const std::string &clients, - const std::string &clientCmdlines) const { +void ListCommand::addLine(TextTable *textTable, const std::string &interfaceName, + const std::string &transport, const std::string &arch, + const std::string &threadUsage, const std::string &server, + const std::string &serverCmdline, const std::string &address, + const std::string &clients, const std::string &clientCmdlines) const { + std::vector<std::string> columns; if (mSelectedColumns & ENABLE_INTERFACE_NAME) - mOut << std::setw(80) << interfaceName << "\t"; + columns.push_back(interfaceName); if (mSelectedColumns & ENABLE_TRANSPORT) - mOut << std::setw(10) << transport << "\t"; + columns.push_back(transport); if (mSelectedColumns & ENABLE_ARCH) - mOut << std::setw(5) << arch << "\t"; + columns.push_back(arch); if (mSelectedColumns & ENABLE_THREADS) { - mOut << std::setw(8) << threadUsage << "\t"; + columns.push_back(threadUsage); } if (mSelectedColumns & ENABLE_SERVER_PID) { if (mEnableCmdlines) { - mOut << std::setw(15) << serverCmdline << "\t"; + columns.push_back(serverCmdline); } else { - mOut << std::setw(5) << server << "\t"; + columns.push_back(server); } } if (mSelectedColumns & ENABLE_SERVER_ADDR) - mOut << std::setw(16) << address << "\t"; + columns.push_back(address); if (mSelectedColumns & ENABLE_CLIENT_PIDS) { if (mEnableCmdlines) { - mOut << std::setw(0) << clientCmdlines; + columns.push_back(clientCmdlines); } else { - mOut << std::setw(0) << clients; + columns.push_back(clients); } } - mOut << std::endl; + textTable->add(std::move(columns)); } static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) { @@ -397,7 +394,25 @@ static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo: } } +void ListCommand::addLine(TextTable *table, const TableEntry &entry) { + addLine(table, entry.interfaceName, entry.transport, getArchString(entry.arch), + entry.getThreadUsage(), + entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid), + entry.serverCmdline, + entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress), + join(entry.clientPids, " "), join(entry.clientCmdlines, ";")); +} + void ListCommand::dumpTable() { + if (mNeat) { + TextTable textTable; + forEachTable([this, &textTable](const Table &table) { + for (const auto &entry : table) addLine(&textTable, entry); + }); + textTable.dump(mOut.buf()); + return; + } + mServicesTable.description = "All binderized services (registered services through hwservicemanager)"; mPassthroughRefTable.description = @@ -409,42 +424,34 @@ void ListCommand::dumpTable() { "the library and successfully fetched the passthrough implementation."; mImplementationsTable.description = "All available passthrough implementations (all -impl.so files)"; - forEachTable([this] (const Table &table) { - if (!mNeat) { - mOut << table.description << std::endl; - } - mOut << std::left; - if (!mNeat) { - printLine("Interface", "Transport", "Arch", "Thread Use", "Server", - "Server CMD", "PTR", "Clients", "Clients CMD"); - } - for (const auto &entry : table) { - printLine(entry.interfaceName, - entry.transport, - getArchString(entry.arch), - entry.getThreadUsage(), - entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid), - entry.serverCmdline, - entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress), - join(entry.clientPids, " "), - join(entry.clientCmdlines, ";")); + forEachTable([this](const Table &table) { + TextTable textTable; + textTable.add(table.description); + addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD", + "PTR", "Clients", "Clients CMD"); + + for (const auto &entry : table) { + addLine(&textTable, entry); // We're only interested in dumping debug info for already // instantiated services. There's little value in dumping the // debug info for a service we create on the fly, so we only operate // on the "mServicesTable". if (mEmitDebugInfo && &table == &mServicesTable) { + std::stringstream out; auto pair = splitFirst(entry.interfaceName, '/'); - mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(), - NullableOStream<std::ostream>(nullptr)); + mLshal.emitDebugInfo(pair.first, pair.second, {}, out, + NullableOStream<std::ostream>(nullptr)); + textTable.add(out.str()); } } - if (!mNeat) { - mOut << std::endl; - } - }); + // Add empty line after each table + textTable.add(); + + textTable.dump(mOut.buf()); + }); } void ListCommand::dump() { @@ -771,6 +778,14 @@ Status ListCommand::parseArgs(const std::string &command, const Arg &arg) { if (optind < arg.argc) { // see non option mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl; + mLshal.usage(command); + return USAGE; + } + + if (mNeat && mEmitDebugInfo) { + mErr << "Error: --neat should not be used with --debug." << std::endl; + mLshal.usage(command); + return USAGE; } if (mSelectedColumns == 0) { @@ -792,4 +807,3 @@ Status ListCommand::main(const std::string &command, const Arg &arg) { } // namespace lshal } // namespace android - diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index a75db04960..d06ec7d7cf 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -28,6 +28,7 @@ #include "NullableOStream.h" #include "TableEntry.h" +#include "TextTable.h" #include "utils.h" namespace android { @@ -58,16 +59,11 @@ private: void dumpTable(); void dumpVintf() const; - void printLine( - const std::string &interfaceName, - const std::string &transport, - const std::string &arch, - const std::string &threadUsage, - const std::string &server, - const std::string &serverCmdline, - const std::string &address, - const std::string &clients, - const std::string &clientCmdlines) const; + void addLine(TextTable *table, const std::string &interfaceName, const std::string &transport, + const std::string &arch, const std::string &threadUsage, const std::string &server, + const std::string &serverCmdline, const std::string &address, + const std::string &clients, const std::string &clientCmdlines) const; + void addLine(TextTable *table, const TableEntry &entry); // Return /proc/{pid}/cmdline if it exists, else empty string. const std::string &getCmdline(pid_t pid); // Call getCmdline on all pid in pids. If it returns empty string, the process might diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index 0d4a1d193a..ac74775e07 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -63,7 +63,7 @@ void Lshal::usage(const std::string &command) const { "list:\n" " lshal\n" " lshal list\n" - " List all hals with default ordering and columns (`lshal list -ipc`)\n" + " List all hals with default ordering and columns (`lshal list -iepc`)\n" " lshal list [-h|--help]\n" " -h, --help: Print help message for list (`lshal help list`)\n" " lshal [list] [--interface|-i] [--transport|-t] [-r|--arch] [-e|--threads]\n" @@ -81,10 +81,11 @@ void Lshal::usage(const std::string &command) const { " -c, --clients: print the client PIDs, or client cmdlines if -m is set\n" " -m, --cmdline: print cmdline instead of PIDs\n" " -d[=<output file>], --debug[=<output file>]: emit debug info from \n" - " IBase::debug with empty options\n" + " IBase::debug with empty options. Cannot be used with --neat.\n" " --sort=i, --sort=interface: sort by interface name\n" " --sort=p, --sort=pid: sort by server pid\n" " --neat: output is machine parsable (no explanatory text)\n" + " Cannot be used with --debug.\n" " --init-vintf[=<output file>]: form a skeleton HAL manifest to specified\n" " file, or stdout if no file specified.\n"; diff --git a/cmds/lshal/TextTable.cpp b/cmds/lshal/TextTable.cpp new file mode 100644 index 0000000000..0a721e9f81 --- /dev/null +++ b/cmds/lshal/TextTable.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include <iomanip> + +#include "TextTable.h" + +namespace android { +namespace lshal { + +void TextTable::computeWidth(const std::vector<std::string>& v) { + if (mWidths.size() < v.size()) { + mWidths.resize(v.size()); + } + for (size_t i = 0; i < v.size() - 1; ++i) { + mWidths[i] = std::max(mWidths[i], v[i].length()); + } + // last column has std::setw(0) to avoid printing unnecessary spaces. + mWidths[v.size() - 1] = 0; +} + +void TextTable::dump(std::ostream& out) const { + out << std::left; + for (const auto& row : mTable) { + if (!row.isRow()) { + out << row.line() << std::endl; + continue; + } + + for (size_t i = 0; i < row.fields().size(); ++i) { + if (i != 0) { + out << " "; + } + if (i < mWidths.size()) { + out << std::setw(mWidths[i]); + } + out << row.fields()[i]; + } + out << std::endl; + } +} + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/TextTable.h b/cmds/lshal/TextTable.h new file mode 100644 index 0000000000..4636f15a27 --- /dev/null +++ b/cmds/lshal/TextTable.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ + +#include <iostream> +#include <string> +#include <vector> + +#include "TableEntry.h" + +namespace android { +namespace lshal { + +// An element in TextTable. This is either an actual row (an array of cells +// in this row), or a string of explanatory text. +// To see if this is an actual row, test fields().empty(). +class TextTableRow { +public: + // An empty line. + TextTableRow() {} + + // A row of cells. + TextTableRow(std::vector<std::string>&& v) : mFields(std::move(v)) {} + + // A single comment string. + TextTableRow(std::string&& s) : mLine(std::move(s)) {} + TextTableRow(const std::string& s) : mLine(s) {} + + // Whether this row is an actual row of cells. + bool isRow() const { return !fields().empty(); } + + // Get all cells. + const std::vector<std::string>& fields() const { return mFields; } + + // Get the single comment string. + const std::string& line() const { return mLine; } + +private: + std::vector<std::string> mFields; + std::string mLine; +}; + +// A TextTable is a 2D array of strings. +class TextTable { +public: + + // Add a TextTableRow. + void add() { mTable.emplace_back(); } + void add(std::vector<std::string>&& v) { + computeWidth(v); + mTable.emplace_back(std::move(v)); + } + void add(const std::string& s) { mTable.emplace_back(s); } + void add(std::string&& s) { mTable.emplace_back(std::move(s)); } + + // Prints the table to out, with column widths adjusted appropriately according + // to the content. + void dump(std::ostream& out) const; + +private: + void computeWidth(const std::vector<std::string>& v); + std::vector<size_t> mWidths; + std::vector<TextTableRow> mTable; +}; + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TEXT_TABLE_H_ |