Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bc1e9ff authored by Yifan Hong's avatar Yifan Hong
Browse files

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
parent 74a871ae
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ cc_library_shared {
        "Lshal.cpp",
        "ListCommand.cpp",
        "PipeRelay.cpp",
        "TextTable.cpp",
        "utils.cpp",
    ],
}
+60 −46
Original line number Diff line number Diff line
@@ -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");
        }
        TextTable textTable;

        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, ";"));
        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(),
                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
+6 −10
Original line number Diff line number Diff line
@@ -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
+3 −2
Original line number Diff line number Diff line
@@ -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";

+58 −0
Original line number Diff line number Diff line
/*
 * 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
Loading