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