blob: 31c42e77765230c9c59bc8ae42474d9fb87ac3d5 [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 Hong05494a52017-08-29 18:50:00 -0700216 for (TableColumnType type : mSelectedColumns) {
217 switch (type) {
218 case TableColumnType::INTERFACE_NAME: {
219 columns.push_back(interfaceName);
220 } break;
221 case TableColumnType::TRANSPORT: {
222 columns.push_back(transport);
223 } break;
224 case TableColumnType::ARCH: {
225 columns.push_back(arch);
226 } break;
227 case TableColumnType::THREADS: {
228 columns.push_back(threadUsage);
229 } break;
230 case TableColumnType::SERVER_ADDR: {
231 columns.push_back(address);
232 } break;
233 case TableColumnType::SERVER_PID: {
234 if (mEnableCmdlines) {
235 columns.push_back(serverCmdline);
236 } else {
237 columns.push_back(server);
238 }
239 } break;
240 case TableColumnType::CLIENT_PIDS: {
241 if (mEnableCmdlines) {
242 columns.push_back(clientCmdlines);
243 } else {
244 columns.push_back(clients);
245 }
246 } break;
Yifan Hong443df792017-05-09 18:49:45 -0700247 }
248 }
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700249 textTable->add(std::move(columns));
Yifan Hong443df792017-05-09 18:49:45 -0700250}
251
Yifan Hong77c87822017-06-19 15:47:39 -0700252static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
253 for (vintf::Version& v : hal->versions) {
254 if (v.majorVer == version.majorVer) {
255 v.minorVer = std::max(v.minorVer, version.minorVer);
256 return true;
257 }
258 }
259 return false;
260}
261
Yifan Hong443df792017-05-09 18:49:45 -0700262void ListCommand::dumpVintf() const {
Yifan Hong236301c2017-06-19 12:27:08 -0700263 using vintf::operator|=;
Yifan Hong443df792017-05-09 18:49:45 -0700264 mOut << "<!-- " << std::endl
265 << " This is a skeleton device manifest. Notes: " << std::endl
266 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
267 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
268 << " only hwbinder is shown." << std::endl
269 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
270 << " <interface> declared; users will have to write them by hand." << std::endl
Yifan Hong77c87822017-06-19 15:47:39 -0700271 << " 4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
272 << " higher minor version if they have the same name and major version." << std::endl
273 << " 5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700274 << " is removed from the manifest file and written by assemble_vintf" << std::endl
275 << " at build time." << std::endl
276 << "-->" << std::endl;
277
278 vintf::HalManifest manifest;
279 forEachTable([this, &manifest] (const Table &table) {
280 for (const TableEntry &entry : table) {
281
282 std::string fqInstanceName = entry.interfaceName;
283
284 if (&table == &mImplementationsTable) {
285 // Quick hack to work around *'s
286 replaceAll(&fqInstanceName, '*', 'D');
287 }
288 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
289 FQName fqName(splittedFqInstanceName.first);
290 if (!fqName.isValid()) {
291 mErr << "Warning: '" << splittedFqInstanceName.first
292 << "' is not a valid FQName." << std::endl;
293 continue;
294 }
295 // Strip out system libs.
296 if (fqName.inPackage("android.hidl") ||
297 fqName.inPackage("android.frameworks") ||
298 fqName.inPackage("android.system")) {
299 continue;
300 }
301 std::string interfaceName =
302 &table == &mImplementationsTable ? "" : fqName.name();
303 std::string instanceName =
304 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
305
306 vintf::Version version{fqName.getPackageMajorVersion(),
307 fqName.getPackageMinorVersion()};
308 vintf::Transport transport;
309 vintf::Arch arch;
310 if (entry.transport == "hwbinder") {
311 transport = vintf::Transport::HWBINDER;
312 arch = vintf::Arch::ARCH_EMPTY;
313 } else if (entry.transport == "passthrough") {
314 transport = vintf::Transport::PASSTHROUGH;
315 switch (entry.arch) {
316 case lshal::ARCH32:
317 arch = vintf::Arch::ARCH_32; break;
318 case lshal::ARCH64:
319 arch = vintf::Arch::ARCH_64; break;
320 case lshal::ARCH_BOTH:
321 arch = vintf::Arch::ARCH_32_64; break;
322 case lshal::ARCH_UNKNOWN: // fallthrough
323 default:
324 mErr << "Warning: '" << fqName.package()
325 << "' doesn't have bitness info, assuming 32+64." << std::endl;
326 arch = vintf::Arch::ARCH_32_64;
327 }
328 } else {
329 mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
330 continue;
331 }
332
333 bool done = false;
334 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
335 if (hal->transport() != transport) {
336 if (transport != vintf::Transport::PASSTHROUGH) {
Yifan Hong236301c2017-06-19 12:27:08 -0700337 mErr << "Fatal: should not reach here. Generated result may be wrong for '"
338 << hal->name << "'."
Yifan Hong443df792017-05-09 18:49:45 -0700339 << std::endl;
340 }
341 done = true;
342 break;
343 }
Yifan Hong77c87822017-06-19 15:47:39 -0700344 if (findAndBumpVersion(hal, version)) {
Yifan Hong443df792017-05-09 18:49:45 -0700345 if (&table != &mImplementationsTable) {
346 hal->interfaces[interfaceName].name = interfaceName;
347 hal->interfaces[interfaceName].instances.insert(instanceName);
348 }
Yifan Hong236301c2017-06-19 12:27:08 -0700349 hal->transportArch.arch |= arch;
Yifan Hong443df792017-05-09 18:49:45 -0700350 done = true;
351 break;
352 }
353 }
354 if (done) {
355 continue; // to next TableEntry
356 }
357 decltype(vintf::ManifestHal::interfaces) interfaces;
358 if (&table != &mImplementationsTable) {
359 interfaces[interfaceName].name = interfaceName;
360 interfaces[interfaceName].instances.insert(instanceName);
361 }
362 if (!manifest.add(vintf::ManifestHal{
363 .format = vintf::HalFormat::HIDL,
364 .name = fqName.package(),
365 .versions = {version},
366 .transportArch = {transport, arch},
367 .interfaces = interfaces})) {
368 mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
369 }
370 }
371 });
372 mOut << vintf::gHalManifestConverter(manifest);
373}
374
375static const std::string &getArchString(Architecture arch) {
376 static const std::string sStr64 = "64";
377 static const std::string sStr32 = "32";
378 static const std::string sStrBoth = "32+64";
379 static const std::string sStrUnknown = "";
380 switch (arch) {
381 case ARCH64:
382 return sStr64;
383 case ARCH32:
384 return sStr32;
385 case ARCH_BOTH:
386 return sStrBoth;
387 case ARCH_UNKNOWN: // fall through
388 default:
389 return sStrUnknown;
390 }
391}
392
393static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
394 switch (a) {
395 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
396 return ARCH64;
397 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
398 return ARCH32;
399 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
400 default:
401 return ARCH_UNKNOWN;
402 }
403}
404
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700405void ListCommand::addLine(TextTable *table, const TableEntry &entry) {
406 addLine(table, entry.interfaceName, entry.transport, getArchString(entry.arch),
407 entry.getThreadUsage(),
408 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
409 entry.serverCmdline,
410 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
411 join(entry.clientPids, " "), join(entry.clientCmdlines, ";"));
412}
413
Yifan Hong443df792017-05-09 18:49:45 -0700414void ListCommand::dumpTable() {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700415 if (mNeat) {
416 TextTable textTable;
417 forEachTable([this, &textTable](const Table &table) {
418 for (const auto &entry : table) addLine(&textTable, entry);
419 });
420 textTable.dump(mOut.buf());
421 return;
422 }
423
Yifan Hong443df792017-05-09 18:49:45 -0700424 mServicesTable.description =
425 "All binderized services (registered services through hwservicemanager)";
426 mPassthroughRefTable.description =
427 "All interfaces that getService() has ever return as a passthrough interface;\n"
428 "PIDs / processes shown below might be inaccurate because the process\n"
429 "might have relinquished the interface or might have died.\n"
430 "The Server / Server CMD column can be ignored.\n"
431 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
432 "the library and successfully fetched the passthrough implementation.";
433 mImplementationsTable.description =
434 "All available passthrough implementations (all -impl.so files)";
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700435
436 forEachTable([this](const Table &table) {
437 TextTable textTable;
438
439 textTable.add(table.description);
440 addLine(&textTable, "Interface", "Transport", "Arch", "Thread Use", "Server", "Server CMD",
441 "PTR", "Clients", "Clients CMD");
Yifan Hong443df792017-05-09 18:49:45 -0700442
Yifan Hong443df792017-05-09 18:49:45 -0700443 for (const auto &entry : table) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700444 addLine(&textTable, entry);
Yifan Hong48dc9f82017-05-09 19:33:08 -0700445 // We're only interested in dumping debug info for already
446 // instantiated services. There's little value in dumping the
447 // debug info for a service we create on the fly, so we only operate
448 // on the "mServicesTable".
449 if (mEmitDebugInfo && &table == &mServicesTable) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700450 std::stringstream out;
Yifan Hong443df792017-05-09 18:49:45 -0700451 auto pair = splitFirst(entry.interfaceName, '/');
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700452 mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
453 NullableOStream<std::ostream>(nullptr));
454 textTable.add(out.str());
Yifan Hong443df792017-05-09 18:49:45 -0700455 }
456 }
Yifan Hong443df792017-05-09 18:49:45 -0700457
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700458 // Add empty line after each table
459 textTable.add();
460
461 textTable.dump(mOut.buf());
462 });
Yifan Hong443df792017-05-09 18:49:45 -0700463}
464
465void ListCommand::dump() {
466 if (mVintf) {
467 dumpVintf();
468 if (!!mFileOutput) {
469 mFileOutput.buf().close();
470 delete &mFileOutput.buf();
471 mFileOutput = nullptr;
472 }
473 mOut = std::cout;
474 } else {
475 dumpTable();
476 }
477}
478
479void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
480 Table *table = nullptr;
481 switch (source) {
482 case HWSERVICEMANAGER_LIST :
483 table = &mServicesTable; break;
484 case PTSERVICEMANAGER_REG_CLIENT :
485 table = &mPassthroughRefTable; break;
486 case LIST_DLLIB :
487 table = &mImplementationsTable; break;
488 default:
489 mErr << "Error: Unknown source of entry " << source << std::endl;
490 }
491 if (table) {
492 table->entries.push_back(std::forward<TableEntry>(entry));
493 }
494}
495
496Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
497 using namespace ::android::hardware;
498 using namespace ::android::hidl::manager::V1_0;
499 using namespace ::android::hidl::base::V1_0;
Yifan Hongf2d557b2017-05-24 19:45:02 -0700500 using std::literals::chrono_literals::operator""s;
501 auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hong443df792017-05-09 18:49:45 -0700502 std::map<std::string, TableEntry> entries;
503 for (const auto &info : infos) {
504 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
505 std::string{info.instanceName.c_str()};
506 entries.emplace(interfaceName, TableEntry{
507 .interfaceName = interfaceName,
508 .transport = "passthrough",
509 .serverPid = NO_PID,
510 .serverObjectAddress = NO_PTR,
Yifan Hongf2d557b2017-05-24 19:45:02 -0700511 .clientPids = info.clientPids,
Yifan Hong443df792017-05-09 18:49:45 -0700512 .arch = ARCH_UNKNOWN
513 }).first->second.arch |= fromBaseArchitecture(info.arch);
514 }
515 for (auto &&pair : entries) {
516 putEntry(LIST_DLLIB, std::move(pair.second));
517 }
518 });
519 if (!ret.isOk()) {
520 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
521 << ret.description() << std::endl;
522 return DUMP_ALL_LIBS_ERROR;
523 }
524 return OK;
525}
526
527Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
528 using namespace ::android::hardware;
529 using namespace ::android::hardware::details;
530 using namespace ::android::hidl::manager::V1_0;
531 using namespace ::android::hidl::base::V1_0;
532 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
533 for (const auto &info : infos) {
534 if (info.clientPids.size() <= 0) {
535 continue;
536 }
537 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
538 .interfaceName =
539 std::string{info.interfaceName.c_str()} + "/" +
540 std::string{info.instanceName.c_str()},
541 .transport = "passthrough",
542 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
543 .serverObjectAddress = NO_PTR,
544 .clientPids = info.clientPids,
545 .arch = fromBaseArchitecture(info.arch)
546 });
547 }
548 });
549 if (!ret.isOk()) {
550 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
551 << ret.description() << std::endl;
552 return DUMP_PASSTHROUGH_ERROR;
553 }
554 return OK;
555}
556
557Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
558 using namespace ::std;
559 using namespace ::android::hardware;
560 using namespace ::android::hidl::manager::V1_0;
561 using namespace ::android::hidl::base::V1_0;
562 const std::string mode = "hwbinder";
563
564 hidl_vec<hidl_string> fqInstanceNames;
565 // copying out for timeoutIPC
566 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
567 fqInstanceNames = names;
568 });
569 if (!listRet.isOk()) {
570 mErr << "Error: Failed to list services for " << mode << ": "
571 << listRet.description() << std::endl;
572 return DUMP_BINDERIZED_ERROR;
573 }
574
575 Status status = OK;
576 // server pid, .ptr value of binder object, child pids
577 std::map<std::string, DebugInfo> allDebugInfos;
Steven Morelandd8e20192017-05-24 11:23:08 -0700578 std::map<pid_t, PidInfo> allPids;
Yifan Hong443df792017-05-09 18:49:45 -0700579 for (const auto &fqInstanceName : fqInstanceNames) {
580 const auto pair = splitFirst(fqInstanceName, '/');
581 const auto &serviceName = pair.first;
582 const auto &instanceName = pair.second;
583 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
584 if (!getRet.isOk()) {
585 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
586 << "cannot be fetched from service manager:"
587 << getRet.description() << std::endl;
588 status |= DUMP_BINDERIZED_ERROR;
589 continue;
590 }
591 sp<IBase> service = getRet;
592 if (service == nullptr) {
593 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
594 << "cannot be fetched from service manager (null)"
595 << std::endl;
596 status |= DUMP_BINDERIZED_ERROR;
597 continue;
598 }
599 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
600 allDebugInfos[fqInstanceName] = debugInfo;
601 if (debugInfo.pid >= 0) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700602 allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
Yifan Hong443df792017-05-09 18:49:45 -0700603 }
604 });
605 if (!debugRet.isOk()) {
606 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
607 << "debugging information cannot be retrieved:"
608 << debugRet.description() << std::endl;
609 status |= DUMP_BINDERIZED_ERROR;
610 }
611 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700612
Yifan Hong443df792017-05-09 18:49:45 -0700613 for (auto &pair : allPids) {
614 pid_t serverPid = pair.first;
Steven Morelandd8e20192017-05-24 11:23:08 -0700615 if (!getPidInfo(serverPid, &allPids[serverPid])) {
Yifan Hong443df792017-05-09 18:49:45 -0700616 mErr << "Warning: no information for PID " << serverPid
617 << ", are you root?" << std::endl;
618 status |= DUMP_BINDERIZED_ERROR;
619 }
620 }
621 for (const auto &fqInstanceName : fqInstanceNames) {
622 auto it = allDebugInfos.find(fqInstanceName);
623 if (it == allDebugInfos.end()) {
624 putEntry(HWSERVICEMANAGER_LIST, {
625 .interfaceName = fqInstanceName,
626 .transport = mode,
627 .serverPid = NO_PID,
628 .serverObjectAddress = NO_PTR,
629 .clientPids = {},
Steven Morelandd8e20192017-05-24 11:23:08 -0700630 .threadUsage = 0,
631 .threadCount = 0,
Yifan Hong443df792017-05-09 18:49:45 -0700632 .arch = ARCH_UNKNOWN
633 });
634 continue;
635 }
636 const DebugInfo &info = it->second;
Steven Morelandd8e20192017-05-24 11:23:08 -0700637 bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
638
Yifan Hong443df792017-05-09 18:49:45 -0700639 putEntry(HWSERVICEMANAGER_LIST, {
640 .interfaceName = fqInstanceName,
641 .transport = mode,
642 .serverPid = info.pid,
643 .serverObjectAddress = info.ptr,
Steven Morelandd8e20192017-05-24 11:23:08 -0700644 .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
645 .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
646 .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
Yifan Hong443df792017-05-09 18:49:45 -0700647 .arch = fromBaseArchitecture(info.arch),
648 });
649 }
650 return status;
651}
652
653Status ListCommand::fetch() {
654 Status status = OK;
Yifan Hong9881df92017-05-10 14:33:05 -0700655 auto bManager = mLshal.serviceManager();
Yifan Hong443df792017-05-09 18:49:45 -0700656 if (bManager == nullptr) {
657 mErr << "Failed to get defaultServiceManager()!" << std::endl;
658 status |= NO_BINDERIZED_MANAGER;
659 } else {
660 status |= fetchBinderized(bManager);
661 // Passthrough PIDs are registered to the binderized manager as well.
662 status |= fetchPassthrough(bManager);
663 }
664
Yifan Hong9881df92017-05-10 14:33:05 -0700665 auto pManager = mLshal.passthroughManager();
Yifan Hong443df792017-05-09 18:49:45 -0700666 if (pManager == nullptr) {
667 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
668 status |= NO_PASSTHROUGH_MANAGER;
669 } else {
670 status |= fetchAllLibraries(pManager);
671 }
672 return status;
673}
674
675Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
676 static struct option longOptions[] = {
677 // long options with short alternatives
678 {"help", no_argument, 0, 'h' },
679 {"interface", no_argument, 0, 'i' },
680 {"transport", no_argument, 0, 't' },
681 {"arch", no_argument, 0, 'r' },
682 {"pid", no_argument, 0, 'p' },
683 {"address", no_argument, 0, 'a' },
684 {"clients", no_argument, 0, 'c' },
Steven Morelandd8e20192017-05-24 11:23:08 -0700685 {"threads", no_argument, 0, 'e' },
Yifan Hong443df792017-05-09 18:49:45 -0700686 {"cmdline", no_argument, 0, 'm' },
687 {"debug", optional_argument, 0, 'd' },
688
689 // long options without short alternatives
690 {"sort", required_argument, 0, 's' },
691 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong6da06912017-05-12 16:56:43 -0700692 {"neat", no_argument, 0, 'n' },
Yifan Hong443df792017-05-09 18:49:45 -0700693 { 0, 0, 0, 0 }
694 };
695
696 int optionIndex;
697 int c;
698 // Lshal::parseArgs has set optind to the next option to parse
699 for (;;) {
700 // using getopt_long in case we want to add other options in the future
701 c = getopt_long(arg.argc, arg.argv,
Steven Morelandd8e20192017-05-24 11:23:08 -0700702 "hitrpacmde", longOptions, &optionIndex);
Yifan Hong443df792017-05-09 18:49:45 -0700703 if (c == -1) {
704 break;
705 }
706 switch (c) {
707 case 's': {
708 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
709 mSortColumn = TableEntry::sortByInterfaceName;
710 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
711 mSortColumn = TableEntry::sortByServerPid;
712 } else {
713 mErr << "Unrecognized sorting column: " << optarg << std::endl;
714 mLshal.usage(command);
715 return USAGE;
716 }
717 break;
718 }
719 case 'v': {
720 if (optarg) {
721 mFileOutput = new std::ofstream{optarg};
722 mOut = mFileOutput;
723 if (!mFileOutput.buf().is_open()) {
724 mErr << "Could not open file '" << optarg << "'." << std::endl;
725 return IO_ERROR;
726 }
727 }
728 mVintf = true;
729 }
730 case 'i': {
Yifan Hong05494a52017-08-29 18:50:00 -0700731 mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
Yifan Hong443df792017-05-09 18:49:45 -0700732 break;
733 }
734 case 't': {
Yifan Hong05494a52017-08-29 18:50:00 -0700735 mSelectedColumns.push_back(TableColumnType::TRANSPORT);
Yifan Hong443df792017-05-09 18:49:45 -0700736 break;
737 }
738 case 'r': {
Yifan Hong05494a52017-08-29 18:50:00 -0700739 mSelectedColumns.push_back(TableColumnType::ARCH);
Yifan Hong443df792017-05-09 18:49:45 -0700740 break;
741 }
742 case 'p': {
Yifan Hong05494a52017-08-29 18:50:00 -0700743 mSelectedColumns.push_back(TableColumnType::SERVER_PID);
Yifan Hong443df792017-05-09 18:49:45 -0700744 break;
745 }
746 case 'a': {
Yifan Hong05494a52017-08-29 18:50:00 -0700747 mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
Yifan Hong443df792017-05-09 18:49:45 -0700748 break;
749 }
750 case 'c': {
Yifan Hong05494a52017-08-29 18:50:00 -0700751 mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
Yifan Hong443df792017-05-09 18:49:45 -0700752 break;
753 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700754 case 'e': {
Yifan Hong05494a52017-08-29 18:50:00 -0700755 mSelectedColumns.push_back(TableColumnType::THREADS);
Steven Morelandd8e20192017-05-24 11:23:08 -0700756 break;
757 }
Yifan Hong443df792017-05-09 18:49:45 -0700758 case 'm': {
759 mEnableCmdlines = true;
760 break;
761 }
762 case 'd': {
763 mEmitDebugInfo = true;
764
765 if (optarg) {
766 mFileOutput = new std::ofstream{optarg};
767 mOut = mFileOutput;
768 if (!mFileOutput.buf().is_open()) {
769 mErr << "Could not open file '" << optarg << "'." << std::endl;
770 return IO_ERROR;
771 }
772 chown(optarg, AID_SHELL, AID_SHELL);
773 }
774 break;
775 }
Yifan Hong6da06912017-05-12 16:56:43 -0700776 case 'n': {
777 mNeat = true;
778 break;
779 }
Yifan Hong443df792017-05-09 18:49:45 -0700780 case 'h': // falls through
781 default: // see unrecognized options
782 mLshal.usage(command);
783 return USAGE;
784 }
785 }
786 if (optind < arg.argc) {
787 // see non option
788 mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700789 mLshal.usage(command);
790 return USAGE;
791 }
792
793 if (mNeat && mEmitDebugInfo) {
794 mErr << "Error: --neat should not be used with --debug." << std::endl;
795 mLshal.usage(command);
796 return USAGE;
Yifan Hong443df792017-05-09 18:49:45 -0700797 }
798
Yifan Hong05494a52017-08-29 18:50:00 -0700799 if (mSelectedColumns.empty()) {
800 mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
801 TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
Yifan Hong443df792017-05-09 18:49:45 -0700802 }
803 return OK;
804}
805
806Status ListCommand::main(const std::string &command, const Arg &arg) {
807 Status status = parseArgs(command, arg);
808 if (status != OK) {
809 return status;
810 }
811 status = fetch();
812 postprocess();
813 dump();
814 return status;
815}
816
817} // namespace lshal
818} // namespace android
Yifan Hong05494a52017-08-29 18:50:00 -0700819