blob: d213fd314ff725412d4af0e357622199f7e64cea [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
32using ::android::hardware::hidl_string;
33using ::android::hidl::manager::V1_0::IServiceManager;
34
35namespace android {
36namespace lshal {
37
Yifan Hongb0dde932017-02-10 17:49:58 -080038template <typename A>
39std::string join(const A &components, const std::string &separator) {
40 std::stringstream out;
41 bool first = true;
42 for (const auto &component : components) {
43 if (!first) {
44 out << separator;
45 }
46 out << component;
47
48 first = false;
49 }
50 return out.str();
51}
52
53static std::string toHexString(uint64_t t) {
54 std::ostringstream os;
55 os << std::hex << std::setfill('0') << std::setw(16) << t;
56 return os.str();
57}
58
59static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
60 const char *pos = strchr(s.c_str(), c);
61 if (pos == nullptr) {
62 return {s, {}};
63 }
64 return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
65}
66
67static std::vector<std::string> split(const std::string &s, char c) {
68 std::vector<std::string> components{};
69 size_t startPos = 0;
70 size_t matchPos;
71 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
72 components.push_back(s.substr(startPos, matchPos - startPos));
73 startPos = matchPos + 1;
74 }
75
76 if (startPos <= s.length()) {
77 components.push_back(s.substr(startPos));
78 }
79 return components;
80}
81
82bool Lshal::getReferencedPids(
83 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
84
85 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
86 if (!ifs.is_open()) {
87 return false;
88 }
89
90 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
91
92 std::string line;
93 std::smatch match;
94 while(getline(ifs, line)) {
95 if (!std::regex_search(line, match, prefix)) {
96 // the line doesn't start with the correct prefix
97 continue;
98 }
99 std::string ptrString = "0x" + match.str(2); // use number after c
100 uint64_t ptr;
101 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
102 // Should not reach here, but just be tolerant.
103 mErr << "Could not parse number " << ptrString << std::endl;
104 continue;
105 }
106 const std::string proc = " proc ";
107 auto pos = line.rfind(proc);
108 if (pos != std::string::npos) {
109 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
110 int32_t pid;
111 if (!::android::base::ParseInt(pidStr, &pid)) {
112 mErr << "Could not parse number " << pidStr << std::endl;
113 continue;
114 }
115 (*objects)[ptr].push_back(pid);
116 }
117 }
118 }
119 return true;
120}
121
Yifan Hong38d53e02017-02-13 17:51:59 -0800122void Lshal::postprocess() {
123 if (mSortColumn) {
124 std::sort(mTable.begin(), mTable.end(), mSortColumn);
125 }
126}
127
128void Lshal::printLine(
129 const std::string &interfaceName,
130 const std::string &transport, const std::string &server,
131 const std::string &address, const std::string &clients) const {
132 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
133 mOut << std::setw(80) << interfaceName << "\t";
134 if (mSelectedColumns & ENABLE_TRANSPORT)
135 mOut << std::setw(10) << transport << "\t";
136 if (mSelectedColumns & ENABLE_SERVER_PID)
137 mOut << std::setw(5) << server << "\t";
138 if (mSelectedColumns & ENABLE_SERVER_ADDR)
139 mOut << std::setw(16) << address << "\t";
140 if (mSelectedColumns & ENABLE_CLIENT_PIDS)
141 mOut << std::setw(0) << clients;
142 mOut << std::endl;
143}
144
Yifan Hongb0dde932017-02-10 17:49:58 -0800145void Lshal::dump() const {
146 mOut << "All services:" << std::endl;
Yifan Hong38d53e02017-02-13 17:51:59 -0800147 mOut << std::left;
148 printLine("Interface", "Transport", "Server", "PTR", "Clients");
Yifan Hongb0dde932017-02-10 17:49:58 -0800149 for (const auto &entry : mTable) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800150 printLine(entry.interfaceName,
Yifan Hongb0dde932017-02-10 17:49:58 -0800151 entry.transport,
152 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
153 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
154 join(entry.clientPids, " "));
155 }
156}
157
158void Lshal::putEntry(TableEntry &&entry) {
159 mTable.push_back(std::forward<TableEntry>(entry));
160}
161
162Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
163 using namespace ::android::hardware;
164 using namespace ::android::hidl::manager::V1_0;
165 using namespace ::android::hidl::base::V1_0;
166 auto ret = manager->list([&] (const auto &fqInstanceNames) {
167 for (const auto &fqInstanceName : fqInstanceNames) {
168 putEntry({
169 .interfaceName = fqInstanceName,
170 .transport = "passthrough",
171 .serverPid = NO_PID,
172 .serverObjectAddress = NO_PTR,
173 .clientPids = {}
174 });
175 }
176 });
177 if (!ret.isOk()) {
178 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
179 << ret.description() << std::endl;
180 return DUMP_ALL_LIBS_ERROR;
181 }
182 return OK;
183}
184
185Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
186 using namespace ::android::hardware;
187 using namespace ::android::hidl::manager::V1_0;
188 using namespace ::android::hidl::base::V1_0;
189 auto ret = manager->debugDump([&] (const auto &infos) {
190 for (const auto &info : infos) {
191 putEntry({
192 .interfaceName =
193 std::string{info.interfaceName.c_str()} + "/" +
194 std::string{info.instanceName.c_str()},
195 .transport = "passthrough",
196 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
197 .serverObjectAddress = NO_PTR,
198 .clientPids = info.clientPids
199 });
200 }
201 });
202 if (!ret.isOk()) {
203 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
204 << ret.description() << std::endl;
205 return DUMP_PASSTHROUGH_ERROR;
206 }
207 return OK;
208}
209
210Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
211 using namespace ::std;
212 using namespace ::android::hardware;
213 using namespace ::android::hidl::manager::V1_0;
214 using namespace ::android::hidl::base::V1_0;
215 const std::string mode = "hwbinder";
216 Status status = OK;
217 auto listRet = manager->list([&] (const auto &fqInstanceNames) {
218 // server pid, .ptr value of binder object, child pids
219 std::map<std::string, DebugInfo> allDebugInfos;
220 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
221 for (const auto &fqInstanceName : fqInstanceNames) {
222 const auto pair = split(fqInstanceName, '/');
223 const auto &serviceName = pair.first;
224 const auto &instanceName = pair.second;
225 auto getRet = manager->get(serviceName, instanceName);
226 if (!getRet.isOk()) {
227 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
228 << "cannot be fetched from service manager:"
229 << getRet.description() << std::endl;
230 status |= DUMP_BINDERIZED_ERROR;
231 continue;
232 }
233 sp<IBase> service = getRet;
234 if (service == nullptr) {
235 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
236 << "cannot be fetched from service manager (null)";
237 status |= DUMP_BINDERIZED_ERROR;
238 continue;
239 }
240 auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) {
241 allDebugInfos[fqInstanceName] = debugInfo;
242 if (debugInfo.pid >= 0) {
243 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
244 }
245 });
246 if (!debugRet.isOk()) {
247 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
248 << "debugging information cannot be retrieved:"
249 << debugRet.description() << std::endl;
250 status |= DUMP_BINDERIZED_ERROR;
251 }
252 }
253 for (auto &pair : allPids) {
254 pid_t serverPid = pair.first;
255 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
256 mErr << "Warning: no information for PID " << serverPid
257 << ", are you root?" << std::endl;
258 status |= DUMP_BINDERIZED_ERROR;
259 }
260 }
261 for (const auto &fqInstanceName : fqInstanceNames) {
262 auto it = allDebugInfos.find(fqInstanceName);
263 if (it == allDebugInfos.end()) {
264 putEntry({
265 .interfaceName = fqInstanceName,
266 .transport = mode,
267 .serverPid = NO_PID,
268 .serverObjectAddress = NO_PTR,
269 .clientPids = {}
270 });
271 continue;
272 }
273 const DebugInfo &info = it->second;
274 putEntry({
275 .interfaceName = fqInstanceName,
276 .transport = mode,
277 .serverPid = info.pid,
278 .serverObjectAddress = info.ptr,
279 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
280 ? Pids{} : allPids[info.pid][info.ptr]
281 });
282 }
283
284 });
285 if (!listRet.isOk()) {
286 mErr << "Error: Failed to list services for " << mode << ": "
287 << listRet.description() << std::endl;
288 status |= DUMP_BINDERIZED_ERROR;
289 }
290 return status;
291}
292
293Status Lshal::fetch() {
294 Status status = OK;
295 auto bManager = ::android::hardware::defaultServiceManager();
296 if (bManager == nullptr) {
297 mErr << "Failed to get defaultServiceManager()!" << std::endl;
298 status |= NO_BINDERIZED_MANAGER;
299 } else {
300 status |= fetchBinderized(bManager);
301 // Passthrough PIDs are registered to the binderized manager as well.
302 status |= fetchPassthrough(bManager);
303 }
304
305 auto pManager = ::android::hardware::getPassthroughServiceManager();
306 if (pManager == nullptr) {
307 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
308 status |= NO_PASSTHROUGH_MANAGER;
309 } else {
310 status |= fetchAllLibraries(pManager);
311 }
312 return status;
313}
314
315void Lshal::usage() const {
316 mErr
317 << "usage: lshal" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800318 << " Dump all hals with default ordering and columns [-itpc]." << std::endl
319 << " lshal [--interface|-i] [--transport|-t]" << std::endl
320 << " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
321 << " [--sort={interface|i|pid|p}]" << std::endl
322 << " -i, --interface: print the interface name column" << std::endl
323 << " -n, --instance: print the instance name column" << std::endl
324 << " -t, --transport: print the transport mode column" << std::endl
325 << " -p, --pid: print the server PID column" << std::endl
326 << " -a, --address: print the server object address column" << std::endl
327 << " -c, --clients: print the client PIDs column" << std::endl
328 << " --sort=i, --sort=interface: sort by interface name" << std::endl
329 << " --sort=p, --sort=pid: sort by server pid" << std::endl
Yifan Hongb0dde932017-02-10 17:49:58 -0800330 << " lshal [-h|--help]" << std::endl
331 << " -h, --help: show this help information." << std::endl;
332}
333
334Status Lshal::parseArgs(int argc, char **argv) {
335 static struct option longOptions[] = {
Yifan Hong38d53e02017-02-13 17:51:59 -0800336 // long options with short alternatives
337 {"help", no_argument, 0, 'h' },
338 {"interface", no_argument, 0, 'i' },
339 {"transport", no_argument, 0, 't' },
340 {"pid", no_argument, 0, 'p' },
341 {"address", no_argument, 0, 'a' },
342 {"clients", no_argument, 0, 'c' },
343
344 // long options without short alternatives
345 {"sort", required_argument, 0, 's' },
346 { 0, 0, 0, 0 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800347 };
348
349 int optionIndex;
350 int c;
351 optind = 1;
352 for (;;) {
353 // using getopt_long in case we want to add other options in the future
Yifan Hong38d53e02017-02-13 17:51:59 -0800354 c = getopt_long(argc, argv, "hitpac", longOptions, &optionIndex);
Yifan Hongb0dde932017-02-10 17:49:58 -0800355 if (c == -1) {
356 break;
357 }
358 switch (c) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800359 case 's': {
360 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
361 mSortColumn = TableEntry::sortByInterfaceName;
362 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
363 mSortColumn = TableEntry::sortByServerPid;
364 } else {
365 mErr << "Unrecognized sorting column: " << optarg << std::endl;
366 usage();
367 return USAGE;
368 }
369 break;
370 }
371 case 'i': {
372 mSelectedColumns |= ENABLE_INTERFACE_NAME;
373 break;
374 }
375 case 't': {
376 mSelectedColumns |= ENABLE_TRANSPORT;
377 break;
378 }
379 case 'p': {
380 mSelectedColumns |= ENABLE_SERVER_PID;
381 break;
382 }
383 case 'a': {
384 mSelectedColumns |= ENABLE_SERVER_ADDR;
385 break;
386 }
387 case 'c': {
388 mSelectedColumns |= ENABLE_CLIENT_PIDS;
389 break;
390 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800391 case 'h': // falls through
392 default: // see unrecognized options
393 usage();
394 return USAGE;
395 }
396 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800397
398 if (mSelectedColumns == 0) {
399 mSelectedColumns = ENABLE_INTERFACE_NAME
400 | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
401 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800402 return OK;
403}
404
405int Lshal::main(int argc, char **argv) {
406 Status status = parseArgs(argc, argv);
407 if (status != OK) {
408 return status;
409 }
410 status = fetch();
Yifan Hong38d53e02017-02-13 17:51:59 -0800411 postprocess();
Yifan Hongb0dde932017-02-10 17:49:58 -0800412 dump();
413 return status;
414}
415
416} // namespace lshal
417} // namespace android
418
419int main(int argc, char **argv) {
420 return ::android::lshal::Lshal{}.main(argc, argv);
421}