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

Commit 73d5aef5 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "lshal --vintf to create a skeleton hal manifest."

parents 180bb926 4b86549b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ cc_binary {
        "libbase",
        "libutils",
        "libhidlbase",
        "android.hidl.manager@1.0",
        "libhidltransport",
        "libhidl-gen-utils",
        "libvintf",
        "android.hidl.manager@1.0",
    ],
    srcs: [
        "Lshal.cpp"
+124 −9
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@
#include <android-base/parseint.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <hidl/ServiceManagement.h>
#include <hidl-util/FQName.h>
#include <vintf/HalManifest.h>
#include <vintf/parse_xml.h>

#include "Timeout.h"

@@ -58,12 +61,13 @@ static std::string toHexString(uint64_t t) {
    return os.str();
}

static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
template<typename String>
static std::pair<String, String> splitFirst(const String &s, char c) {
    const char *pos = strchr(s.c_str(), c);
    if (pos == nullptr) {
        return {s, {}};
    }
    return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
    return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
}

static std::vector<std::string> split(const std::string &s, char c) {
@@ -81,6 +85,14 @@ static std::vector<std::string> split(const std::string &s, char c) {
    return components;
}

static void replaceAll(std::string *s, char from, char to) {
    for (size_t i = 0; i < s->size(); ++i) {
        if (s->at(i) == from) {
            s->at(i) = to;
        }
    }
}

std::string getCmdline(pid_t pid) {
    std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
    std::string cmdline;
@@ -189,7 +201,78 @@ void Lshal::printLine(
    mOut << std::endl;
}

void Lshal::dump() const {
void Lshal::dumpVintf() const {
    vintf::HalManifest manifest;
    for (const TableEntry &entry : mTable) {

        std::string fqInstanceName = entry.interfaceName;

        if (entry.source == LIST_DLLIB) {
            // Quick hack to work around *'s
            replaceAll(&fqInstanceName, '*', 'D');
        }
        auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
        FQName fqName(splittedFqInstanceName.first);
        if (!fqName.isValid()) {
            mErr << "Warning: '" << splittedFqInstanceName.first
                 << "' is not a valid FQName." << std::endl;
            continue;
        }
        // Strip out system libs.
        // TODO(b/34772739): might want to add other framework HAL packages
        if (fqName.inPackage("android.hidl")) {
            continue;
        }
        std::string interfaceName =
                entry.source == LIST_DLLIB ? "" : fqName.name();
        std::string instanceName =
                entry.source == LIST_DLLIB ? "" : splittedFqInstanceName.second;

        vintf::Transport transport;
        if (entry.transport == "hwbinder") {
            transport = vintf::Transport::HWBINDER;
        } else if (entry.transport == "passthrough") {
            transport = vintf::Transport::PASSTHROUGH;
        } else {
            mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
            continue;
        }

        vintf::ManifestHal *hal = manifest.getHal(fqName.package());
        if (hal == nullptr) {
            if (!manifest.add(vintf::ManifestHal{
                .format = vintf::HalFormat::HIDL,
                .name = fqName.package(),
                .impl = {.implLevel = vintf::ImplLevel::GENERIC, .impl = ""},
                .transport = transport
            })) {
                mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
                continue;
            }
            hal = manifest.getHal(fqName.package());
        }
        if (hal == nullptr) {
            mErr << "Warning: cannot get hal '" << fqInstanceName
                 << "' after adding it" << std::endl;
            continue;
        }
        vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()};
        if (std::find(hal->versions.begin(), hal->versions.end(), version) == hal->versions.end()) {
            hal->versions.push_back(version);
        }
        if (entry.source != LIST_DLLIB) {
            auto it = hal->interfaces.find(interfaceName);
            if (it == hal->interfaces.end()) {
                hal->interfaces.insert({interfaceName, {interfaceName, {{instanceName}}}});
            } else {
                it->second.instances.insert(instanceName);
            }
        }
    }
    mOut << vintf::gHalManifestConverter(manifest);
}

void Lshal::dumpTable() const {
    mOut << "All services:" << std::endl;
    mOut << std::left;
    printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
@@ -204,6 +287,20 @@ void Lshal::dump() const {
    }
}

void Lshal::dump() {
    if (mVintf) {
        dumpVintf();
        if (!!mFileOutput) {
            mFileOutput.buf().close();
            delete &mFileOutput.buf();
            mFileOutput = nullptr;
        }
        mOut = std::cout;
    } else {
        dumpTable();
    }
}

void Lshal::putEntry(TableEntry &&entry) {
    mTable.push_back(std::forward<TableEntry>(entry));
}
@@ -219,7 +316,8 @@ Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
                .transport = "passthrough",
                .serverPid = NO_PID,
                .serverObjectAddress = NO_PTR,
                .clientPids = {}
                .clientPids = {},
                .source = LIST_DLLIB
            });
        }
    });
@@ -244,7 +342,8 @@ Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
                .transport = "passthrough",
                .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
                .serverObjectAddress = NO_PTR,
                .clientPids = info.clientPids
                .clientPids = info.clientPids,
                .source = PTSERVICEMANAGER_REG_CLIENT
            });
        }
    });
@@ -279,7 +378,7 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
    std::map<std::string, DebugInfo> allDebugInfos;
    std::map<pid_t, std::map<uint64_t, Pids>> allPids;
    for (const auto &fqInstanceName : fqInstanceNames) {
        const auto pair = split(fqInstanceName, '/');
        const auto pair = splitFirst(fqInstanceName, '/');
        const auto &serviceName = pair.first;
        const auto &instanceName = pair.second;
        auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
@@ -326,7 +425,8 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
                .transport = mode,
                .serverPid = NO_PID,
                .serverObjectAddress = NO_PTR,
                .clientPids = {}
                .clientPids = {},
                .source = HWSERVICEMANAGER_LIST
            });
            continue;
        }
@@ -337,7 +437,8 @@ Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
            .serverPid = info.pid,
            .serverObjectAddress = info.ptr,
            .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
                    ? Pids{} : allPids[info.pid][info.ptr]
                    ? Pids{} : allPids[info.pid][info.ptr],
            .source = HWSERVICEMANAGER_LIST
        });
    }
    return status;
@@ -371,7 +472,7 @@ void Lshal::usage() const {
        << "           Dump all hals with default ordering and columns [-itpc]." << std::endl
        << "       lshal [--interface|-i] [--transport|-t]" << std::endl
        << "             [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
        << "             [--sort={interface|i|pid|p}]" << std::endl
        << "             [--sort={interface|i|pid|p}] [--init-vintf[=path]]" << std::endl
        << "           -i, --interface: print the interface name column" << std::endl
        << "           -n, --instance: print the instance name column" << std::endl
        << "           -t, --transport: print the transport mode column" << std::endl
@@ -382,6 +483,8 @@ void Lshal::usage() const {
        << "           -m, --cmdline: print cmdline instead of PIDs" << std::endl
        << "           --sort=i, --sort=interface: sort by interface name" << std::endl
        << "           --sort=p, --sort=pid: sort by server pid" << std::endl
        << "           --init-vintf=path: form a skeleton HAL manifest to specified file " << std::endl
        << "                         (stdout if no file specified)" << std::endl
        << "       lshal [-h|--help]" << std::endl
        << "           -h, --help: show this help information." << std::endl;
}
@@ -399,6 +502,7 @@ Status Lshal::parseArgs(int argc, char **argv) {

        // long options without short alternatives
        {"sort",      required_argument, 0, 's' },
        {"init-vintf",optional_argument, 0, 'v' },
        { 0,          0,                 0,  0  }
    };

@@ -424,6 +528,17 @@ Status Lshal::parseArgs(int argc, char **argv) {
            }
            break;
        }
        case 'v': {
            if (optarg) {
                mFileOutput = new std::ofstream{optarg};
                mOut = mFileOutput;
                if (!mFileOutput.buf().is_open()) {
                    mErr << "Could not open file '" << optarg << "'." << std::endl;
                    return IO_ERROR;
                }
            }
            mVintf = true;
        }
        case 'i': {
            mSelectedColumns |= ENABLE_INTERFACE_NAME;
            break;
+11 −5
Original line number Diff line number Diff line
@@ -19,12 +19,13 @@

#include <stdint.h>

#include <iostream>
#include <fstream>
#include <string>
#include <vector>

#include <android/hidl/manager/1.0/IServiceManager.h>

#include "NullableOStream.h"
#include "TableEntry.h"

namespace android {
@@ -38,6 +39,7 @@ enum : unsigned int {
    DUMP_BINDERIZED_ERROR                   = 1 << 3,
    DUMP_PASSTHROUGH_ERROR                  = 1 << 4,
    DUMP_ALL_LIBS_ERROR                     = 1 << 5,
    IO_ERROR                                = 1 << 6,
};
using Status = unsigned int;

@@ -49,7 +51,7 @@ private:
    Status parseArgs(int argc, char **argv);
    Status fetch();
    void postprocess();
    void dump() const;
    void dump();
    void usage() const;
    void putEntry(TableEntry &&entry);
    Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
@@ -57,6 +59,8 @@ private:
    Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager);
    bool getReferencedPids(
        pid_t serverPid, std::map<uint64_t, Pids> *objects) const;
    void dumpTable() const;
    void dumpVintf() const;
    void printLine(
            const std::string &interfaceName,
            const std::string &transport, const std::string &server,
@@ -70,12 +74,14 @@ private:
    void removeDeadProcesses(Pids *pids);

    Table mTable{};
    std::ostream &mErr = std::cerr;
    std::ostream &mOut = std::cout;
    NullableOStream<std::ostream> mErr = std::cerr;
    NullableOStream<std::ostream> mOut = std::cout;
    NullableOStream<std::ofstream> mFileOutput = nullptr;
    TableEntryCompare mSortColumn = nullptr;
    TableEntrySelect mSelectedColumns = 0;
    // If true, cmdlines will be printed instead of pid.
    bool mEnableCmdlines;
    bool mEnableCmdlines = false;
    bool mVintf = false;
    // 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.
    // If an entry exist and not empty, it contains the cached content of /proc/{pid}/cmdline.
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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_NULLABLE_O_STREAM_H_
#define FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_

#include <iostream>

namespace android {
namespace lshal {

template<typename S>
class NullableOStream {
public:
    NullableOStream(S &os) : mOs(&os) {}
    NullableOStream(S *os) : mOs(os) {}
    NullableOStream &operator=(S &os) {
        mOs = &os;
        return *this;
    }
    NullableOStream &operator=(S *os) {
        mOs = os;
        return *this;
    }
    template<typename Other>
    NullableOStream &operator=(const NullableOStream<Other> &other) {
        mOs = other.mOs;
        return *this;
    }

    const NullableOStream &operator<<(std::ostream& (*pf)(std::ostream&)) const {
        if (mOs) {
            (*mOs) << pf;
        }
        return *this;
    }
    template<typename T>
    const NullableOStream &operator<<(const T &rhs) const {
        if (mOs) {
            (*mOs) << rhs;
        }
        return *this;
    }
    S& buf() const {
        return *mOs;
    }
    operator bool() const {
        return mOs != nullptr;
    }
private:
    template<typename>
    friend class NullableOStream;

    S *mOs = nullptr;
};

}  // namespace lshal
}  // namespace android

#endif  // FRAMEWORK_NATIVE_CMDS_LSHAL_NULLABLE_O_STREAM_H_
+8 −0
Original line number Diff line number Diff line
@@ -28,6 +28,13 @@ namespace lshal {

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;

struct TableEntry {
    std::string interfaceName;
    std::string transport;
@@ -36,6 +43,7 @@ struct TableEntry {
    uint64_t serverObjectAddress;
    Pids clientPids;
    std::vector<std::string> clientCmdlines;
    TableEntrySource source;

    static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) {
        return a.interfaceName < b.interfaceName;