diff options
author | Yifan Hong <elsk@google.com> | 2017-02-28 19:38:24 -0800 |
---|---|---|
committer | Yifan Hong <elsk@google.com> | 2017-02-28 20:30:30 -0800 |
commit | 4b86549b14b30a0ba4381c09c5ce5185ad56bf1c (patch) | |
tree | 508917991c8921503eb18138b0fdeabe185347e2 | |
parent | 180bb92608a9bf24ab11a101b87844ea478b0324 (diff) | |
download | native-4b86549b14b30a0ba4381c09c5ce5185ad56bf1c.tar.gz |
lshal --vintf to create a skeleton hal manifest.
Run it as follows:
lshal --init-vintf=/data/a.xml
lshal --init-vintf > /data/a.xml
Test: lshal --init-vintf with and without path argument
Bug: 35852743
Change-Id: Ief9385fc2764a487d1a70644699e01133bdc8a8e
-rw-r--r-- | cmds/lshal/Android.bp | 4 | ||||
-rw-r--r-- | cmds/lshal/Lshal.cpp | 133 | ||||
-rw-r--r-- | cmds/lshal/Lshal.h | 16 | ||||
-rw-r--r-- | cmds/lshal/NullableOStream.h | 73 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.h | 8 |
5 files changed, 219 insertions, 15 deletions
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 5aab35a450..39e0ba3468 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -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" diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index ae5a8a0634..e953dedea2 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -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; diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index ead99dc068..1c30908189 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -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. diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h new file mode 100644 index 0000000000..ab37a04c56 --- /dev/null +++ b/cmds/lshal/NullableOStream.h @@ -0,0 +1,73 @@ +/* + * 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_ diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 4ec3a0ced1..e55806e2b6 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -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; |