blob: 6fd9b2136816b5060f7bc6bcc7dd645442e3e5a4 [file] [log] [blame]
Yifan Hongb0dde932017-02-10 17:49:58 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Lshal.h"
18
19#include <getopt.h>
20
21#include <fstream>
22#include <iomanip>
23#include <iostream>
24#include <map>
25#include <sstream>
26#include <regex>
27
28#include <android-base/parseint.h>
29#include <android/hidl/manager/1.0/IServiceManager.h>
30#include <hidl/ServiceManagement.h>
31
Yifan Honge2dadf02017-02-14 15:43:31 -080032#include "Timeout.h"
33
Yifan Hongb0dde932017-02-10 17:49:58 -080034using ::android::hardware::hidl_string;
35using ::android::hidl::manager::V1_0::IServiceManager;
36
37namespace android {
38namespace lshal {
39
Yifan Hongb0dde932017-02-10 17:49:58 -080040template <typename A>
41std::string join(const A &components, const std::string &separator) {
42 std::stringstream out;
43 bool first = true;
44 for (const auto &component : components) {
45 if (!first) {
46 out << separator;
47 }
48 out << component;
49
50 first = false;
51 }
52 return out.str();
53}
54
55static std::string toHexString(uint64_t t) {
56 std::ostringstream os;
57 os << std::hex << std::setfill('0') << std::setw(16) << t;
58 return os.str();
59}
60
61static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
62 const char *pos = strchr(s.c_str(), c);
63 if (pos == nullptr) {
64 return {s, {}};
65 }
66 return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
67}
68
69static std::vector<std::string> split(const std::string &s, char c) {
70 std::vector<std::string> components{};
71 size_t startPos = 0;
72 size_t matchPos;
73 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
74 components.push_back(s.substr(startPos, matchPos - startPos));
75 startPos = matchPos + 1;
76 }
77
78 if (startPos <= s.length()) {
79 components.push_back(s.substr(startPos));
80 }
81 return components;
82}
83
Yifan Hongae09a3d2017-02-14 17:33:50 -080084std::string getCmdline(pid_t pid) {
85 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
86 std::string cmdline;
87 if (!ifs.is_open()) {
88 return "";
89 }
90 ifs >> cmdline;
91 return cmdline;
92}
93
94const std::string &Lshal::getCmdline(pid_t pid) {
95 auto pair = mCmdlines.find(pid);
96 if (pair != mCmdlines.end()) {
97 return pair->second;
98 }
99 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
100 return mCmdlines[pid];
101}
102
103void Lshal::removeDeadProcesses(Pids *pids) {
104 static const pid_t myPid = getpid();
105 std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
106 return pid == myPid || this->getCmdline(pid).empty();
107 });
108}
109
Yifan Hongb0dde932017-02-10 17:49:58 -0800110bool Lshal::getReferencedPids(
111 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
112
113 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
114 if (!ifs.is_open()) {
115 return false;
116 }
117
118 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
119
120 std::string line;
121 std::smatch match;
122 while(getline(ifs, line)) {
123 if (!std::regex_search(line, match, prefix)) {
124 // the line doesn't start with the correct prefix
125 continue;
126 }
127 std::string ptrString = "0x" + match.str(2); // use number after c
128 uint64_t ptr;
129 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
130 // Should not reach here, but just be tolerant.
131 mErr << "Could not parse number " << ptrString << std::endl;
132 continue;
133 }
134 const std::string proc = " proc ";
135 auto pos = line.rfind(proc);
136 if (pos != std::string::npos) {
137 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
138 int32_t pid;
139 if (!::android::base::ParseInt(pidStr, &pid)) {
140 mErr << "Could not parse number " << pidStr << std::endl;
141 continue;
142 }
143 (*objects)[ptr].push_back(pid);
144 }
145 }
146 }
147 return true;
148}
149
Yifan Hong38d53e02017-02-13 17:51:59 -0800150void Lshal::postprocess() {
151 if (mSortColumn) {
152 std::sort(mTable.begin(), mTable.end(), mSortColumn);
153 }
Yifan Hongae09a3d2017-02-14 17:33:50 -0800154 for (TableEntry &entry : mTable) {
155 entry.serverCmdline = getCmdline(entry.serverPid);
156 removeDeadProcesses(&entry.clientPids);
157 for (auto pid : entry.clientPids) {
158 entry.clientCmdlines.push_back(this->getCmdline(pid));
159 }
160 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800161}
162
163void Lshal::printLine(
164 const std::string &interfaceName,
165 const std::string &transport, const std::string &server,
Yifan Hongae09a3d2017-02-14 17:33:50 -0800166 const std::string &serverCmdline,
167 const std::string &address, const std::string &clients,
168 const std::string &clientCmdlines) const {
Yifan Hong38d53e02017-02-13 17:51:59 -0800169 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
170 mOut << std::setw(80) << interfaceName << "\t";
171 if (mSelectedColumns & ENABLE_TRANSPORT)
172 mOut << std::setw(10) << transport << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800173 if (mSelectedColumns & ENABLE_SERVER_PID) {
174 if (mEnableCmdlines) {
175 mOut << std::setw(15) << serverCmdline << "\t";
176 } else {
177 mOut << std::setw(5) << server << "\t";
178 }
179 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800180 if (mSelectedColumns & ENABLE_SERVER_ADDR)
181 mOut << std::setw(16) << address << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800182 if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
183 if (mEnableCmdlines) {
184 mOut << std::setw(0) << clientCmdlines;
185 } else {
186 mOut << std::setw(0) << clients;
187 }
188 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800189 mOut << std::endl;
190}
191
Yifan Hongb0dde932017-02-10 17:49:58 -0800192void Lshal::dump() const {
193 mOut << "All services:" << std::endl;
Yifan Hong38d53e02017-02-13 17:51:59 -0800194 mOut << std::left;
Yifan Hongae09a3d2017-02-14 17:33:50 -0800195 printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
Yifan Hongb0dde932017-02-10 17:49:58 -0800196 for (const auto &entry : mTable) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800197 printLine(entry.interfaceName,
Yifan Hongb0dde932017-02-10 17:49:58 -0800198 entry.transport,
199 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
Yifan Hongae09a3d2017-02-14 17:33:50 -0800200 entry.serverCmdline,
Yifan Hongb0dde932017-02-10 17:49:58 -0800201 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
Yifan Hongae09a3d2017-02-14 17:33:50 -0800202 join(entry.clientPids, " "),
203 join(entry.clientCmdlines, ";"));
Yifan Hongb0dde932017-02-10 17:49:58 -0800204 }
205}
206
207void Lshal::putEntry(TableEntry &&entry) {
208 mTable.push_back(std::forward<TableEntry>(entry));
209}
210
211Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
212 using namespace ::android::hardware;
213 using namespace ::android::hidl::manager::V1_0;
214 using namespace ::android::hidl::base::V1_0;
Yifan Honge2dadf02017-02-14 15:43:31 -0800215 auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800216 for (const auto &fqInstanceName : fqInstanceNames) {
217 putEntry({
218 .interfaceName = fqInstanceName,
219 .transport = "passthrough",
220 .serverPid = NO_PID,
221 .serverObjectAddress = NO_PTR,
222 .clientPids = {}
223 });
224 }
225 });
226 if (!ret.isOk()) {
227 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
228 << ret.description() << std::endl;
229 return DUMP_ALL_LIBS_ERROR;
230 }
231 return OK;
232}
233
234Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
235 using namespace ::android::hardware;
236 using namespace ::android::hidl::manager::V1_0;
237 using namespace ::android::hidl::base::V1_0;
Yifan Honge2dadf02017-02-14 15:43:31 -0800238 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800239 for (const auto &info : infos) {
240 putEntry({
241 .interfaceName =
242 std::string{info.interfaceName.c_str()} + "/" +
243 std::string{info.instanceName.c_str()},
244 .transport = "passthrough",
245 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
246 .serverObjectAddress = NO_PTR,
247 .clientPids = info.clientPids
248 });
249 }
250 });
251 if (!ret.isOk()) {
252 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
253 << ret.description() << std::endl;
254 return DUMP_PASSTHROUGH_ERROR;
255 }
256 return OK;
257}
258
259Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
260 using namespace ::std;
261 using namespace ::android::hardware;
262 using namespace ::android::hidl::manager::V1_0;
263 using namespace ::android::hidl::base::V1_0;
264 const std::string mode = "hwbinder";
265 Status status = OK;
Yifan Honge2dadf02017-02-14 15:43:31 -0800266 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800267 // server pid, .ptr value of binder object, child pids
268 std::map<std::string, DebugInfo> allDebugInfos;
269 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
270 for (const auto &fqInstanceName : fqInstanceNames) {
271 const auto pair = split(fqInstanceName, '/');
272 const auto &serviceName = pair.first;
273 const auto &instanceName = pair.second;
Yifan Honge2dadf02017-02-14 15:43:31 -0800274 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
Yifan Hongb0dde932017-02-10 17:49:58 -0800275 if (!getRet.isOk()) {
276 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
277 << "cannot be fetched from service manager:"
278 << getRet.description() << std::endl;
279 status |= DUMP_BINDERIZED_ERROR;
280 continue;
281 }
282 sp<IBase> service = getRet;
283 if (service == nullptr) {
284 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
285 << "cannot be fetched from service manager (null)";
286 status |= DUMP_BINDERIZED_ERROR;
287 continue;
288 }
Yifan Honge2dadf02017-02-14 15:43:31 -0800289 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800290 allDebugInfos[fqInstanceName] = debugInfo;
291 if (debugInfo.pid >= 0) {
292 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
293 }
294 });
295 if (!debugRet.isOk()) {
296 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
297 << "debugging information cannot be retrieved:"
298 << debugRet.description() << std::endl;
299 status |= DUMP_BINDERIZED_ERROR;
300 }
301 }
302 for (auto &pair : allPids) {
303 pid_t serverPid = pair.first;
304 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
305 mErr << "Warning: no information for PID " << serverPid
306 << ", are you root?" << std::endl;
307 status |= DUMP_BINDERIZED_ERROR;
308 }
309 }
310 for (const auto &fqInstanceName : fqInstanceNames) {
311 auto it = allDebugInfos.find(fqInstanceName);
312 if (it == allDebugInfos.end()) {
313 putEntry({
314 .interfaceName = fqInstanceName,
315 .transport = mode,
316 .serverPid = NO_PID,
317 .serverObjectAddress = NO_PTR,
318 .clientPids = {}
319 });
320 continue;
321 }
322 const DebugInfo &info = it->second;
323 putEntry({
324 .interfaceName = fqInstanceName,
325 .transport = mode,
326 .serverPid = info.pid,
327 .serverObjectAddress = info.ptr,
328 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
329 ? Pids{} : allPids[info.pid][info.ptr]
330 });
331 }
332
333 });
334 if (!listRet.isOk()) {
335 mErr << "Error: Failed to list services for " << mode << ": "
336 << listRet.description() << std::endl;
337 status |= DUMP_BINDERIZED_ERROR;
338 }
339 return status;
340}
341
342Status Lshal::fetch() {
343 Status status = OK;
344 auto bManager = ::android::hardware::defaultServiceManager();
345 if (bManager == nullptr) {
346 mErr << "Failed to get defaultServiceManager()!" << std::endl;
347 status |= NO_BINDERIZED_MANAGER;
348 } else {
349 status |= fetchBinderized(bManager);
350 // Passthrough PIDs are registered to the binderized manager as well.
351 status |= fetchPassthrough(bManager);
352 }
353
354 auto pManager = ::android::hardware::getPassthroughServiceManager();
355 if (pManager == nullptr) {
356 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
357 status |= NO_PASSTHROUGH_MANAGER;
358 } else {
359 status |= fetchAllLibraries(pManager);
360 }
361 return status;
362}
363
364void Lshal::usage() const {
365 mErr
366 << "usage: lshal" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800367 << " Dump all hals with default ordering and columns [-itpc]." << std::endl
368 << " lshal [--interface|-i] [--transport|-t]" << std::endl
369 << " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
370 << " [--sort={interface|i|pid|p}]" << std::endl
371 << " -i, --interface: print the interface name column" << std::endl
372 << " -n, --instance: print the instance name column" << std::endl
373 << " -t, --transport: print the transport mode column" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800374 << " -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800375 << " -a, --address: print the server object address column" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800376 << " -c, --clients: print the client PIDs, or client cmdlines if -m is set"
377 << std::endl
378 << " -m, --cmdline: print cmdline instead of PIDs" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800379 << " --sort=i, --sort=interface: sort by interface name" << std::endl
380 << " --sort=p, --sort=pid: sort by server pid" << std::endl
Yifan Hongb0dde932017-02-10 17:49:58 -0800381 << " lshal [-h|--help]" << std::endl
382 << " -h, --help: show this help information." << std::endl;
383}
384
385Status Lshal::parseArgs(int argc, char **argv) {
386 static struct option longOptions[] = {
Yifan Hong38d53e02017-02-13 17:51:59 -0800387 // long options with short alternatives
388 {"help", no_argument, 0, 'h' },
389 {"interface", no_argument, 0, 'i' },
390 {"transport", no_argument, 0, 't' },
391 {"pid", no_argument, 0, 'p' },
392 {"address", no_argument, 0, 'a' },
393 {"clients", no_argument, 0, 'c' },
Yifan Hongae09a3d2017-02-14 17:33:50 -0800394 {"cmdline", no_argument, 0, 'm' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800395
396 // long options without short alternatives
397 {"sort", required_argument, 0, 's' },
398 { 0, 0, 0, 0 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800399 };
400
401 int optionIndex;
402 int c;
403 optind = 1;
404 for (;;) {
405 // using getopt_long in case we want to add other options in the future
Yifan Hongae09a3d2017-02-14 17:33:50 -0800406 c = getopt_long(argc, argv, "hitpacm", longOptions, &optionIndex);
Yifan Hongb0dde932017-02-10 17:49:58 -0800407 if (c == -1) {
408 break;
409 }
410 switch (c) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800411 case 's': {
412 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
413 mSortColumn = TableEntry::sortByInterfaceName;
414 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
415 mSortColumn = TableEntry::sortByServerPid;
416 } else {
417 mErr << "Unrecognized sorting column: " << optarg << std::endl;
418 usage();
419 return USAGE;
420 }
421 break;
422 }
423 case 'i': {
424 mSelectedColumns |= ENABLE_INTERFACE_NAME;
425 break;
426 }
427 case 't': {
428 mSelectedColumns |= ENABLE_TRANSPORT;
429 break;
430 }
431 case 'p': {
432 mSelectedColumns |= ENABLE_SERVER_PID;
433 break;
434 }
435 case 'a': {
436 mSelectedColumns |= ENABLE_SERVER_ADDR;
437 break;
438 }
439 case 'c': {
440 mSelectedColumns |= ENABLE_CLIENT_PIDS;
441 break;
442 }
Yifan Hongae09a3d2017-02-14 17:33:50 -0800443 case 'm': {
444 mEnableCmdlines = true;
445 break;
446 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800447 case 'h': // falls through
448 default: // see unrecognized options
449 usage();
450 return USAGE;
451 }
452 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800453
454 if (mSelectedColumns == 0) {
455 mSelectedColumns = ENABLE_INTERFACE_NAME
456 | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
457 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800458 return OK;
459}
460
461int Lshal::main(int argc, char **argv) {
462 Status status = parseArgs(argc, argv);
463 if (status != OK) {
464 return status;
465 }
466 status = fetch();
Yifan Hong38d53e02017-02-13 17:51:59 -0800467 postprocess();
Yifan Hongb0dde932017-02-10 17:49:58 -0800468 dump();
469 return status;
470}
471
Yifan Honga57dffb2017-02-21 14:59:00 -0800472void signalHandler(int sig) {
473 if (sig == SIGINT) {
474 int retVal;
475 pthread_exit(&retVal);
476 }
477}
478
Yifan Hongb0dde932017-02-10 17:49:58 -0800479} // namespace lshal
480} // namespace android
481
482int main(int argc, char **argv) {
Yifan Honga57dffb2017-02-21 14:59:00 -0800483 signal(SIGINT, ::android::lshal::signalHandler);
Yifan Hongb0dde932017-02-10 17:49:58 -0800484 return ::android::lshal::Lshal{}.main(argc, argv);
485}