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

Commit fee209dd authored by Yifan Hong's avatar Yifan Hong
Browse files

lshal: add Released column.

Example output:

$ lshal --neat -lis
...
Y android.hardware.configstore@1.0::ISurfaceFlingerConfigs/default 7f5fe8f4f8a24037153c504d8b4d3313c2ce33d81c8c69fe5194ddd2d4080e72
  android.hardware.configstore@1.1::ISurfaceFlingerConfigs/default 0000000000000000000000000000000000000000000000000000000000000000
...

Bug: 65123158
Test: lshal_test
Test: lshal
Test: lshal -ils
Test: lshal --help

Change-Id: I18e52eb977461d68909057583be8223d53f6748b
parent 22ea7b85
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ cc_library_shared {
        "libutils",
        "libhidlbase",
        "libhidltransport",
        "libhidl-gen-hash",
        "libhidl-gen-utils",
        "libvintf",
    ],
+50 −2
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl-hash/Hash.h>
#include <hidl-util/FQName.h>
#include <private/android_filesystem_config.h>
#include <sys/stat.h>
@@ -590,6 +591,44 @@ Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
            entry->threadCount = pidInfo->threadCount;
        }
    } while (0);

    // hash
    do {
        ssize_t hashIndex = -1;
        auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
            for (size_t i = 0; i < c.size(); ++i) {
                if (serviceName == c[i]) {
                    hashIndex = static_cast<ssize_t>(i);
                    break;
                }
            }
        });
        if (!ifaceChainRet.isOk()) {
            handleError(TRANSACTION_ERROR,
                        "interfaceChain fails: " + ifaceChainRet.description());
            break; // skip getHashChain
        }
        if (hashIndex < 0) {
            handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
            break; // skip getHashChain
        }
        auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
            if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
                handleError(BAD_IMPL,
                            "interfaceChain indicates position " + std::to_string(hashIndex) +
                            " but getHashChain returns " + std::to_string(hashChain.size()) +
                            " hashes");
                return;
            }

            auto&& hashArray = hashChain[hashIndex];
            std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
            entry->hash = Hash::hexString(hashVec);
        });
        if (!hashRet.isOk()) {
            handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
        }
    } while (0);
    return status;
}

@@ -627,6 +666,10 @@ void ListCommand::registerAllOptions() {
        thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
        return OK;
    }, "print the instance name column"});
    mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
        return OK;
    }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"});
    mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
        return OK;
@@ -635,6 +678,10 @@ void ListCommand::registerAllOptions() {
        thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
        return OK;
    }, "print the bitness column"});
    mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::HASH);
        return OK;
    }, "print hash of the interface"});
    mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
        thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
        return OK;
@@ -777,7 +824,8 @@ Status ListCommand::parseArgs(const Arg &arg) {
    }

    if (mSelectedColumns.empty()) {
        mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
        mSelectedColumns = {TableColumnType::RELEASED,
                            TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
                            TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
    }

@@ -845,7 +893,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 -iepc`)" << std::endl
          << "        List all hals with default ordering and columns (`lshal list -riepc`)" << 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;
+19 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#define LOG_TAG "lshal"
#include <android-base/logging.h>

#include <hidl-hash/Hash.h>

#include "TableEntry.h"

#include "TextTable.h"
@@ -53,8 +55,10 @@ static std::string getTitle(TableColumnType type) {
        case TableColumnType::CLIENT_CMDS:      return "Clients CMD";
        case TableColumnType::ARCH:             return "Arch";
        case TableColumnType::THREADS:          return "Thread Use";
        case TableColumnType::RELEASED:         return "R";
        case TableColumnType::HASH:             return "Hash";
        default:
            LOG(FATAL) << "Should not reach here.";
            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
            return "";
    }
}
@@ -79,12 +83,25 @@ std::string TableEntry::getField(TableColumnType type) const {
            return getArchString(arch);
        case TableColumnType::THREADS:
            return getThreadUsage();
        case TableColumnType::RELEASED:
            return isReleased();
        case TableColumnType::HASH:
            return hash;
        default:
            LOG(FATAL) << "Should not reach here.";
            LOG(FATAL) << __func__ << "Should not reach here. " << static_cast<int>(type);
            return "";
    }
}

std::string TableEntry::isReleased() const {
    static const std::string unreleased = Hash::hexString(Hash::kEmptyHash);

    if (hash.empty() || hash == unreleased) {
        return " "; // unknown or unreleased
    }
    return "Y"; // released
}

TextTable Table::createTextTable(bool neat,
    const std::function<std::string(const std::string&)>& emitDebugInfo) const {

+6 −0
Original line number Diff line number Diff line
@@ -55,6 +55,8 @@ enum class TableColumnType : unsigned int {
    CLIENT_CMDS,
    ARCH,
    THREADS,
    RELEASED,
    HASH,
};

enum {
@@ -73,6 +75,8 @@ struct TableEntry {
    Pids clientPids{};
    std::vector<std::string> clientCmdlines{};
    Architecture arch{ARCH_UNKNOWN};
    // empty: unknown, all zeros: unreleased, otherwise: released
    std::string hash{};

    static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
        return a.interfaceName < b.interfaceName;
@@ -89,6 +93,8 @@ struct TableEntry {
        return std::to_string(threadUsage) + "/" + std::to_string(threadCount);
    }

    std::string isReleased() const;

    std::string getField(TableColumnType type) const;

    bool operator==(const TableEntry& other) const;
+73 −4
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
using ::android::hidl::manager::V1_0::IServiceNotification;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_death_recipient;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
@@ -46,6 +47,8 @@ using ::android::hardware::hidl_vec;

using InstanceDebugInfo = IServiceManager::InstanceDebugInfo;

using hidl_hash = hidl_array<uint8_t, 32>;

namespace android {
namespace hardware {
namespace tests {
@@ -277,17 +280,34 @@ static std::string getCmdlineFromId(pid_t serverId) {
    if (serverId == NO_PID) return "";
    return "command_line_" + std::to_string(serverId);
}
static bool getIsReleasedFromId(pid_t p) { return p % 2 == 0; }
static hidl_hash getHashFromId(pid_t serverId) {
    hidl_hash hash;
    bool isReleased = getIsReleasedFromId(serverId);
    for (size_t i = 0; i < hash.size(); ++i) {
        hash[i] = isReleased ? static_cast<uint8_t>(serverId) : 0u;
    }
    return hash;
}

// Fake service returned by mocked IServiceManager::get.
class TestService : public IBase {
public:
    TestService(DebugInfo&& info) : mInfo(std::move(info)) {}
    TestService(pid_t id) : mId(id) {}
    hardware::Return<void> getDebugInfo(getDebugInfo_cb cb) override {
        cb(mInfo);
        cb({ mId /* pid */, getPtr(mId), DebugInfo::Architecture::IS_64BIT });
        return hardware::Void();
    }
    hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
        cb({getInterfaceName(mId), IBase::descriptor});
        return hardware::Void();
    }
    hardware::Return<void> getHashChain(getHashChain_cb cb) override {
        cb({getHashFromId(mId), getHashFromId(0xff)});
        return hardware::Void();
    }
private:
    DebugInfo mInfo;
    pid_t mId;
};

class ListTest : public ::testing::Test {
@@ -328,7 +348,7 @@ public:
        ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke(
            [&](const hidl_string&, const hidl_string& instance) {
                int id = getIdFromInstanceName(instance);
                return sp<IBase>(new TestService({ id /* pid */, getPtr(id), A::IS_64BIT }));
                return sp<IBase>(new TestService(id));
            }));

        ON_CALL(*serviceManager, debugDump(_)).WillByDefault(Invoke(
@@ -473,6 +493,55 @@ TEST_F(ListTest, DumpVintf) {
        << vintf::gHalManifestConverter.lastError();
}

// test default columns
TEST_F(ListTest, DumpDefault) {
    const std::string expected =
        "[fake description 0]\n"
        "R Interface            Thread Use Server Clients\n"
        "  a.h.foo1@1.0::IFoo/1 11/21      1      2 4\n"
        "Y a.h.foo2@2.0::IFoo/2 12/22      2      3 5\n"
        "\n"
        "[fake description 1]\n"
        "R Interface            Thread Use Server Clients\n"
        "  a.h.foo3@3.0::IFoo/3 N/A        N/A    4 6\n"
        "  a.h.foo4@4.0::IFoo/4 N/A        N/A    5 7\n"
        "\n"
        "[fake description 2]\n"
        "R Interface            Thread Use Server Clients\n"
        "  a.h.foo5@5.0::IFoo/5 N/A        N/A    6 8\n"
        "  a.h.foo6@6.0::IFoo/6 N/A        N/A    7 9\n"
        "\n";

    optind = 1; // mimic Lshal::parseArg()
    EXPECT_EQ(0u, mockList->main(createArg({"lshal"})));
    EXPECT_EQ(expected, out.str());
    EXPECT_EQ("", err.str());
}

TEST_F(ListTest, DumpHash) {
    const std::string expected =
        "[fake description 0]\n"
        "Interface            R Hash\n"
        "a.h.foo1@1.0::IFoo/1   0000000000000000000000000000000000000000000000000000000000000000\n"
        "a.h.foo2@2.0::IFoo/2 Y 0202020202020202020202020202020202020202020202020202020202020202\n"
        "\n"
        "[fake description 1]\n"
        "Interface            R Hash\n"
        "a.h.foo3@3.0::IFoo/3   \n"
        "a.h.foo4@4.0::IFoo/4   \n"
        "\n"
        "[fake description 2]\n"
        "Interface            R Hash\n"
        "a.h.foo5@5.0::IFoo/5   \n"
        "a.h.foo6@6.0::IFoo/6   \n"
        "\n";

    optind = 1; // mimic Lshal::parseArg()
    EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-ils"})));
    EXPECT_EQ(expected, out.str());
    EXPECT_EQ("", err.str());
}

TEST_F(ListTest, Dump) {
    const std::string expected =
        "[fake description 0]\n"
Loading