summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYifan Hong <elsk@google.com>2017-02-28 19:38:24 -0800
committerYifan Hong <elsk@google.com>2017-02-28 20:30:30 -0800
commit4b86549b14b30a0ba4381c09c5ce5185ad56bf1c (patch)
tree508917991c8921503eb18138b0fdeabe185347e2
parent180bb92608a9bf24ab11a101b87844ea478b0324 (diff)
downloadnative-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.bp4
-rw-r--r--cmds/lshal/Lshal.cpp133
-rw-r--r--cmds/lshal/Lshal.h16
-rw-r--r--cmds/lshal/NullableOStream.h73
-rw-r--r--cmds/lshal/TableEntry.h8
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;