blob: 7fb8083aa79bd5ed13f015ef8ce2ac12a21b9a1c [file] [log] [blame]
Yifan Hong443df792017-05-09 18:49:45 -07001/*
2 * Copyright (C) 2017 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 "ListCommand.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>
Yifan Hong443df792017-05-09 18:49:45 -070030#include <hidl-util/FQName.h>
31#include <private/android_filesystem_config.h>
32#include <sys/stat.h>
33#include <vintf/HalManifest.h>
34#include <vintf/parse_xml.h>
35
36#include "Lshal.h"
37#include "PipeRelay.h"
Yifan Hong1bc1e9f2017-08-29 17:28:12 -070038#include "TextTable.h"
Yifan Hong443df792017-05-09 18:49:45 -070039#include "Timeout.h"
40#include "utils.h"
41
42using ::android::hardware::hidl_string;
43using ::android::hidl::manager::V1_0::IServiceManager;
44
45namespace android {
46namespace lshal {
47
48ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
49}
50
51std::string getCmdline(pid_t pid) {
52 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
53 std::string cmdline;
54 if (!ifs.is_open()) {
55 return "";
56 }
57 ifs >> cmdline;
58 return cmdline;
59}
60
61const std::string &ListCommand::getCmdline(pid_t pid) {
62 auto pair = mCmdlines.find(pid);
63 if (pair != mCmdlines.end()) {
64 return pair->second;
65 }
66 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
67 return mCmdlines[pid];
68}
69
70void ListCommand::removeDeadProcesses(Pids *pids) {
71 static const pid_t myPid = getpid();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070072 pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
Yifan Hong443df792017-05-09 18:49:45 -070073 return pid == myPid || this->getCmdline(pid).empty();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070074 }), pids->end());
Yifan Hong443df792017-05-09 18:49:45 -070075}
76
Steven Morelandd8e20192017-05-24 11:23:08 -070077bool scanBinderContext(pid_t pid,
78 const std::string &contextName,
79 std::function<void(const std::string&)> eachLine) {
80 std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
Yifan Hong443df792017-05-09 18:49:45 -070081 if (!ifs.is_open()) {
82 return false;
83 }
84
Steven Morelandd8e20192017-05-24 11:23:08 -070085 static const std::regex kContextLine("^context (\\w+)$");
Yifan Hong443df792017-05-09 18:49:45 -070086
Steven Morelandd8e20192017-05-24 11:23:08 -070087 bool isDesiredContext = false;
Yifan Hong443df792017-05-09 18:49:45 -070088 std::string line;
89 std::smatch match;
90 while(getline(ifs, line)) {
Steven Morelandd8e20192017-05-24 11:23:08 -070091 if (std::regex_search(line, match, kContextLine)) {
92 isDesiredContext = match.str(1) == contextName;
Yifan Hong443df792017-05-09 18:49:45 -070093 continue;
94 }
Steven Morelandd8e20192017-05-24 11:23:08 -070095
96 if (!isDesiredContext) {
Yifan Hong443df792017-05-09 18:49:45 -070097 continue;
98 }
Steven Morelandd8e20192017-05-24 11:23:08 -070099
100 eachLine(line);
Yifan Hong443df792017-05-09 18:49:45 -0700101 }
102 return true;
103}
104
Steven Morelandd8e20192017-05-24 11:23:08 -0700105bool ListCommand::getPidInfo(
106 pid_t serverPid, PidInfo *pidInfo) const {
107 static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
108 static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
109
110 std::smatch match;
111 return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
112 if (std::regex_search(line, match, kReferencePrefix)) {
113 const std::string &ptrString = "0x" + match.str(2); // use number after c
114 uint64_t ptr;
115 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
116 // Should not reach here, but just be tolerant.
117 mErr << "Could not parse number " << ptrString << std::endl;
118 return;
119 }
120 const std::string proc = " proc ";
121 auto pos = line.rfind(proc);
122 if (pos != std::string::npos) {
123 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
124 int32_t pid;
125 if (!::android::base::ParseInt(pidStr, &pid)) {
126 mErr << "Could not parse number " << pidStr << std::endl;
127 return;
128 }
129 pidInfo->refPids[ptr].push_back(pid);
130 }
131 }
132
133 return;
134 }
135
136 if (std::regex_search(line, match, kThreadPrefix)) {
137 // "1" is waiting in binder driver
138 // "2" is poll. It's impossible to tell if these are in use.
139 // and HIDL default code doesn't use it.
140 bool isInUse = match.str(1) != "1";
141 // "0" is a thread that has called into binder
142 // "1" is looper thread
143 // "2" is main looper thread
144 bool isHwbinderThread = match.str(2) != "0";
145
146 if (!isHwbinderThread) {
147 return;
148 }
149
150 if (isInUse) {
151 pidInfo->threadUsage++;
152 }
153
154 pidInfo->threadCount++;
155 return;
156 }
157
158 // not reference or thread line
159 return;
160 });
161}
162
Yifan Hong443df792017-05-09 18:49:45 -0700163// Must process hwbinder services first, then passthrough services.
164void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
165 f(mServicesTable);
166 f(mPassthroughRefTable);
167 f(mImplementationsTable);
168}
169void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
170 f(mServicesTable);
171 f(mPassthroughRefTable);
172 f(mImplementationsTable);
173}
174
175void ListCommand::postprocess() {
176 forEachTable([this](Table &table) {
177 if (mSortColumn) {
178 std::sort(table.begin(), table.end(), mSortColumn);
179 }
180 for (TableEntry &entry : table) {
181 entry.serverCmdline = getCmdline(entry.serverPid);
182 removeDeadProcesses(&entry.clientPids);
183 for (auto pid : entry.clientPids) {
184 entry.clientCmdlines.push_back(this->getCmdline(pid));
185 }
186 }
187 });
188 // use a double for loop here because lshal doesn't care about efficiency.
189 for (TableEntry &packageEntry : mImplementationsTable) {
190 std::string packageName = packageEntry.interfaceName;
191 FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
192 if (!fqPackageName.isValid()) {
193 continue;
194 }
195 for (TableEntry &interfaceEntry : mPassthroughRefTable) {
196 if (interfaceEntry.arch != ARCH_UNKNOWN) {
197 continue;
198 }
199 FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
200 if (!interfaceName.isValid()) {
201 continue;
202 }
203 if (interfaceName.getPackageAndVersion() == fqPackageName) {
204 interfaceEntry.arch = packageEntry.arch;
205 }
206 }
207 }
208}
209
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700210void ListCommand::addLine(TextTable *textTable, const std::string &interfaceName,
211 const std::string &transport, const std::string &arch,
212 const std::string &threadUsage, const std::string &server,
213 const std::string &serverCmdline, const std::string &address,
214 const std::string &clients, const std::string &clientCmdlines) const {
215 std::vector<std::string> columns;
Yifan Hong443df792017-05-09 18:49:45 -0700216 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700217 columns.push_back(interfaceName);
Yifan Hong443df792017-05-09 18:49:45 -0700218 if (mSelectedColumns & ENABLE_TRANSPORT)
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700219 columns.push_back(transport);
Yifan Hong443df792017-05-09 18:49:45 -0700220 if (mSelectedColumns & ENABLE_ARCH)
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700221 columns.push_back(arch);
Steven Morelandd8e20192017-05-24 11:23:08 -0700222 if (mSelectedColumns & ENABLE_THREADS) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700223 columns.push_back(threadUsage);
Steven Morelandd8e20192017-05-24 11:23:08 -0700224 }
Yifan Hong443df792017-05-09 18:49:45 -0700225 if (mSelectedColumns & ENABLE_SERVER_PID) {
226 if (mEnableCmdlines) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700227 columns.push_back(serverCmdline);
Yifan Hong443df792017-05-09 18:49:45 -0700228 } else {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700229 columns.push_back(server);
Yifan Hong443df792017-05-09 18:49:45 -0700230 }
231 }
232 if (mSelectedColumns & ENABLE_SERVER_ADDR)
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700233 columns.push_back(address);
Yifan Hong443df792017-05-09 18:49:45 -0700234 if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
235 if (mEnableCmdlines) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700236 columns.push_back(clientCmdlines);
Yifan Hong443df792017-05-09 18:49:45 -0700237 } else {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700238 columns.push_back(clients);
Yifan Hong443df792017-05-09 18:49:45 -0700239 }
240 }
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700241 textTable->add(std::move(columns));
Yifan Hong443df792017-05-09 18:49:45 -0700242}
243
Yifan Hong77c87822017-06-19 15:47:39 -0700244static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
245 for (vintf::Version& v : hal->versions) {
246 if (v.majorVer == version.majorVer) {
247 v.minorVer = std::max(v.minorVer, version.minorVer);
248 return true;
249 }
250 }
251 return false;
252}
253
Yifan Hong443df792017-05-09 18:49:45 -0700254void ListCommand::dumpVintf() const {
Yifan Hong236301c2017-06-19 12:27:08 -0700255 using vintf::operator|=;
Yifan Hong443df792017-05-09 18:49:45 -0700256 mOut << "<!-- " << std::endl
257 << " This is a skeleton device manifest. Notes: " << std::endl
258 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
259 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
260 << " only hwbinder is shown." << std::endl
261 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
262 << " <interface> declared; users will have to write them by hand." << std::endl
Yifan Hong77c87822017-06-19 15:47:39 -0700263 << " 4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
264 << " higher minor version if they have the same name and major version." << std::endl
265 << " 5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700266 << " is removed from the manifest file and written by assemble_vintf" << std::endl
267 << " at build time." << std::endl
268 << "-->" << std::endl;
269
270 vintf::HalManifest manifest;
271 forEachTable([this, &manifest] (const Table &table) {
272 for (const TableEntry &entry : table) {
273
274 std::string fqInstanceName = entry.interfaceName;
275
276 if (&table == &mImplementationsTable) {
277 // Quick hack to work around *'s
278 replaceAll(&fqInstanceName, '*', 'D');
279 }
280 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
281 FQName fqName(splittedFqInstanceName.first);
282 if (!fqName.isValid()) {
283 mErr << "Warning: '" << splittedFqInstanceName.first
284 << "' is not a valid FQName." << std::endl;
285 continue;
286 }
287 // Strip out system libs.
288 if (fqName.inPackage("android.hidl") ||
289 fqName.inPackage("android.frameworks") ||
290 fqName.inPackage("android.system")) {
291 continue;
292 }
293 std::string interfaceName =
294 &table == &mImplementationsTable ? "" : fqName.name();
295 std::string instanceName =
296 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
297
298 vintf::Version version{fqName.getPackageMajorVersion(),
299 fqName.getPackageMinorVersion()};
300 vintf::Transport transport;
301 vintf::Arch arch;
302 if (entry.transport == "hwbinder") {
303 transport = vintf::Transport::HWBINDER;
304 arch = vintf::Arch::ARCH_EMPTY;
305 } else if (entry.transport == "passthrough") {
306 transport = vintf::Transport::PASSTHROUGH;
307 switch (entry.arch) {
308 case lshal::ARCH32:
309 arch = vintf::Arch::ARCH_32; break;
310 case lshal::ARCH64:
311 arch = vintf::Arch::ARCH_64; break;
312 case lshal::ARCH_BOTH:
313 arch = vintf::Arch::ARCH_32_64; break;
314 case lshal::ARCH_UNKNOWN: // fallthrough
315 default:
316 mErr << "Warning: '" << fqName.package()
317 << "' doesn't have bitness info, assuming 32+64." << std::endl;
318 arch = vintf::Arch::ARCH_32_64;
319 }
320 } else {
321 mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
322 continue;
323 }
324
325 bool done = false;
326 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
327 if (hal->transport() != transport) {
328 if (transport != vintf::Transport::PASSTHROUGH) {
Yifan Hong236301c2017-06-19 12:27:08 -0700329 mErr << "Fatal: should not reach here. Generated result may be wrong for '"
330 << hal->name << "'."
Yifan Hong443df792017-05-09 18:49:45 -0700331 << std::endl;
332 }
333 done = true;
334 break;
335 }
Yifan Hong77c87822017-06-19 15:47:39 -0700336 if (findAndBumpVersion(hal, version)) {
Yifan Hong443df792017-05-09 18:49:45 -0700337 if (&table != &mImplementationsTable) {
338 hal->interfaces[interfaceName].name = interfaceName;
339 hal->interfaces[interfaceName].instances.insert(instanceName);
340 }
Yifan Hong236301c2017-06-19 12:27:08 -0700341 hal->transportArch.arch |= arch;
Yifan Hong443df792017-05-09 18:49:45 -0700342 done = true;
343 break;
344 }
345 }
346 if (done) {
347 continue; // to next TableEntry
348 }
349 decltype(vintf::ManifestHal::interfaces) interfaces;
350 if (&table != &mImplementationsTable) {
351 interfaces[interfaceName].name = interfaceName;
352 interfaces[interfaceName].instances.insert(instanceName);
353 }
354 if (!manifest.add(vintf::ManifestHal{
355 .format = vintf::HalFormat::HIDL,
356 .name = fqName.package(),
357 .versions = {version},
358 .transportArch = {transport, arch},
359 .interfaces = interfaces})) {
360 mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
361 }
362 }
363 });
364 mOut << vintf::gHalManifestConverter(manifest);
365}
366
367static const std::string &getArchString(Architecture arch) {
368 static const std::string sStr64 = "64";
369 static const std::string sStr32 = "32";
370 static const std::string sStrBoth = "32+64";
371 static const std::string sStrUnknown = "";
372 switch (arch) {
373 case ARCH64:
374 return sStr64;
375 case ARCH32:
376 return sStr32;
377 case ARCH_BOTH:
378 return sStrBoth;
379 case ARCH_UNKNOWN: // fall through
380 default:
381 return sStrUnknown;
382 }
383}
384
385static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
386 switch (a) {
387 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
388 return ARCH64;
389 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
390 return ARCH32;
391 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
392 default:
393 return ARCH_UNKNOWN;
394 }
395}
396
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700397void ListCommand::addLine(TextTable *table, const TableEntry &entry) {
398 addLine(table, entry.interfaceName, entry.transport, getArchString(entry.arch),
399 entry.getThreadUsage(),
400 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
401 entry.serverCmdline,
402 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
403 join(entry.clientPids, " "), join(entry.clientCmdlines, ";"));
404}
405
Yifan Hong443df792017-05-09 18:49:45 -0700406void ListCommand::dumpTable() {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700407 if (mNeat) {
408 TextTable textTable;
409 forEachTable([this, &textTable](const Table &table) {
410 for (const auto &entry : table) addLine(&textTable, entry);
411 });
412 textTable.dump(mOut.buf());
413 return;
414 }
415
Yifan Hong443df792017-05-09 18:49:45 -0700416 mServicesTable.description =
417 "All binderized services (registered services through hwservicemanager)";
418 mPassthroughRefTable.description =
419 "All interfaces that getService() has ever return as a passthrough interface;\n"
420 "PIDs / processes shown below might be inaccurate because the process\n"
421 "might have relinquished the interface or might have died.\n"
422 "The Server / Server CMD column can be ignored.\n"
423 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
424 "the library and successfully fetched the passthrough implementation.";
425 mImplementationsTable.description =
426 "All available passthrough implementations (all -impl.so files)";
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700427
428 forEachTable([this](const Table &table) {
429 TextTable textTable;
430
431 textTable.add(table.description);
432 addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD",
433 "PTR", "Clients", "Clients CMD");
Yifan Hong443df792017-05-09 18:49:45 -0700434
Yifan Hong443df792017-05-09 18:49:45 -0700435 for (const auto &entry : table) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700436 addLine(&textTable, entry);
Yifan Hong48dc9f82017-05-09 19:33:08 -0700437 // We're only interested in dumping debug info for already
438 // instantiated services. There's little value in dumping the
439 // debug info for a service we create on the fly, so we only operate
440 // on the "mServicesTable".
441 if (mEmitDebugInfo && &table == &mServicesTable) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700442 std::stringstream out;
Yifan Hong443df792017-05-09 18:49:45 -0700443 auto pair = splitFirst(entry.interfaceName, '/');
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700444 mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
445 NullableOStream<std::ostream>(nullptr));
446 textTable.add(out.str());
Yifan Hong443df792017-05-09 18:49:45 -0700447 }
448 }
Yifan Hong443df792017-05-09 18:49:45 -0700449
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700450 // Add empty line after each table
451 textTable.add();
452
453 textTable.dump(mOut.buf());
454 });
Yifan Hong443df792017-05-09 18:49:45 -0700455}
456
457void ListCommand::dump() {
458 if (mVintf) {
459 dumpVintf();
460 if (!!mFileOutput) {
461 mFileOutput.buf().close();
462 delete &mFileOutput.buf();
463 mFileOutput = nullptr;
464 }
465 mOut = std::cout;
466 } else {
467 dumpTable();
468 }
469}
470
471void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
472 Table *table = nullptr;
473 switch (source) {
474 case HWSERVICEMANAGER_LIST :
475 table = &mServicesTable; break;
476 case PTSERVICEMANAGER_REG_CLIENT :
477 table = &mPassthroughRefTable; break;
478 case LIST_DLLIB :
479 table = &mImplementationsTable; break;
480 default:
481 mErr << "Error: Unknown source of entry " << source << std::endl;
482 }
483 if (table) {
484 table->entries.push_back(std::forward<TableEntry>(entry));
485 }
486}
487
488Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
489 using namespace ::android::hardware;
490 using namespace ::android::hidl::manager::V1_0;
491 using namespace ::android::hidl::base::V1_0;
Yifan Hongf2d557b2017-05-24 19:45:02 -0700492 using std::literals::chrono_literals::operator""s;
493 auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hong443df792017-05-09 18:49:45 -0700494 std::map<std::string, TableEntry> entries;
495 for (const auto &info : infos) {
496 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
497 std::string{info.instanceName.c_str()};
498 entries.emplace(interfaceName, TableEntry{
499 .interfaceName = interfaceName,
500 .transport = "passthrough",
501 .serverPid = NO_PID,
502 .serverObjectAddress = NO_PTR,
Yifan Hongf2d557b2017-05-24 19:45:02 -0700503 .clientPids = info.clientPids,
Yifan Hong443df792017-05-09 18:49:45 -0700504 .arch = ARCH_UNKNOWN
505 }).first->second.arch |= fromBaseArchitecture(info.arch);
506 }
507 for (auto &&pair : entries) {
508 putEntry(LIST_DLLIB, std::move(pair.second));
509 }
510 });
511 if (!ret.isOk()) {
512 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
513 << ret.description() << std::endl;
514 return DUMP_ALL_LIBS_ERROR;
515 }
516 return OK;
517}
518
519Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
520 using namespace ::android::hardware;
521 using namespace ::android::hardware::details;
522 using namespace ::android::hidl::manager::V1_0;
523 using namespace ::android::hidl::base::V1_0;
524 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
525 for (const auto &info : infos) {
526 if (info.clientPids.size() <= 0) {
527 continue;
528 }
529 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
530 .interfaceName =
531 std::string{info.interfaceName.c_str()} + "/" +
532 std::string{info.instanceName.c_str()},
533 .transport = "passthrough",
534 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
535 .serverObjectAddress = NO_PTR,
536 .clientPids = info.clientPids,
537 .arch = fromBaseArchitecture(info.arch)
538 });
539 }
540 });
541 if (!ret.isOk()) {
542 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
543 << ret.description() << std::endl;
544 return DUMP_PASSTHROUGH_ERROR;
545 }
546 return OK;
547}
548
549Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
550 using namespace ::std;
551 using namespace ::android::hardware;
552 using namespace ::android::hidl::manager::V1_0;
553 using namespace ::android::hidl::base::V1_0;
554 const std::string mode = "hwbinder";
555
556 hidl_vec<hidl_string> fqInstanceNames;
557 // copying out for timeoutIPC
558 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
559 fqInstanceNames = names;
560 });
561 if (!listRet.isOk()) {
562 mErr << "Error: Failed to list services for " << mode << ": "
563 << listRet.description() << std::endl;
564 return DUMP_BINDERIZED_ERROR;
565 }
566
567 Status status = OK;
568 // server pid, .ptr value of binder object, child pids
569 std::map<std::string, DebugInfo> allDebugInfos;
Steven Morelandd8e20192017-05-24 11:23:08 -0700570 std::map<pid_t, PidInfo> allPids;
Yifan Hong443df792017-05-09 18:49:45 -0700571 for (const auto &fqInstanceName : fqInstanceNames) {
572 const auto pair = splitFirst(fqInstanceName, '/');
573 const auto &serviceName = pair.first;
574 const auto &instanceName = pair.second;
575 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
576 if (!getRet.isOk()) {
577 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
578 << "cannot be fetched from service manager:"
579 << getRet.description() << std::endl;
580 status |= DUMP_BINDERIZED_ERROR;
581 continue;
582 }
583 sp<IBase> service = getRet;
584 if (service == nullptr) {
585 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
586 << "cannot be fetched from service manager (null)"
587 << std::endl;
588 status |= DUMP_BINDERIZED_ERROR;
589 continue;
590 }
591 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
592 allDebugInfos[fqInstanceName] = debugInfo;
593 if (debugInfo.pid >= 0) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700594 allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
Yifan Hong443df792017-05-09 18:49:45 -0700595 }
596 });
597 if (!debugRet.isOk()) {
598 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
599 << "debugging information cannot be retrieved:"
600 << debugRet.description() << std::endl;
601 status |= DUMP_BINDERIZED_ERROR;
602 }
603 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700604
Yifan Hong443df792017-05-09 18:49:45 -0700605 for (auto &pair : allPids) {
606 pid_t serverPid = pair.first;
Steven Morelandd8e20192017-05-24 11:23:08 -0700607 if (!getPidInfo(serverPid, &allPids[serverPid])) {
Yifan Hong443df792017-05-09 18:49:45 -0700608 mErr << "Warning: no information for PID " << serverPid
609 << ", are you root?" << std::endl;
610 status |= DUMP_BINDERIZED_ERROR;
611 }
612 }
613 for (const auto &fqInstanceName : fqInstanceNames) {
614 auto it = allDebugInfos.find(fqInstanceName);
615 if (it == allDebugInfos.end()) {
616 putEntry(HWSERVICEMANAGER_LIST, {
617 .interfaceName = fqInstanceName,
618 .transport = mode,
619 .serverPid = NO_PID,
620 .serverObjectAddress = NO_PTR,
621 .clientPids = {},
Steven Morelandd8e20192017-05-24 11:23:08 -0700622 .threadUsage = 0,
623 .threadCount = 0,
Yifan Hong443df792017-05-09 18:49:45 -0700624 .arch = ARCH_UNKNOWN
625 });
626 continue;
627 }
628 const DebugInfo &info = it->second;
Steven Morelandd8e20192017-05-24 11:23:08 -0700629 bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
630
Yifan Hong443df792017-05-09 18:49:45 -0700631 putEntry(HWSERVICEMANAGER_LIST, {
632 .interfaceName = fqInstanceName,
633 .transport = mode,
634 .serverPid = info.pid,
635 .serverObjectAddress = info.ptr,
Steven Morelandd8e20192017-05-24 11:23:08 -0700636 .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
637 .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
638 .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
Yifan Hong443df792017-05-09 18:49:45 -0700639 .arch = fromBaseArchitecture(info.arch),
640 });
641 }
642 return status;
643}
644
645Status ListCommand::fetch() {
646 Status status = OK;
Yifan Hong9881df92017-05-10 14:33:05 -0700647 auto bManager = mLshal.serviceManager();
Yifan Hong443df792017-05-09 18:49:45 -0700648 if (bManager == nullptr) {
649 mErr << "Failed to get defaultServiceManager()!" << std::endl;
650 status |= NO_BINDERIZED_MANAGER;
651 } else {
652 status |= fetchBinderized(bManager);
653 // Passthrough PIDs are registered to the binderized manager as well.
654 status |= fetchPassthrough(bManager);
655 }
656
Yifan Hong9881df92017-05-10 14:33:05 -0700657 auto pManager = mLshal.passthroughManager();
Yifan Hong443df792017-05-09 18:49:45 -0700658 if (pManager == nullptr) {
659 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
660 status |= NO_PASSTHROUGH_MANAGER;
661 } else {
662 status |= fetchAllLibraries(pManager);
663 }
664 return status;
665}
666
667Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
668 static struct option longOptions[] = {
669 // long options with short alternatives
670 {"help", no_argument, 0, 'h' },
671 {"interface", no_argument, 0, 'i' },
672 {"transport", no_argument, 0, 't' },
673 {"arch", no_argument, 0, 'r' },
674 {"pid", no_argument, 0, 'p' },
675 {"address", no_argument, 0, 'a' },
676 {"clients", no_argument, 0, 'c' },
Steven Morelandd8e20192017-05-24 11:23:08 -0700677 {"threads", no_argument, 0, 'e' },
Yifan Hong443df792017-05-09 18:49:45 -0700678 {"cmdline", no_argument, 0, 'm' },
679 {"debug", optional_argument, 0, 'd' },
680
681 // long options without short alternatives
682 {"sort", required_argument, 0, 's' },
683 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong6da06912017-05-12 16:56:43 -0700684 {"neat", no_argument, 0, 'n' },
Yifan Hong443df792017-05-09 18:49:45 -0700685 { 0, 0, 0, 0 }
686 };
687
688 int optionIndex;
689 int c;
690 // Lshal::parseArgs has set optind to the next option to parse
691 for (;;) {
692 // using getopt_long in case we want to add other options in the future
693 c = getopt_long(arg.argc, arg.argv,
Steven Morelandd8e20192017-05-24 11:23:08 -0700694 "hitrpacmde", longOptions, &optionIndex);
Yifan Hong443df792017-05-09 18:49:45 -0700695 if (c == -1) {
696 break;
697 }
698 switch (c) {
699 case 's': {
700 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
701 mSortColumn = TableEntry::sortByInterfaceName;
702 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
703 mSortColumn = TableEntry::sortByServerPid;
704 } else {
705 mErr << "Unrecognized sorting column: " << optarg << std::endl;
706 mLshal.usage(command);
707 return USAGE;
708 }
709 break;
710 }
711 case 'v': {
712 if (optarg) {
713 mFileOutput = new std::ofstream{optarg};
714 mOut = mFileOutput;
715 if (!mFileOutput.buf().is_open()) {
716 mErr << "Could not open file '" << optarg << "'." << std::endl;
717 return IO_ERROR;
718 }
719 }
720 mVintf = true;
721 }
722 case 'i': {
723 mSelectedColumns |= ENABLE_INTERFACE_NAME;
724 break;
725 }
726 case 't': {
727 mSelectedColumns |= ENABLE_TRANSPORT;
728 break;
729 }
730 case 'r': {
731 mSelectedColumns |= ENABLE_ARCH;
732 break;
733 }
734 case 'p': {
735 mSelectedColumns |= ENABLE_SERVER_PID;
736 break;
737 }
738 case 'a': {
739 mSelectedColumns |= ENABLE_SERVER_ADDR;
740 break;
741 }
742 case 'c': {
743 mSelectedColumns |= ENABLE_CLIENT_PIDS;
744 break;
745 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700746 case 'e': {
747 mSelectedColumns |= ENABLE_THREADS;
748 break;
749 }
Yifan Hong443df792017-05-09 18:49:45 -0700750 case 'm': {
751 mEnableCmdlines = true;
752 break;
753 }
754 case 'd': {
755 mEmitDebugInfo = true;
756
757 if (optarg) {
758 mFileOutput = new std::ofstream{optarg};
759 mOut = mFileOutput;
760 if (!mFileOutput.buf().is_open()) {
761 mErr << "Could not open file '" << optarg << "'." << std::endl;
762 return IO_ERROR;
763 }
764 chown(optarg, AID_SHELL, AID_SHELL);
765 }
766 break;
767 }
Yifan Hong6da06912017-05-12 16:56:43 -0700768 case 'n': {
769 mNeat = true;
770 break;
771 }
Yifan Hong443df792017-05-09 18:49:45 -0700772 case 'h': // falls through
773 default: // see unrecognized options
774 mLshal.usage(command);
775 return USAGE;
776 }
777 }
778 if (optind < arg.argc) {
779 // see non option
780 mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700781 mLshal.usage(command);
782 return USAGE;
783 }
784
785 if (mNeat && mEmitDebugInfo) {
786 mErr << "Error: --neat should not be used with --debug." << std::endl;
787 mLshal.usage(command);
788 return USAGE;
Yifan Hong443df792017-05-09 18:49:45 -0700789 }
790
791 if (mSelectedColumns == 0) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700792 mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS | ENABLE_THREADS;
Yifan Hong443df792017-05-09 18:49:45 -0700793 }
794 return OK;
795}
796
797Status ListCommand::main(const std::string &command, const Arg &arg) {
798 Status status = parseArgs(command, arg);
799 if (status != OK) {
800 return status;
801 }
802 status = fetch();
803 postprocess();
804 dump();
805 return status;
806}
807
808} // namespace lshal
809} // namespace android