diff options
author | Yifan Hong <elsk@google.com> | 2017-02-16 18:02:51 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-02-16 18:02:54 +0000 |
commit | 7e894fc94fab8135ede1bf046ae3b4183c53057b (patch) | |
tree | 89d7657e868047708dfd5eae424575ecfac3bf34 | |
parent | 9555a5d4974771cd46750550e5722f3cba050709 (diff) | |
parent | ae09a3ddfec51c82257e338346e39ee472470061 (diff) | |
download | native-7e894fc94fab8135ede1bf046ae3b4183c53057b.tar.gz |
Merge changes from topic 'lshal'
* changes:
lshal: Add option to print cmd lines instead of pids.
lshal: Add timeout for IPC calls.
lshal: Allow selecting columns and sorting by column.
lshal: Refactor lshal to use an Lshal class; combined instance column with interface column
-rw-r--r-- | cmds/lshal/Android.bp | 2 | ||||
-rw-r--r-- | cmds/lshal/Lshal.cpp | 477 | ||||
-rw-r--r-- | cmds/lshal/Lshal.h | 89 | ||||
-rw-r--r-- | cmds/lshal/TableEntry.h | 69 | ||||
-rw-r--r-- | cmds/lshal/Timeout.h | 81 | ||||
-rw-r--r-- | cmds/lshal/lshal.cpp | 306 |
6 files changed, 717 insertions, 307 deletions
diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index c3805985c4..5aab35a450 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -22,6 +22,6 @@ cc_binary { "libhidltransport", ], srcs: [ - "lshal.cpp" + "Lshal.cpp" ], } diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp new file mode 100644 index 0000000000..ce058c8a47 --- /dev/null +++ b/cmds/lshal/Lshal.cpp @@ -0,0 +1,477 @@ +/* + * 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. + */ + +#include "Lshal.h" + +#include <getopt.h> + +#include <fstream> +#include <iomanip> +#include <iostream> +#include <map> +#include <sstream> +#include <regex> + +#include <android-base/parseint.h> +#include <android/hidl/manager/1.0/IServiceManager.h> +#include <hidl/ServiceManagement.h> + +#include "Timeout.h" + +using ::android::hardware::hidl_string; +using ::android::hidl::manager::V1_0::IServiceManager; + +namespace android { +namespace lshal { + +template <typename A> +std::string join(const A &components, const std::string &separator) { + std::stringstream out; + bool first = true; + for (const auto &component : components) { + if (!first) { + out << separator; + } + out << component; + + first = false; + } + return out.str(); +} + +static std::string toHexString(uint64_t t) { + std::ostringstream os; + os << std::hex << std::setfill('0') << std::setw(16) << t; + return os.str(); +} + +static std::pair<hidl_string, hidl_string> split(const hidl_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)}; +} + +static std::vector<std::string> split(const std::string &s, char c) { + std::vector<std::string> components{}; + size_t startPos = 0; + size_t matchPos; + while ((matchPos = s.find(c, startPos)) != std::string::npos) { + components.push_back(s.substr(startPos, matchPos - startPos)); + startPos = matchPos + 1; + } + + if (startPos <= s.length()) { + components.push_back(s.substr(startPos)); + } + return components; +} + +std::string getCmdline(pid_t pid) { + std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline"); + std::string cmdline; + if (!ifs.is_open()) { + return ""; + } + ifs >> cmdline; + return cmdline; +} + +const std::string &Lshal::getCmdline(pid_t pid) { + auto pair = mCmdlines.find(pid); + if (pair != mCmdlines.end()) { + return pair->second; + } + mCmdlines[pid] = ::android::lshal::getCmdline(pid); + return mCmdlines[pid]; +} + +void Lshal::removeDeadProcesses(Pids *pids) { + static const pid_t myPid = getpid(); + std::remove_if(pids->begin(), pids->end(), [this](auto pid) { + return pid == myPid || this->getCmdline(pid).empty(); + }); +} + +bool Lshal::getReferencedPids( + pid_t serverPid, std::map<uint64_t, Pids> *objects) const { + + std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid)); + if (!ifs.is_open()) { + return false; + } + + static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+"); + + std::string line; + std::smatch match; + while(getline(ifs, line)) { + if (!std::regex_search(line, match, prefix)) { + // the line doesn't start with the correct prefix + continue; + } + std::string ptrString = "0x" + match.str(2); // use number after c + uint64_t ptr; + if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) { + // Should not reach here, but just be tolerant. + mErr << "Could not parse number " << ptrString << std::endl; + continue; + } + const std::string proc = " proc "; + auto pos = line.rfind(proc); + if (pos != std::string::npos) { + for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) { + int32_t pid; + if (!::android::base::ParseInt(pidStr, &pid)) { + mErr << "Could not parse number " << pidStr << std::endl; + continue; + } + (*objects)[ptr].push_back(pid); + } + } + } + return true; +} + +void Lshal::postprocess() { + if (mSortColumn) { + std::sort(mTable.begin(), mTable.end(), mSortColumn); + } + for (TableEntry &entry : mTable) { + entry.serverCmdline = getCmdline(entry.serverPid); + removeDeadProcesses(&entry.clientPids); + for (auto pid : entry.clientPids) { + entry.clientCmdlines.push_back(this->getCmdline(pid)); + } + } +} + +void Lshal::printLine( + const std::string &interfaceName, + const std::string &transport, const std::string &server, + const std::string &serverCmdline, + const std::string &address, const std::string &clients, + const std::string &clientCmdlines) const { + if (mSelectedColumns & ENABLE_INTERFACE_NAME) + mOut << std::setw(80) << interfaceName << "\t"; + if (mSelectedColumns & ENABLE_TRANSPORT) + mOut << std::setw(10) << transport << "\t"; + if (mSelectedColumns & ENABLE_SERVER_PID) { + if (mEnableCmdlines) { + mOut << std::setw(15) << serverCmdline << "\t"; + } else { + mOut << std::setw(5) << server << "\t"; + } + } + if (mSelectedColumns & ENABLE_SERVER_ADDR) + mOut << std::setw(16) << address << "\t"; + if (mSelectedColumns & ENABLE_CLIENT_PIDS) { + if (mEnableCmdlines) { + mOut << std::setw(0) << clientCmdlines; + } else { + mOut << std::setw(0) << clients; + } + } + mOut << std::endl; +} + +void Lshal::dump() const { + mOut << "All services:" << std::endl; + mOut << std::left; + printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD"); + for (const auto &entry : mTable) { + printLine(entry.interfaceName, + entry.transport, + 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 Lshal::putEntry(TableEntry &&entry) { + mTable.push_back(std::forward<TableEntry>(entry)); +} + +Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) { + using namespace ::android::hardware; + using namespace ::android::hidl::manager::V1_0; + using namespace ::android::hidl::base::V1_0; + auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) { + for (const auto &fqInstanceName : fqInstanceNames) { + putEntry({ + .interfaceName = fqInstanceName, + .transport = "passthrough", + .serverPid = NO_PID, + .serverObjectAddress = NO_PTR, + .clientPids = {} + }); + } + }); + if (!ret.isOk()) { + mErr << "Error: Failed to call list on getPassthroughServiceManager(): " + << ret.description() << std::endl; + return DUMP_ALL_LIBS_ERROR; + } + return OK; +} + +Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) { + using namespace ::android::hardware; + using namespace ::android::hidl::manager::V1_0; + using namespace ::android::hidl::base::V1_0; + auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) { + for (const auto &info : infos) { + putEntry({ + .interfaceName = + std::string{info.interfaceName.c_str()} + "/" + + std::string{info.instanceName.c_str()}, + .transport = "passthrough", + .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID, + .serverObjectAddress = NO_PTR, + .clientPids = info.clientPids + }); + } + }); + if (!ret.isOk()) { + mErr << "Error: Failed to call debugDump on defaultServiceManager(): " + << ret.description() << std::endl; + return DUMP_PASSTHROUGH_ERROR; + } + return OK; +} + +Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) { + using namespace ::std; + using namespace ::android::hardware; + using namespace ::android::hidl::manager::V1_0; + using namespace ::android::hidl::base::V1_0; + const std::string mode = "hwbinder"; + Status status = OK; + auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) { + // server pid, .ptr value of binder object, child pids + 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 &serviceName = pair.first; + const auto &instanceName = pair.second; + auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName); + if (!getRet.isOk()) { + mErr << "Warning: Skipping \"" << fqInstanceName << "\": " + << "cannot be fetched from service manager:" + << getRet.description() << std::endl; + status |= DUMP_BINDERIZED_ERROR; + continue; + } + sp<IBase> service = getRet; + if (service == nullptr) { + mErr << "Warning: Skipping \"" << fqInstanceName << "\": " + << "cannot be fetched from service manager (null)"; + status |= DUMP_BINDERIZED_ERROR; + continue; + } + auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) { + allDebugInfos[fqInstanceName] = debugInfo; + if (debugInfo.pid >= 0) { + allPids[static_cast<pid_t>(debugInfo.pid)].clear(); + } + }); + if (!debugRet.isOk()) { + mErr << "Warning: Skipping \"" << fqInstanceName << "\": " + << "debugging information cannot be retrieved:" + << debugRet.description() << std::endl; + status |= DUMP_BINDERIZED_ERROR; + } + } + for (auto &pair : allPids) { + pid_t serverPid = pair.first; + if (!getReferencedPids(serverPid, &allPids[serverPid])) { + mErr << "Warning: no information for PID " << serverPid + << ", are you root?" << std::endl; + status |= DUMP_BINDERIZED_ERROR; + } + } + for (const auto &fqInstanceName : fqInstanceNames) { + auto it = allDebugInfos.find(fqInstanceName); + if (it == allDebugInfos.end()) { + putEntry({ + .interfaceName = fqInstanceName, + .transport = mode, + .serverPid = NO_PID, + .serverObjectAddress = NO_PTR, + .clientPids = {} + }); + continue; + } + const DebugInfo &info = it->second; + putEntry({ + .interfaceName = fqInstanceName, + .transport = mode, + .serverPid = info.pid, + .serverObjectAddress = info.ptr, + .clientPids = info.pid == NO_PID || info.ptr == NO_PTR + ? Pids{} : allPids[info.pid][info.ptr] + }); + } + + }); + if (!listRet.isOk()) { + mErr << "Error: Failed to list services for " << mode << ": " + << listRet.description() << std::endl; + status |= DUMP_BINDERIZED_ERROR; + } + return status; +} + +Status Lshal::fetch() { + Status status = OK; + auto bManager = ::android::hardware::defaultServiceManager(); + if (bManager == nullptr) { + mErr << "Failed to get defaultServiceManager()!" << std::endl; + status |= NO_BINDERIZED_MANAGER; + } else { + status |= fetchBinderized(bManager); + // Passthrough PIDs are registered to the binderized manager as well. + status |= fetchPassthrough(bManager); + } + + auto pManager = ::android::hardware::getPassthroughServiceManager(); + if (pManager == nullptr) { + mErr << "Failed to get getPassthroughServiceManager()!" << std::endl; + status |= NO_PASSTHROUGH_MANAGER; + } else { + status |= fetchAllLibraries(pManager); + } + return status; +} + +void Lshal::usage() const { + mErr + << "usage: lshal" << std::endl + << " 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 + << " -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 + << " -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl + << " -a, --address: print the server object address column" << std::endl + << " -c, --clients: print the client PIDs, or client cmdlines if -m is set" + << std::endl + << " -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 + << " lshal [-h|--help]" << std::endl + << " -h, --help: show this help information." << std::endl; +} + +Status Lshal::parseArgs(int argc, char **argv) { + static struct option longOptions[] = { + // long options with short alternatives + {"help", no_argument, 0, 'h' }, + {"interface", no_argument, 0, 'i' }, + {"transport", no_argument, 0, 't' }, + {"pid", no_argument, 0, 'p' }, + {"address", no_argument, 0, 'a' }, + {"clients", no_argument, 0, 'c' }, + {"cmdline", no_argument, 0, 'm' }, + + // long options without short alternatives + {"sort", required_argument, 0, 's' }, + { 0, 0, 0, 0 } + }; + + int optionIndex; + int c; + optind = 1; + for (;;) { + // using getopt_long in case we want to add other options in the future + c = getopt_long(argc, argv, "hitpacm", longOptions, &optionIndex); + if (c == -1) { + break; + } + switch (c) { + case 's': { + if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) { + mSortColumn = TableEntry::sortByInterfaceName; + } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) { + mSortColumn = TableEntry::sortByServerPid; + } else { + mErr << "Unrecognized sorting column: " << optarg << std::endl; + usage(); + return USAGE; + } + break; + } + case 'i': { + mSelectedColumns |= ENABLE_INTERFACE_NAME; + break; + } + case 't': { + mSelectedColumns |= ENABLE_TRANSPORT; + break; + } + case 'p': { + mSelectedColumns |= ENABLE_SERVER_PID; + break; + } + case 'a': { + mSelectedColumns |= ENABLE_SERVER_ADDR; + break; + } + case 'c': { + mSelectedColumns |= ENABLE_CLIENT_PIDS; + break; + } + case 'm': { + mEnableCmdlines = true; + break; + } + case 'h': // falls through + default: // see unrecognized options + usage(); + return USAGE; + } + } + + if (mSelectedColumns == 0) { + mSelectedColumns = ENABLE_INTERFACE_NAME + | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS; + } + return OK; +} + +int Lshal::main(int argc, char **argv) { + Status status = parseArgs(argc, argv); + if (status != OK) { + return status; + } + status = fetch(); + postprocess(); + dump(); + return status; +} + +} // namespace lshal +} // namespace android + +int main(int argc, char **argv) { + return ::android::lshal::Lshal{}.main(argc, argv); +} diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h new file mode 100644 index 0000000000..ead99dc068 --- /dev/null +++ b/cmds/lshal/Lshal.h @@ -0,0 +1,89 @@ +/* + * 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_LSHAL_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ + +#include <stdint.h> + +#include <iostream> +#include <string> +#include <vector> + +#include <android/hidl/manager/1.0/IServiceManager.h> + +#include "TableEntry.h" + +namespace android { +namespace lshal { + +enum : unsigned int { + OK = 0, + USAGE = 1 << 0, + NO_BINDERIZED_MANAGER = 1 << 1, + NO_PASSTHROUGH_MANAGER = 1 << 2, + DUMP_BINDERIZED_ERROR = 1 << 3, + DUMP_PASSTHROUGH_ERROR = 1 << 4, + DUMP_ALL_LIBS_ERROR = 1 << 5, +}; +using Status = unsigned int; + +class Lshal { +public: + int main(int argc, char **argv); + +private: + Status parseArgs(int argc, char **argv); + Status fetch(); + void postprocess(); + void dump() const; + void usage() const; + void putEntry(TableEntry &&entry); + Status fetchPassthrough(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); + Status fetchBinderized(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); + Status fetchAllLibraries(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager); + bool getReferencedPids( + pid_t serverPid, std::map<uint64_t, Pids> *objects) const; + void printLine( + const std::string &interfaceName, + const std::string &transport, const std::string &server, + const std::string &serverCmdline, + const std::string &address, const std::string &clients, + const std::string &clientCmdlines) const ; + // 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 + // have died, and the pid is removed from pids. + void removeDeadProcesses(Pids *pids); + + Table mTable{}; + std::ostream &mErr = std::cerr; + std::ostream &mOut = std::cout; + TableEntryCompare mSortColumn = nullptr; + TableEntrySelect mSelectedColumns = 0; + // If true, cmdlines will be printed instead of pid. + bool mEnableCmdlines; + // 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. + std::map<pid_t, std::string> mCmdlines; +}; + + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_LSHAL_H_ diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h new file mode 100644 index 0000000000..4ec3a0ced1 --- /dev/null +++ b/cmds/lshal/TableEntry.h @@ -0,0 +1,69 @@ +/* + * 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_TABLE_ENTRY_H_ +#define FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ + +#include <stdint.h> + +#include <string> +#include <vector> +#include <iostream> + +namespace android { +namespace lshal { + +using Pids = std::vector<int32_t>; + +struct TableEntry { + std::string interfaceName; + std::string transport; + int32_t serverPid; + std::string serverCmdline; + uint64_t serverObjectAddress; + Pids clientPids; + std::vector<std::string> clientCmdlines; + + static bool sortByInterfaceName(const TableEntry &a, const TableEntry &b) { + return a.interfaceName < b.interfaceName; + }; + static bool sortByServerPid(const TableEntry &a, const TableEntry &b) { + return a.serverPid < b.serverPid; + }; +}; + +using Table = std::vector<TableEntry>; +using TableEntryCompare = std::function<bool(const TableEntry &, const TableEntry &)>; + +enum : unsigned int { + ENABLE_INTERFACE_NAME = 1 << 0, + ENABLE_TRANSPORT = 1 << 1, + ENABLE_SERVER_PID = 1 << 2, + ENABLE_SERVER_ADDR = 1 << 3, + ENABLE_CLIENT_PIDS = 1 << 4 +}; + +using TableEntrySelect = unsigned int; + +enum { + NO_PID = -1, + NO_PTR = 0 +}; + +} // namespace lshal +} // namespace android + +#endif // FRAMEWORK_NATIVE_CMDS_LSHAL_TABLE_ENTRY_H_ diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h new file mode 100644 index 0000000000..bf883c0a4c --- /dev/null +++ b/cmds/lshal/Timeout.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#include <condition_variable> +#include <chrono> +#include <functional> +#include <mutex> +#include <thread> + +#include <hidl/Status.h> + +namespace android { +namespace lshal { + +static constexpr std::chrono::milliseconds IPC_CALL_WAIT{500}; + +class BackgroundTaskState { +public: + BackgroundTaskState(){} + void notify() { + std::unique_lock<std::mutex> lock(mMutex); + mFinished = true; + lock.unlock(); + mCondVar.notify_all(); + } + template<class C, class D> + bool wait(std::chrono::time_point<C, D> end) { + std::unique_lock<std::mutex> lock(mMutex); + mCondVar.wait_until(lock, end, [this](){ return this->mFinished; }); + return mFinished; + } +private: + std::mutex mMutex; + std::condition_variable mCondVar; + bool mFinished = false; +}; + +template<class R, class P> +bool timeout(std::chrono::duration<R, P> delay, const std::function<void(void)> &func) { + auto now = std::chrono::system_clock::now(); + BackgroundTaskState state{}; + std::thread t([&state, &func] { + func(); + state.notify(); + }); + t.detach(); + bool success = state.wait(now + delay); + return success; +} + +template<class Function, class I, class... Args> +typename std::result_of<Function(I *, Args...)>::type +timeoutIPC(const sp<I> &interfaceObject, Function &&func, Args &&... args) { + using ::android::hardware::Status; + typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()}; + auto boundFunc = std::bind(std::forward<Function>(func), + interfaceObject.get(), std::forward<Args>(args)...); + bool success = timeout(IPC_CALL_WAIT, [&ret, &boundFunc] { + ret = boundFunc(); + }); + if (!success) { + return Status::fromStatusT(TIMED_OUT); + } + return ret; +} + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/lshal.cpp b/cmds/lshal/lshal.cpp deleted file mode 100644 index 9998a462a3..0000000000 --- a/cmds/lshal/lshal.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * 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. - */ - - -#include <getopt.h> - -#include <map> -#include <fstream> -#include <iomanip> -#include <iostream> -#include <sstream> -#include <regex> - -#include <android-base/parseint.h> -#include <android/hidl/manager/1.0/IServiceManager.h> -#include <hidl/ServiceManagement.h> - -using ::android::sp; -using ::android::hardware::hidl_string; -using ::android::hidl::manager::V1_0::IServiceManager; - -template <typename A, typename B, typename C, typename D, typename E, typename F> -void printColumn(std::stringstream &stream, - const A &a, const B &b, const C &c, const D &d, const E &, const F &f) { - using namespace ::std; - stream << left - << setw(70) << a << "\t" - << setw(20) << b << "\t" - << setw(10) << c << "\t" - << setw(5) << d << "\t" - // TODO(b/34984175): enable selecting columns - // << setw(16) << e << "\t" - << setw(0) << f - << endl; -} - -template <typename A> -std::string join(const A &components, const std::string &separator) { - std::stringstream out; - bool first = true; - for (const auto &component : components) { - if (!first) { - out << separator; - } - out << component; - - first = false; - } - return out.str(); -} - -std::string toHexString(uint64_t t) { - std::ostringstream os; - os << std::hex << std::setfill('0') << std::setw(16) << t; - return os.str(); -} - -std::pair<hidl_string, hidl_string> split(const hidl_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)}; -} - -bool getReferencedPids( - pid_t serverPid, std::map<uint64_t, std::string> *objects) { - - std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid)); - if (!ifs.is_open()) { - return false; - } - - static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+"); - - std::string line; - std::smatch match; - while(getline(ifs, line)) { - if (!std::regex_search(line, match, prefix)) { - // the line doesn't start with the correct prefix - continue; - } - std::string ptrString = "0x" + match.str(2); // use number after c - uint64_t ptr; - if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) { - // Should not reach here, but just be tolerant. - std::cerr << "Could not parse number " << ptrString << std::endl; - continue; - } - const std::string proc = " proc "; - auto pos = line.rfind(proc); - if (pos != std::string::npos) { - (*objects)[ptr] += line.substr(pos + proc.size()); - } - } - return true; -} - -void dumpAllLibraries(std::stringstream &stream, const std::string &mode, - const sp<IServiceManager> &manager) { - using namespace ::std; - using namespace ::android::hardware; - using namespace ::android::hidl::manager::V1_0; - using namespace ::android::hidl::base::V1_0; - auto ret = manager->list([&] (const auto &fqInstanceNames) { - for (const auto &fqInstanceName : fqInstanceNames) { - const auto pair = split(fqInstanceName, '/'); - const auto &serviceName = pair.first; - const auto &instanceName = pair.second; - printColumn(stream, - serviceName, - instanceName, - mode, - "N/A", - "N/A", - "N/A"); - } - }); - if (!ret.isOk()) { - cerr << "Error: Failed to call debugDump on defaultServiceManager(): " - << ret.description() << endl; - } -} - -void dumpPassthrough(std::stringstream &stream, const std::string &mode, - const sp<IServiceManager> &manager) { - using namespace ::std; - using namespace ::android::hardware; - using namespace ::android::hidl::manager::V1_0; - using namespace ::android::hidl::base::V1_0; - auto ret = manager->debugDump([&] (const auto &infos) { - for (const auto &info : infos) { - - printColumn(stream, - info.interfaceName, - info.instanceName, - mode, - info.clientPids.size() == 1 ? std::to_string(info.clientPids[0]) : "N/A", - "N/A", - join(info.clientPids, " ")); - } - }); - if (!ret.isOk()) { - cerr << "Error: Failed to call debugDump on defaultServiceManager(): " - << ret.description() << endl; - } -} - -void dumpBinderized(std::stringstream &stream, const std::string &mode, - const sp<IServiceManager> &manager) { - using namespace ::std; - using namespace ::android::hardware; - using namespace ::android::hidl::manager::V1_0; - using namespace ::android::hidl::base::V1_0; - auto listRet = manager->list([&] (const auto &fqInstanceNames) { - // server pid, .ptr value of binder object, child pids - std::map<std::string, DebugInfo> allDebugInfos; - std::map<pid_t, std::map<uint64_t, std::string>> allPids; - for (const auto &fqInstanceName : fqInstanceNames) { - const auto pair = split(fqInstanceName, '/'); - const auto &serviceName = pair.first; - const auto &instanceName = pair.second; - auto getRet = manager->get(serviceName, instanceName); - if (!getRet.isOk()) { - cerr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "cannot be fetched from service manager:" - << getRet.description() << endl; - continue; - } - sp<IBase> service = getRet; - if (service == nullptr) { - cerr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "cannot be fetched from service manager (null)"; - continue; - } - auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) { - allDebugInfos[fqInstanceName] = debugInfo; - if (debugInfo.pid >= 0) { - allPids[static_cast<pid_t>(debugInfo.pid)].clear(); - } - }); - if (!debugRet.isOk()) { - cerr << "Warning: Skipping \"" << fqInstanceName << "\": " - << "debugging information cannot be retrieved:" - << debugRet.description() << endl; - } - } - for (auto &pair : allPids) { - pid_t serverPid = pair.first; - if (!getReferencedPids(serverPid, &allPids[serverPid])) { - std::cerr << "Warning: no information for PID " << serverPid - << ", are you root?" << std::endl; - } - } - for (const auto &fqInstanceName : fqInstanceNames) { - const auto pair = split(fqInstanceName, '/'); - const auto &serviceName = pair.first; - const auto &instanceName = pair.second; - auto it = allDebugInfos.find(fqInstanceName); - if (it == allDebugInfos.end()) { - printColumn(stream, - serviceName, - instanceName, - mode, - "N/A", - "N/A", - "" - ); - continue; - } - const DebugInfo &info = it->second; - printColumn(stream, - serviceName, - instanceName, - mode, - info.pid < 0 ? "N/A" : std::to_string(info.pid), - info.ptr == 0 ? "N/A" : toHexString(info.ptr), - info.pid < 0 || info.ptr == 0 ? "" : allPids[info.pid][info.ptr] - ); - } - - }); - if (!listRet.isOk()) { - cerr << "Error: Failed to list services for " << mode << ": " - << listRet.description() << endl; - } -} - -int dump() { - using namespace ::std; - using namespace ::android::hardware; - - std::stringstream stream; - - stream << "All services:" << endl; - stream << left; - printColumn(stream, "Interface", "Instance", "Transport", "Server", "PTR", "Clients"); - - auto bManager = defaultServiceManager(); - if (bManager == nullptr) { - cerr << "Failed to get defaultServiceManager()!" << endl; - } else { - dumpBinderized(stream, "hwbinder", bManager); - // Passthrough PIDs are registered to the binderized manager as well. - dumpPassthrough(stream, "passthrough", bManager); - } - - auto pManager = getPassthroughServiceManager(); - if (pManager == nullptr) { - cerr << "Failed to get getPassthroughServiceManager()!" << endl; - } else { - dumpAllLibraries(stream, "passthrough", pManager); - } - - cout << stream.rdbuf(); - return 0; -} - -int usage() { - using namespace ::std; - cerr - << "usage: lshal" << endl - << " To dump all hals." << endl - << "or:" << endl - << " lshal [-h|--help]" << endl - << " -h, --help: show this help information." << endl; - return -1; -} - -int main(int argc, char **argv) { - static struct option longOptions[] = { - {"help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } - }; - - int optionIndex; - int c; - optind = 1; - for (;;) { - // using getopt_long in case we want to add other options in the future - c = getopt_long(argc, argv, "h", longOptions, &optionIndex); - if (c == -1) { - break; - } - switch (c) { - case 'h': // falls through - default: // see unrecognized options - return usage(); - } - } - return dump(); - -} |