blob: 2fc37158890d5abf09879f3e0f67c57423ea4bb0 [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
Andreas Huber28d35912017-03-24 13:14:11 -070028#include <android-base/logging.h>
Yifan Hongb0dde932017-02-10 17:49:58 -080029#include <android-base/parseint.h>
30#include <android/hidl/manager/1.0/IServiceManager.h>
31#include <hidl/ServiceManagement.h>
Yifan Hong4b865492017-02-28 19:38:24 -080032#include <hidl-util/FQName.h>
Andreas Huber28d35912017-03-24 13:14:11 -070033#include <private/android_filesystem_config.h>
34#include <sys/stat.h>
Yifan Hong4b865492017-02-28 19:38:24 -080035#include <vintf/HalManifest.h>
36#include <vintf/parse_xml.h>
Yifan Hongb0dde932017-02-10 17:49:58 -080037
Andreas Huber28d35912017-03-24 13:14:11 -070038#include "PipeRelay.h"
Yifan Honge2dadf02017-02-14 15:43:31 -080039#include "Timeout.h"
40
Yifan Hongb0dde932017-02-10 17:49:58 -080041using ::android::hardware::hidl_string;
42using ::android::hidl::manager::V1_0::IServiceManager;
43
44namespace android {
45namespace lshal {
46
Yifan Hongb0dde932017-02-10 17:49:58 -080047template <typename A>
48std::string join(const A &components, const std::string &separator) {
49 std::stringstream out;
50 bool first = true;
51 for (const auto &component : components) {
52 if (!first) {
53 out << separator;
54 }
55 out << component;
56
57 first = false;
58 }
59 return out.str();
60}
61
62static std::string toHexString(uint64_t t) {
63 std::ostringstream os;
64 os << std::hex << std::setfill('0') << std::setw(16) << t;
65 return os.str();
66}
67
Yifan Hong4b865492017-02-28 19:38:24 -080068template<typename String>
69static std::pair<String, String> splitFirst(const String &s, char c) {
Yifan Hongb0dde932017-02-10 17:49:58 -080070 const char *pos = strchr(s.c_str(), c);
71 if (pos == nullptr) {
72 return {s, {}};
73 }
Yifan Hong4b865492017-02-28 19:38:24 -080074 return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
Yifan Hongb0dde932017-02-10 17:49:58 -080075}
76
77static std::vector<std::string> split(const std::string &s, char c) {
78 std::vector<std::string> components{};
79 size_t startPos = 0;
80 size_t matchPos;
81 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
82 components.push_back(s.substr(startPos, matchPos - startPos));
83 startPos = matchPos + 1;
84 }
85
86 if (startPos <= s.length()) {
87 components.push_back(s.substr(startPos));
88 }
89 return components;
90}
91
Yifan Hong4b865492017-02-28 19:38:24 -080092static void replaceAll(std::string *s, char from, char to) {
93 for (size_t i = 0; i < s->size(); ++i) {
94 if (s->at(i) == from) {
95 s->at(i) = to;
96 }
97 }
98}
99
Yifan Hongae09a3d2017-02-14 17:33:50 -0800100std::string getCmdline(pid_t pid) {
101 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
102 std::string cmdline;
103 if (!ifs.is_open()) {
104 return "";
105 }
106 ifs >> cmdline;
107 return cmdline;
108}
109
110const std::string &Lshal::getCmdline(pid_t pid) {
111 auto pair = mCmdlines.find(pid);
112 if (pair != mCmdlines.end()) {
113 return pair->second;
114 }
115 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
116 return mCmdlines[pid];
117}
118
119void Lshal::removeDeadProcesses(Pids *pids) {
120 static const pid_t myPid = getpid();
121 std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
122 return pid == myPid || this->getCmdline(pid).empty();
123 });
124}
125
Yifan Hongb0dde932017-02-10 17:49:58 -0800126bool Lshal::getReferencedPids(
127 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
128
129 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
130 if (!ifs.is_open()) {
131 return false;
132 }
133
134 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
135
136 std::string line;
137 std::smatch match;
138 while(getline(ifs, line)) {
139 if (!std::regex_search(line, match, prefix)) {
140 // the line doesn't start with the correct prefix
141 continue;
142 }
143 std::string ptrString = "0x" + match.str(2); // use number after c
144 uint64_t ptr;
145 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
146 // Should not reach here, but just be tolerant.
147 mErr << "Could not parse number " << ptrString << std::endl;
148 continue;
149 }
150 const std::string proc = " proc ";
151 auto pos = line.rfind(proc);
152 if (pos != std::string::npos) {
153 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
154 int32_t pid;
155 if (!::android::base::ParseInt(pidStr, &pid)) {
156 mErr << "Could not parse number " << pidStr << std::endl;
157 continue;
158 }
159 (*objects)[ptr].push_back(pid);
160 }
161 }
162 }
163 return true;
164}
165
Yifan Hong9ee841f2017-04-18 14:19:40 -0700166// Must process hwbinder services first, then passthrough services.
Yifan Honga3b87092017-03-02 19:19:29 -0800167void Lshal::forEachTable(const std::function<void(Table &)> &f) {
Yifan Hong8ab3bee2017-03-08 14:01:11 -0800168 f(mServicesTable);
169 f(mPassthroughRefTable);
170 f(mImplementationsTable);
Yifan Honga3b87092017-03-02 19:19:29 -0800171}
172void Lshal::forEachTable(const std::function<void(const Table &)> &f) const {
Yifan Hong8ab3bee2017-03-08 14:01:11 -0800173 f(mServicesTable);
174 f(mPassthroughRefTable);
175 f(mImplementationsTable);
Yifan Honga3b87092017-03-02 19:19:29 -0800176}
177
Yifan Hong38d53e02017-02-13 17:51:59 -0800178void Lshal::postprocess() {
Yifan Honga3b87092017-03-02 19:19:29 -0800179 forEachTable([this](Table &table) {
180 if (mSortColumn) {
181 std::sort(table.begin(), table.end(), mSortColumn);
Yifan Hongae09a3d2017-02-14 17:33:50 -0800182 }
Yifan Honga3b87092017-03-02 19:19:29 -0800183 for (TableEntry &entry : table) {
184 entry.serverCmdline = getCmdline(entry.serverPid);
185 removeDeadProcesses(&entry.clientPids);
186 for (auto pid : entry.clientPids) {
187 entry.clientCmdlines.push_back(this->getCmdline(pid));
188 }
189 }
190 });
Yifan Hong3fdbd9f2017-03-08 14:01:58 -0800191 // use a double for loop here because lshal doesn't care about efficiency.
192 for (TableEntry &packageEntry : mImplementationsTable) {
193 std::string packageName = packageEntry.interfaceName;
194 FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
195 if (!fqPackageName.isValid()) {
196 continue;
197 }
198 for (TableEntry &interfaceEntry : mPassthroughRefTable) {
199 if (interfaceEntry.arch != ARCH_UNKNOWN) {
200 continue;
201 }
202 FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
203 if (!interfaceName.isValid()) {
204 continue;
205 }
206 if (interfaceName.getPackageAndVersion() == fqPackageName) {
207 interfaceEntry.arch = packageEntry.arch;
208 }
209 }
210 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800211}
212
213void Lshal::printLine(
214 const std::string &interfaceName,
Yifan Hongb4479022017-03-02 16:54:11 -0800215 const std::string &transport,
216 const std::string &arch,
217 const std::string &server,
Yifan Hongae09a3d2017-02-14 17:33:50 -0800218 const std::string &serverCmdline,
219 const std::string &address, const std::string &clients,
220 const std::string &clientCmdlines) const {
Yifan Hong38d53e02017-02-13 17:51:59 -0800221 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
222 mOut << std::setw(80) << interfaceName << "\t";
223 if (mSelectedColumns & ENABLE_TRANSPORT)
224 mOut << std::setw(10) << transport << "\t";
Yifan Hongb4479022017-03-02 16:54:11 -0800225 if (mSelectedColumns & ENABLE_ARCH)
226 mOut << std::setw(5) << arch << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800227 if (mSelectedColumns & ENABLE_SERVER_PID) {
228 if (mEnableCmdlines) {
229 mOut << std::setw(15) << serverCmdline << "\t";
230 } else {
231 mOut << std::setw(5) << server << "\t";
232 }
233 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800234 if (mSelectedColumns & ENABLE_SERVER_ADDR)
235 mOut << std::setw(16) << address << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800236 if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
237 if (mEnableCmdlines) {
238 mOut << std::setw(0) << clientCmdlines;
239 } else {
240 mOut << std::setw(0) << clients;
241 }
242 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800243 mOut << std::endl;
244}
245
Yifan Hong4b865492017-02-28 19:38:24 -0800246void Lshal::dumpVintf() const {
Yifan Hong9ee841f2017-04-18 14:19:40 -0700247 mOut << "<!-- " << std::endl
248 << " This is a skeleton device manifest. Notes: " << std::endl
249 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
250 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
251 << " only hwbinder is shown." << std::endl
252 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
253 << " <interface> declared; users will have to write them by hand." << std::endl
254 << " 4. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
255 << " is removed from the manifest file and written by assemble_vintf" << std::endl
256 << " at build time." << std::endl
257 << "-->" << std::endl;
258
Yifan Hong4b865492017-02-28 19:38:24 -0800259 vintf::HalManifest manifest;
Yifan Honga3b87092017-03-02 19:19:29 -0800260 forEachTable([this, &manifest] (const Table &table) {
261 for (const TableEntry &entry : table) {
Yifan Hong4b865492017-02-28 19:38:24 -0800262
Yifan Honga3b87092017-03-02 19:19:29 -0800263 std::string fqInstanceName = entry.interfaceName;
Yifan Hong4b865492017-02-28 19:38:24 -0800264
Yifan Honga3b87092017-03-02 19:19:29 -0800265 if (&table == &mImplementationsTable) {
266 // Quick hack to work around *'s
267 replaceAll(&fqInstanceName, '*', 'D');
268 }
269 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
270 FQName fqName(splittedFqInstanceName.first);
271 if (!fqName.isValid()) {
272 mErr << "Warning: '" << splittedFqInstanceName.first
273 << "' is not a valid FQName." << std::endl;
Yifan Hong4b865492017-02-28 19:38:24 -0800274 continue;
275 }
Yifan Honga3b87092017-03-02 19:19:29 -0800276 // Strip out system libs.
Steven Moreland9b6cd602017-03-22 14:22:55 -0700277 if (fqName.inPackage("android.hidl") ||
278 fqName.inPackage("android.frameworks") ||
279 fqName.inPackage("android.system")) {
Yifan Honga3b87092017-03-02 19:19:29 -0800280 continue;
281 }
282 std::string interfaceName =
283 &table == &mImplementationsTable ? "" : fqName.name();
284 std::string instanceName =
285 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
286
Yifan Hong9ee841f2017-04-18 14:19:40 -0700287 vintf::Version version{fqName.getPackageMajorVersion(),
288 fqName.getPackageMinorVersion()};
Yifan Honga3b87092017-03-02 19:19:29 -0800289 vintf::Transport transport;
Yifan Hong3fdbd9f2017-03-08 14:01:58 -0800290 vintf::Arch arch;
Yifan Honga3b87092017-03-02 19:19:29 -0800291 if (entry.transport == "hwbinder") {
292 transport = vintf::Transport::HWBINDER;
Yifan Hong3fdbd9f2017-03-08 14:01:58 -0800293 arch = vintf::Arch::ARCH_EMPTY;
Yifan Honga3b87092017-03-02 19:19:29 -0800294 } else if (entry.transport == "passthrough") {
295 transport = vintf::Transport::PASSTHROUGH;
Yifan Hong3fdbd9f2017-03-08 14:01:58 -0800296 switch (entry.arch) {
297 case lshal::ARCH32:
298 arch = vintf::Arch::ARCH_32; break;
299 case lshal::ARCH64:
300 arch = vintf::Arch::ARCH_64; break;
301 case lshal::ARCH_BOTH:
302 arch = vintf::Arch::ARCH_32_64; break;
303 case lshal::ARCH_UNKNOWN: // fallthrough
304 default:
305 mErr << "Warning: '" << fqName.package()
306 << "' doesn't have bitness info, assuming 32+64." << std::endl;
307 arch = vintf::Arch::ARCH_32_64;
308 }
Yifan Hong4b865492017-02-28 19:38:24 -0800309 } else {
Yifan Honga3b87092017-03-02 19:19:29 -0800310 mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
311 continue;
312 }
313
Yifan Hong9ee841f2017-04-18 14:19:40 -0700314 bool done = false;
315 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
316 if (hal->transport() != transport) {
317 if (transport != vintf::Transport::PASSTHROUGH) {
318 mErr << "Fatal: should not reach here. Generated result may be wrong."
319 << std::endl;
320 }
321 done = true;
322 break;
323 }
324 if (hal->hasVersion(version)) {
Yifan Hong06ceb022017-05-03 13:13:23 -0700325 if (&table != &mImplementationsTable) {
326 hal->interfaces[interfaceName].name = interfaceName;
327 hal->interfaces[interfaceName].instances.insert(instanceName);
328 }
Yifan Hong9ee841f2017-04-18 14:19:40 -0700329 done = true;
330 break;
331 }
332 }
333 if (done) {
334 continue; // to next TableEntry
335 }
Yifan Hong06ceb022017-05-03 13:13:23 -0700336 decltype(vintf::ManifestHal::interfaces) interfaces;
337 if (&table != &mImplementationsTable) {
338 interfaces[interfaceName].name = interfaceName;
339 interfaces[interfaceName].instances.insert(instanceName);
340 }
Yifan Hong9ee841f2017-04-18 14:19:40 -0700341 if (!manifest.add(vintf::ManifestHal{
Yifan Honga3b87092017-03-02 19:19:29 -0800342 .format = vintf::HalFormat::HIDL,
343 .name = fqName.package(),
Yifan Hong9ee841f2017-04-18 14:19:40 -0700344 .versions = {version},
345 .transportArch = {transport, arch},
Yifan Hong06ceb022017-05-03 13:13:23 -0700346 .interfaces = interfaces})) {
Yifan Hong9ee841f2017-04-18 14:19:40 -0700347 mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
Yifan Hong4b865492017-02-28 19:38:24 -0800348 }
349 }
Yifan Honga3b87092017-03-02 19:19:29 -0800350 });
Yifan Hong4b865492017-02-28 19:38:24 -0800351 mOut << vintf::gHalManifestConverter(manifest);
352}
353
Yifan Hongb4479022017-03-02 16:54:11 -0800354static const std::string &getArchString(Architecture arch) {
355 static const std::string sStr64 = "64";
356 static const std::string sStr32 = "32";
Yifan Hong1071c1b2017-03-03 10:57:43 -0800357 static const std::string sStrBoth = "32+64";
Yifan Hongb4479022017-03-02 16:54:11 -0800358 static const std::string sStrUnknown = "";
359 switch (arch) {
360 case ARCH64:
361 return sStr64;
362 case ARCH32:
363 return sStr32;
364 case ARCH_BOTH:
365 return sStrBoth;
366 case ARCH_UNKNOWN: // fall through
367 default:
368 return sStrUnknown;
369 }
370}
371
372static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
373 switch (a) {
374 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
375 return ARCH64;
376 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
377 return ARCH32;
378 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
379 default:
380 return ARCH_UNKNOWN;
381 }
382}
383
Andreas Huber28d35912017-03-24 13:14:11 -0700384// A unique_ptr type using a custom deleter function.
385template<typename T>
386using deleted_unique_ptr = std::unique_ptr<T, std::function<void(T *)> >;
387
388void Lshal::emitDebugInfo(
389 const sp<IServiceManager> &serviceManager,
390 const std::string &interfaceName,
391 const std::string &instanceName) const {
392 using android::hidl::base::V1_0::IBase;
393
394 hardware::Return<sp<IBase>> retBase =
395 serviceManager->get(interfaceName, instanceName);
396
397 sp<IBase> base;
398 if (!retBase.isOk() || (base = retBase) == nullptr) {
399 // There's a small race, where a service instantiated while collecting
400 // the list of services has by now terminated, so this isn't anything
401 // to be concerned about.
402 return;
403 }
404
405 PipeRelay relay(mOut.buf());
406
407 if (relay.initCheck() != OK) {
408 LOG(ERROR) << "PipeRelay::initCheck() FAILED w/ " << relay.initCheck();
409 return;
410 }
411
412 deleted_unique_ptr<native_handle_t> fdHandle(
413 native_handle_create(1 /* numFds */, 0 /* numInts */),
414 native_handle_delete);
415
416 fdHandle->data[0] = relay.fd();
417
418 hardware::hidl_vec<hardware::hidl_string> options;
419 hardware::Return<void> ret = base->debug(fdHandle.get(), options);
420
421 if (!ret.isOk()) {
422 LOG(ERROR)
423 << interfaceName
424 << "::debug(...) FAILED. (instance "
425 << instanceName
426 << ")";
427 }
428}
429
Yifan Honga3b87092017-03-02 19:19:29 -0800430void Lshal::dumpTable() {
431 mServicesTable.description =
432 "All binderized services (registered services through hwservicemanager)";
433 mPassthroughRefTable.description =
Yifan Hong1071c1b2017-03-03 10:57:43 -0800434 "All interfaces that getService() has ever return as a passthrough interface;\n"
Yifan Honga3b87092017-03-02 19:19:29 -0800435 "PIDs / processes shown below might be inaccurate because the process\n"
Yifan Hong1071c1b2017-03-03 10:57:43 -0800436 "might have relinquished the interface or might have died.\n"
Yifan Honga3b87092017-03-02 19:19:29 -0800437 "The Server / Server CMD column can be ignored.\n"
Yifan Hong1071c1b2017-03-03 10:57:43 -0800438 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
439 "the library and successfully fetched the passthrough implementation.";
Yifan Honga3b87092017-03-02 19:19:29 -0800440 mImplementationsTable.description =
441 "All available passthrough implementations (all -impl.so files)";
442 forEachTable([this] (const Table &table) {
443 mOut << table.description << std::endl;
444 mOut << std::left;
445 printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
446 "PTR", "Clients", "Clients CMD");
Andreas Huber28d35912017-03-24 13:14:11 -0700447
448 // We're only interested in dumping debug info for already
449 // instantiated services. There's little value in dumping the
450 // debug info for a service we create on the fly, so we only operate
451 // on the "mServicesTable".
452 sp<IServiceManager> serviceManager;
453 if (mEmitDebugInfo && &table == &mServicesTable) {
454 serviceManager = ::android::hardware::defaultServiceManager();
455 }
456
Yifan Honga3b87092017-03-02 19:19:29 -0800457 for (const auto &entry : table) {
458 printLine(entry.interfaceName,
459 entry.transport,
460 getArchString(entry.arch),
461 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
462 entry.serverCmdline,
463 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
464 join(entry.clientPids, " "),
465 join(entry.clientCmdlines, ";"));
Andreas Huber28d35912017-03-24 13:14:11 -0700466
467 if (serviceManager != nullptr) {
468 auto pair = splitFirst(entry.interfaceName, '/');
469 emitDebugInfo(serviceManager, pair.first, pair.second);
470 }
Yifan Honga3b87092017-03-02 19:19:29 -0800471 }
472 mOut << std::endl;
473 });
474
Yifan Hongb0dde932017-02-10 17:49:58 -0800475}
476
Yifan Hong4b865492017-02-28 19:38:24 -0800477void Lshal::dump() {
478 if (mVintf) {
479 dumpVintf();
480 if (!!mFileOutput) {
481 mFileOutput.buf().close();
482 delete &mFileOutput.buf();
483 mFileOutput = nullptr;
484 }
485 mOut = std::cout;
486 } else {
487 dumpTable();
488 }
489}
490
Yifan Honga3b87092017-03-02 19:19:29 -0800491void Lshal::putEntry(TableEntrySource source, TableEntry &&entry) {
492 Table *table = nullptr;
493 switch (source) {
494 case HWSERVICEMANAGER_LIST :
495 table = &mServicesTable; break;
496 case PTSERVICEMANAGER_REG_CLIENT :
497 table = &mPassthroughRefTable; break;
498 case LIST_DLLIB :
499 table = &mImplementationsTable; break;
500 default:
501 mErr << "Error: Unknown source of entry " << source << std::endl;
502 }
503 if (table) {
504 table->entries.push_back(std::forward<TableEntry>(entry));
505 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800506}
507
508Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
509 using namespace ::android::hardware;
510 using namespace ::android::hidl::manager::V1_0;
511 using namespace ::android::hidl::base::V1_0;
Yifan Hongb4479022017-03-02 16:54:11 -0800512 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
513 std::map<std::string, TableEntry> entries;
514 for (const auto &info : infos) {
515 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
516 std::string{info.instanceName.c_str()};
Yifan Hong1071c1b2017-03-03 10:57:43 -0800517 entries.emplace(interfaceName, TableEntry{
Yifan Hongb4479022017-03-02 16:54:11 -0800518 .interfaceName = interfaceName,
Yifan Hongb0dde932017-02-10 17:49:58 -0800519 .transport = "passthrough",
520 .serverPid = NO_PID,
521 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800522 .clientPids = {},
Yifan Honga3b87092017-03-02 19:19:29 -0800523 .arch = ARCH_UNKNOWN
Yifan Hongb4479022017-03-02 16:54:11 -0800524 }).first->second.arch |= fromBaseArchitecture(info.arch);
525 }
526 for (auto &&pair : entries) {
Yifan Honga3b87092017-03-02 19:19:29 -0800527 putEntry(LIST_DLLIB, std::move(pair.second));
Yifan Hongb0dde932017-02-10 17:49:58 -0800528 }
529 });
530 if (!ret.isOk()) {
531 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
532 << ret.description() << std::endl;
533 return DUMP_ALL_LIBS_ERROR;
534 }
535 return OK;
536}
537
538Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
539 using namespace ::android::hardware;
Yifan Hongb4479022017-03-02 16:54:11 -0800540 using namespace ::android::hardware::details;
Yifan Hongb0dde932017-02-10 17:49:58 -0800541 using namespace ::android::hidl::manager::V1_0;
542 using namespace ::android::hidl::base::V1_0;
Yifan Honge2dadf02017-02-14 15:43:31 -0800543 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800544 for (const auto &info : infos) {
Steven Moreland56d2d512017-03-21 12:17:55 -0700545 if (info.clientPids.size() <= 0) {
546 continue;
547 }
Yifan Honga3b87092017-03-02 19:19:29 -0800548 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
Yifan Hongb0dde932017-02-10 17:49:58 -0800549 .interfaceName =
550 std::string{info.interfaceName.c_str()} + "/" +
551 std::string{info.instanceName.c_str()},
552 .transport = "passthrough",
553 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
554 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800555 .clientPids = info.clientPids,
Yifan Honga3b87092017-03-02 19:19:29 -0800556 .arch = fromBaseArchitecture(info.arch)
Yifan Hongb0dde932017-02-10 17:49:58 -0800557 });
558 }
559 });
560 if (!ret.isOk()) {
561 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
562 << ret.description() << std::endl;
563 return DUMP_PASSTHROUGH_ERROR;
564 }
565 return OK;
566}
567
568Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
569 using namespace ::std;
570 using namespace ::android::hardware;
571 using namespace ::android::hidl::manager::V1_0;
572 using namespace ::android::hidl::base::V1_0;
573 const std::string mode = "hwbinder";
Yifan Hongb0dde932017-02-10 17:49:58 -0800574
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800575 hidl_vec<hidl_string> fqInstanceNames;
576 // copying out for timeoutIPC
577 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
578 fqInstanceNames = names;
Yifan Hongb0dde932017-02-10 17:49:58 -0800579 });
580 if (!listRet.isOk()) {
581 mErr << "Error: Failed to list services for " << mode << ": "
582 << listRet.description() << std::endl;
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800583 return DUMP_BINDERIZED_ERROR;
584 }
585
586 Status status = OK;
587 // server pid, .ptr value of binder object, child pids
588 std::map<std::string, DebugInfo> allDebugInfos;
589 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
590 for (const auto &fqInstanceName : fqInstanceNames) {
Yifan Hong4b865492017-02-28 19:38:24 -0800591 const auto pair = splitFirst(fqInstanceName, '/');
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800592 const auto &serviceName = pair.first;
593 const auto &instanceName = pair.second;
594 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
595 if (!getRet.isOk()) {
596 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
597 << "cannot be fetched from service manager:"
598 << getRet.description() << std::endl;
599 status |= DUMP_BINDERIZED_ERROR;
600 continue;
601 }
602 sp<IBase> service = getRet;
603 if (service == nullptr) {
604 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
605 << "cannot be fetched from service manager (null)";
606 status |= DUMP_BINDERIZED_ERROR;
607 continue;
608 }
609 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
610 allDebugInfos[fqInstanceName] = debugInfo;
611 if (debugInfo.pid >= 0) {
612 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
613 }
614 });
615 if (!debugRet.isOk()) {
616 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
617 << "debugging information cannot be retrieved:"
618 << debugRet.description() << std::endl;
619 status |= DUMP_BINDERIZED_ERROR;
620 }
621 }
622 for (auto &pair : allPids) {
623 pid_t serverPid = pair.first;
624 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
625 mErr << "Warning: no information for PID " << serverPid
626 << ", are you root?" << std::endl;
627 status |= DUMP_BINDERIZED_ERROR;
628 }
629 }
630 for (const auto &fqInstanceName : fqInstanceNames) {
631 auto it = allDebugInfos.find(fqInstanceName);
632 if (it == allDebugInfos.end()) {
Yifan Honga3b87092017-03-02 19:19:29 -0800633 putEntry(HWSERVICEMANAGER_LIST, {
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800634 .interfaceName = fqInstanceName,
635 .transport = mode,
636 .serverPid = NO_PID,
637 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800638 .clientPids = {},
Yifan Honga3b87092017-03-02 19:19:29 -0800639 .arch = ARCH_UNKNOWN
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800640 });
641 continue;
642 }
643 const DebugInfo &info = it->second;
Yifan Honga3b87092017-03-02 19:19:29 -0800644 putEntry(HWSERVICEMANAGER_LIST, {
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800645 .interfaceName = fqInstanceName,
646 .transport = mode,
647 .serverPid = info.pid,
648 .serverObjectAddress = info.ptr,
649 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
Yifan Hong4b865492017-02-28 19:38:24 -0800650 ? Pids{} : allPids[info.pid][info.ptr],
Yifan Hongb4479022017-03-02 16:54:11 -0800651 .arch = fromBaseArchitecture(info.arch),
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800652 });
Yifan Hongb0dde932017-02-10 17:49:58 -0800653 }
654 return status;
655}
656
657Status Lshal::fetch() {
658 Status status = OK;
659 auto bManager = ::android::hardware::defaultServiceManager();
660 if (bManager == nullptr) {
661 mErr << "Failed to get defaultServiceManager()!" << std::endl;
662 status |= NO_BINDERIZED_MANAGER;
663 } else {
664 status |= fetchBinderized(bManager);
665 // Passthrough PIDs are registered to the binderized manager as well.
666 status |= fetchPassthrough(bManager);
667 }
668
669 auto pManager = ::android::hardware::getPassthroughServiceManager();
670 if (pManager == nullptr) {
671 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
672 status |= NO_PASSTHROUGH_MANAGER;
673 } else {
674 status |= fetchAllLibraries(pManager);
675 }
676 return status;
677}
678
679void Lshal::usage() const {
680 mErr
681 << "usage: lshal" << std::endl
Yifan Honga3b87092017-03-02 19:19:29 -0800682 << " Dump all hals with default ordering and columns [-ipc]." << std::endl
Yifan Hongb4479022017-03-02 16:54:11 -0800683 << " lshal [--interface|-i] [--transport|-t] [-r|--arch]" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800684 << " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
Yifan Hong4b865492017-02-28 19:38:24 -0800685 << " [--sort={interface|i|pid|p}] [--init-vintf[=path]]" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800686 << " -i, --interface: print the interface name column" << std::endl
687 << " -n, --instance: print the instance name column" << std::endl
688 << " -t, --transport: print the transport mode column" << std::endl
Yifan Hongb4479022017-03-02 16:54:11 -0800689 << " -r, --arch: print if the HAL is in 64-bit or 32-bit" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800690 << " -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800691 << " -a, --address: print the server object address column" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800692 << " -c, --clients: print the client PIDs, or client cmdlines if -m is set"
693 << std::endl
694 << " -m, --cmdline: print cmdline instead of PIDs" << std::endl
Steven Moreland6fb35102017-05-02 17:27:52 -0700695 << " -d, --debug: emit debug info from IBase::debug" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800696 << " --sort=i, --sort=interface: sort by interface name" << std::endl
697 << " --sort=p, --sort=pid: sort by server pid" << std::endl
Yifan Hong4b865492017-02-28 19:38:24 -0800698 << " --init-vintf=path: form a skeleton HAL manifest to specified file " << std::endl
699 << " (stdout if no file specified)" << std::endl
Yifan Hongb0dde932017-02-10 17:49:58 -0800700 << " lshal [-h|--help]" << std::endl
701 << " -h, --help: show this help information." << std::endl;
702}
703
704Status Lshal::parseArgs(int argc, char **argv) {
705 static struct option longOptions[] = {
Yifan Hong38d53e02017-02-13 17:51:59 -0800706 // long options with short alternatives
707 {"help", no_argument, 0, 'h' },
708 {"interface", no_argument, 0, 'i' },
709 {"transport", no_argument, 0, 't' },
Yifan Hongb4479022017-03-02 16:54:11 -0800710 {"arch", no_argument, 0, 'r' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800711 {"pid", no_argument, 0, 'p' },
712 {"address", no_argument, 0, 'a' },
713 {"clients", no_argument, 0, 'c' },
Yifan Hongae09a3d2017-02-14 17:33:50 -0800714 {"cmdline", no_argument, 0, 'm' },
Andreas Huber28d35912017-03-24 13:14:11 -0700715 {"debug", optional_argument, 0, 'd' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800716
717 // long options without short alternatives
718 {"sort", required_argument, 0, 's' },
Yifan Hong4b865492017-02-28 19:38:24 -0800719 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800720 { 0, 0, 0, 0 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800721 };
722
723 int optionIndex;
724 int c;
725 optind = 1;
726 for (;;) {
727 // using getopt_long in case we want to add other options in the future
Andreas Huber28d35912017-03-24 13:14:11 -0700728 c = getopt_long(argc, argv, "hitrpacmd", longOptions, &optionIndex);
Yifan Hongb0dde932017-02-10 17:49:58 -0800729 if (c == -1) {
730 break;
731 }
732 switch (c) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800733 case 's': {
734 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
735 mSortColumn = TableEntry::sortByInterfaceName;
736 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
737 mSortColumn = TableEntry::sortByServerPid;
738 } else {
739 mErr << "Unrecognized sorting column: " << optarg << std::endl;
740 usage();
741 return USAGE;
742 }
743 break;
744 }
Yifan Hong4b865492017-02-28 19:38:24 -0800745 case 'v': {
746 if (optarg) {
747 mFileOutput = new std::ofstream{optarg};
748 mOut = mFileOutput;
749 if (!mFileOutput.buf().is_open()) {
750 mErr << "Could not open file '" << optarg << "'." << std::endl;
751 return IO_ERROR;
752 }
753 }
754 mVintf = true;
755 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800756 case 'i': {
757 mSelectedColumns |= ENABLE_INTERFACE_NAME;
758 break;
759 }
760 case 't': {
761 mSelectedColumns |= ENABLE_TRANSPORT;
762 break;
763 }
Yifan Hongb4479022017-03-02 16:54:11 -0800764 case 'r': {
765 mSelectedColumns |= ENABLE_ARCH;
766 break;
767 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800768 case 'p': {
769 mSelectedColumns |= ENABLE_SERVER_PID;
770 break;
771 }
772 case 'a': {
773 mSelectedColumns |= ENABLE_SERVER_ADDR;
774 break;
775 }
776 case 'c': {
777 mSelectedColumns |= ENABLE_CLIENT_PIDS;
778 break;
779 }
Yifan Hongae09a3d2017-02-14 17:33:50 -0800780 case 'm': {
781 mEnableCmdlines = true;
782 break;
783 }
Andreas Huber28d35912017-03-24 13:14:11 -0700784 case 'd': {
785 mEmitDebugInfo = true;
786
787 if (optarg) {
788 mFileOutput = new std::ofstream{optarg};
789 mOut = mFileOutput;
790 if (!mFileOutput.buf().is_open()) {
791 mErr << "Could not open file '" << optarg << "'." << std::endl;
792 return IO_ERROR;
793 }
794 chown(optarg, AID_SHELL, AID_SHELL);
795 }
796 break;
797 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800798 case 'h': // falls through
799 default: // see unrecognized options
800 usage();
801 return USAGE;
802 }
803 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800804
805 if (mSelectedColumns == 0) {
Yifan Honga3b87092017-03-02 19:19:29 -0800806 mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
Yifan Hong38d53e02017-02-13 17:51:59 -0800807 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800808 return OK;
809}
810
811int Lshal::main(int argc, char **argv) {
812 Status status = parseArgs(argc, argv);
813 if (status != OK) {
814 return status;
815 }
816 status = fetch();
Yifan Hong38d53e02017-02-13 17:51:59 -0800817 postprocess();
Yifan Hongb0dde932017-02-10 17:49:58 -0800818 dump();
819 return status;
820}
821
Yifan Honga57dffb2017-02-21 14:59:00 -0800822void signalHandler(int sig) {
823 if (sig == SIGINT) {
824 int retVal;
825 pthread_exit(&retVal);
826 }
827}
828
Yifan Hongb0dde932017-02-10 17:49:58 -0800829} // namespace lshal
830} // namespace android
831
832int main(int argc, char **argv) {
Yifan Honga57dffb2017-02-21 14:59:00 -0800833 signal(SIGINT, ::android::lshal::signalHandler);
Yifan Hongb0dde932017-02-10 17:49:58 -0800834 return ::android::lshal::Lshal{}.main(argc, argv);
835}