blob: 4de84b7c3f3327a41f1f5fe39f77f6a708972eee [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"
38#include "Timeout.h"
39#include "utils.h"
40
41using ::android::hardware::hidl_string;
42using ::android::hidl::manager::V1_0::IServiceManager;
43
44namespace android {
45namespace lshal {
46
Yifan Hong76ac14a2017-09-08 14:59:04 -070047ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal) {
48}
49
50NullableOStream<std::ostream> ListCommand::out() const {
51 return mLshal.out();
52}
53
54NullableOStream<std::ostream> ListCommand::err() const {
55 return mLshal.err();
Yifan Hong443df792017-05-09 18:49:45 -070056}
57
58std::string getCmdline(pid_t pid) {
59 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
60 std::string cmdline;
61 if (!ifs.is_open()) {
62 return "";
63 }
64 ifs >> cmdline;
65 return cmdline;
66}
67
68const std::string &ListCommand::getCmdline(pid_t pid) {
69 auto pair = mCmdlines.find(pid);
70 if (pair != mCmdlines.end()) {
71 return pair->second;
72 }
73 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
74 return mCmdlines[pid];
75}
76
77void ListCommand::removeDeadProcesses(Pids *pids) {
78 static const pid_t myPid = getpid();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070079 pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
Yifan Hong443df792017-05-09 18:49:45 -070080 return pid == myPid || this->getCmdline(pid).empty();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070081 }), pids->end());
Yifan Hong443df792017-05-09 18:49:45 -070082}
83
Steven Morelandd8e20192017-05-24 11:23:08 -070084bool scanBinderContext(pid_t pid,
85 const std::string &contextName,
86 std::function<void(const std::string&)> eachLine) {
87 std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
Yifan Hong443df792017-05-09 18:49:45 -070088 if (!ifs.is_open()) {
89 return false;
90 }
91
Steven Morelandd8e20192017-05-24 11:23:08 -070092 static const std::regex kContextLine("^context (\\w+)$");
Yifan Hong443df792017-05-09 18:49:45 -070093
Steven Morelandd8e20192017-05-24 11:23:08 -070094 bool isDesiredContext = false;
Yifan Hong443df792017-05-09 18:49:45 -070095 std::string line;
96 std::smatch match;
97 while(getline(ifs, line)) {
Steven Morelandd8e20192017-05-24 11:23:08 -070098 if (std::regex_search(line, match, kContextLine)) {
99 isDesiredContext = match.str(1) == contextName;
Yifan Hong443df792017-05-09 18:49:45 -0700100 continue;
101 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700102
103 if (!isDesiredContext) {
Yifan Hong443df792017-05-09 18:49:45 -0700104 continue;
105 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700106
107 eachLine(line);
Yifan Hong443df792017-05-09 18:49:45 -0700108 }
109 return true;
110}
111
Steven Morelandd8e20192017-05-24 11:23:08 -0700112bool ListCommand::getPidInfo(
113 pid_t serverPid, PidInfo *pidInfo) const {
114 static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
115 static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
116
117 std::smatch match;
118 return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
119 if (std::regex_search(line, match, kReferencePrefix)) {
120 const std::string &ptrString = "0x" + match.str(2); // use number after c
121 uint64_t ptr;
122 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
123 // Should not reach here, but just be tolerant.
Yifan Hong76ac14a2017-09-08 14:59:04 -0700124 err() << "Could not parse number " << ptrString << std::endl;
Steven Morelandd8e20192017-05-24 11:23:08 -0700125 return;
126 }
127 const std::string proc = " proc ";
128 auto pos = line.rfind(proc);
129 if (pos != std::string::npos) {
130 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
131 int32_t pid;
132 if (!::android::base::ParseInt(pidStr, &pid)) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700133 err() << "Could not parse number " << pidStr << std::endl;
Steven Morelandd8e20192017-05-24 11:23:08 -0700134 return;
135 }
136 pidInfo->refPids[ptr].push_back(pid);
137 }
138 }
139
140 return;
141 }
142
143 if (std::regex_search(line, match, kThreadPrefix)) {
144 // "1" is waiting in binder driver
145 // "2" is poll. It's impossible to tell if these are in use.
146 // and HIDL default code doesn't use it.
147 bool isInUse = match.str(1) != "1";
148 // "0" is a thread that has called into binder
149 // "1" is looper thread
150 // "2" is main looper thread
151 bool isHwbinderThread = match.str(2) != "0";
152
153 if (!isHwbinderThread) {
154 return;
155 }
156
157 if (isInUse) {
158 pidInfo->threadUsage++;
159 }
160
161 pidInfo->threadCount++;
162 return;
163 }
164
165 // not reference or thread line
166 return;
167 });
168}
169
Yifan Hong443df792017-05-09 18:49:45 -0700170// Must process hwbinder services first, then passthrough services.
171void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
172 f(mServicesTable);
173 f(mPassthroughRefTable);
174 f(mImplementationsTable);
175}
176void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
177 f(mServicesTable);
178 f(mPassthroughRefTable);
179 f(mImplementationsTable);
180}
181
182void ListCommand::postprocess() {
183 forEachTable([this](Table &table) {
184 if (mSortColumn) {
185 std::sort(table.begin(), table.end(), mSortColumn);
186 }
187 for (TableEntry &entry : table) {
188 entry.serverCmdline = getCmdline(entry.serverPid);
189 removeDeadProcesses(&entry.clientPids);
190 for (auto pid : entry.clientPids) {
191 entry.clientCmdlines.push_back(this->getCmdline(pid));
192 }
193 }
194 });
195 // use a double for loop here because lshal doesn't care about efficiency.
196 for (TableEntry &packageEntry : mImplementationsTable) {
197 std::string packageName = packageEntry.interfaceName;
198 FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
199 if (!fqPackageName.isValid()) {
200 continue;
201 }
202 for (TableEntry &interfaceEntry : mPassthroughRefTable) {
203 if (interfaceEntry.arch != ARCH_UNKNOWN) {
204 continue;
205 }
206 FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
207 if (!interfaceName.isValid()) {
208 continue;
209 }
210 if (interfaceName.getPackageAndVersion() == fqPackageName) {
211 interfaceEntry.arch = packageEntry.arch;
212 }
213 }
214 }
215}
216
Yifan Hong77c87822017-06-19 15:47:39 -0700217static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
218 for (vintf::Version& v : hal->versions) {
219 if (v.majorVer == version.majorVer) {
220 v.minorVer = std::max(v.minorVer, version.minorVer);
221 return true;
222 }
223 }
224 return false;
225}
226
Yifan Hong443df792017-05-09 18:49:45 -0700227void ListCommand::dumpVintf() const {
Yifan Hong236301c2017-06-19 12:27:08 -0700228 using vintf::operator|=;
Yifan Hong443df792017-05-09 18:49:45 -0700229 mOut << "<!-- " << std::endl
230 << " This is a skeleton device manifest. Notes: " << std::endl
231 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
232 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
233 << " only hwbinder is shown." << std::endl
234 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
235 << " <interface> declared; users will have to write them by hand." << std::endl
Yifan Hong77c87822017-06-19 15:47:39 -0700236 << " 4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
237 << " higher minor version if they have the same name and major version." << std::endl
238 << " 5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700239 << " is removed from the manifest file and written by assemble_vintf" << std::endl
240 << " at build time." << std::endl
241 << "-->" << std::endl;
242
243 vintf::HalManifest manifest;
244 forEachTable([this, &manifest] (const Table &table) {
245 for (const TableEntry &entry : table) {
246
247 std::string fqInstanceName = entry.interfaceName;
248
249 if (&table == &mImplementationsTable) {
250 // Quick hack to work around *'s
251 replaceAll(&fqInstanceName, '*', 'D');
252 }
253 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
254 FQName fqName(splittedFqInstanceName.first);
255 if (!fqName.isValid()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700256 err() << "Warning: '" << splittedFqInstanceName.first
Yifan Hong443df792017-05-09 18:49:45 -0700257 << "' is not a valid FQName." << std::endl;
258 continue;
259 }
260 // Strip out system libs.
261 if (fqName.inPackage("android.hidl") ||
262 fqName.inPackage("android.frameworks") ||
263 fqName.inPackage("android.system")) {
264 continue;
265 }
266 std::string interfaceName =
267 &table == &mImplementationsTable ? "" : fqName.name();
268 std::string instanceName =
269 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
270
271 vintf::Version version{fqName.getPackageMajorVersion(),
272 fqName.getPackageMinorVersion()};
273 vintf::Transport transport;
274 vintf::Arch arch;
275 if (entry.transport == "hwbinder") {
276 transport = vintf::Transport::HWBINDER;
277 arch = vintf::Arch::ARCH_EMPTY;
278 } else if (entry.transport == "passthrough") {
279 transport = vintf::Transport::PASSTHROUGH;
280 switch (entry.arch) {
281 case lshal::ARCH32:
282 arch = vintf::Arch::ARCH_32; break;
283 case lshal::ARCH64:
284 arch = vintf::Arch::ARCH_64; break;
285 case lshal::ARCH_BOTH:
286 arch = vintf::Arch::ARCH_32_64; break;
287 case lshal::ARCH_UNKNOWN: // fallthrough
288 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700289 err() << "Warning: '" << fqName.package()
Yifan Hong443df792017-05-09 18:49:45 -0700290 << "' doesn't have bitness info, assuming 32+64." << std::endl;
291 arch = vintf::Arch::ARCH_32_64;
292 }
293 } else {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700294 err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700295 continue;
296 }
297
298 bool done = false;
299 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
300 if (hal->transport() != transport) {
301 if (transport != vintf::Transport::PASSTHROUGH) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700302 err() << "Fatal: should not reach here. Generated result may be wrong for '"
Yifan Hong236301c2017-06-19 12:27:08 -0700303 << hal->name << "'."
Yifan Hong443df792017-05-09 18:49:45 -0700304 << std::endl;
305 }
306 done = true;
307 break;
308 }
Yifan Hong77c87822017-06-19 15:47:39 -0700309 if (findAndBumpVersion(hal, version)) {
Yifan Hong443df792017-05-09 18:49:45 -0700310 if (&table != &mImplementationsTable) {
311 hal->interfaces[interfaceName].name = interfaceName;
312 hal->interfaces[interfaceName].instances.insert(instanceName);
313 }
Yifan Hong236301c2017-06-19 12:27:08 -0700314 hal->transportArch.arch |= arch;
Yifan Hong443df792017-05-09 18:49:45 -0700315 done = true;
316 break;
317 }
318 }
319 if (done) {
320 continue; // to next TableEntry
321 }
322 decltype(vintf::ManifestHal::interfaces) interfaces;
323 if (&table != &mImplementationsTable) {
324 interfaces[interfaceName].name = interfaceName;
325 interfaces[interfaceName].instances.insert(instanceName);
326 }
327 if (!manifest.add(vintf::ManifestHal{
328 .format = vintf::HalFormat::HIDL,
329 .name = fqName.package(),
330 .versions = {version},
331 .transportArch = {transport, arch},
332 .interfaces = interfaces})) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700333 err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700334 }
335 }
336 });
337 mOut << vintf::gHalManifestConverter(manifest);
338}
339
Yifan Hong443df792017-05-09 18:49:45 -0700340static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
341 switch (a) {
342 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
343 return ARCH64;
344 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
345 return ARCH32;
346 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
347 default:
348 return ARCH_UNKNOWN;
349 }
350}
351
352void ListCommand::dumpTable() {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700353 if (mNeat) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700354 MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
355 .createTextTable().dump(mOut.buf());
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700356 return;
357 }
358
Yifan Hongd4a77e82017-09-06 19:40:24 -0700359 mServicesTable.setDescription(
360 "All binderized services (registered services through hwservicemanager)");
361 mPassthroughRefTable.setDescription(
Yifan Hong443df792017-05-09 18:49:45 -0700362 "All interfaces that getService() has ever return as a passthrough interface;\n"
363 "PIDs / processes shown below might be inaccurate because the process\n"
364 "might have relinquished the interface or might have died.\n"
365 "The Server / Server CMD column can be ignored.\n"
366 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
Yifan Hongd4a77e82017-09-06 19:40:24 -0700367 "the library and successfully fetched the passthrough implementation.");
368 mImplementationsTable.setDescription(
369 "All available passthrough implementations (all -impl.so files)");
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700370
371 forEachTable([this](const Table &table) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700372
Yifan Hongd4a77e82017-09-06 19:40:24 -0700373 // We're only interested in dumping debug info for already
374 // instantiated services. There's little value in dumping the
375 // debug info for a service we create on the fly, so we only operate
376 // on the "mServicesTable".
377 std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
378 if (mEmitDebugInfo && &table == &mServicesTable) {
379 emitDebugInfo = [this](const auto& iName) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700380 std::stringstream out;
Yifan Hongd4a77e82017-09-06 19:40:24 -0700381 auto pair = splitFirst(iName, '/');
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700382 mLshal.emitDebugInfo(pair.first, pair.second, {}, out,
383 NullableOStream<std::ostream>(nullptr));
Yifan Hongd4a77e82017-09-06 19:40:24 -0700384 return out.str();
385 };
Yifan Hong443df792017-05-09 18:49:45 -0700386 }
Yifan Hongd4a77e82017-09-06 19:40:24 -0700387 table.createTextTable(mNeat, emitDebugInfo).dump(mOut.buf());
388 mOut << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700389 });
Yifan Hong443df792017-05-09 18:49:45 -0700390}
391
392void ListCommand::dump() {
393 if (mVintf) {
394 dumpVintf();
395 if (!!mFileOutput) {
396 mFileOutput.buf().close();
397 delete &mFileOutput.buf();
398 mFileOutput = nullptr;
399 }
400 mOut = std::cout;
401 } else {
402 dumpTable();
403 }
404}
405
406void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
407 Table *table = nullptr;
408 switch (source) {
409 case HWSERVICEMANAGER_LIST :
410 table = &mServicesTable; break;
411 case PTSERVICEMANAGER_REG_CLIENT :
412 table = &mPassthroughRefTable; break;
413 case LIST_DLLIB :
414 table = &mImplementationsTable; break;
415 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700416 err() << "Error: Unknown source of entry " << source << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700417 }
418 if (table) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700419 table->add(std::forward<TableEntry>(entry));
Yifan Hong443df792017-05-09 18:49:45 -0700420 }
421}
422
423Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
424 using namespace ::android::hardware;
425 using namespace ::android::hidl::manager::V1_0;
426 using namespace ::android::hidl::base::V1_0;
Yifan Hongf2d557b2017-05-24 19:45:02 -0700427 using std::literals::chrono_literals::operator""s;
428 auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hong443df792017-05-09 18:49:45 -0700429 std::map<std::string, TableEntry> entries;
430 for (const auto &info : infos) {
431 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
432 std::string{info.instanceName.c_str()};
433 entries.emplace(interfaceName, TableEntry{
434 .interfaceName = interfaceName,
435 .transport = "passthrough",
436 .serverPid = NO_PID,
437 .serverObjectAddress = NO_PTR,
Yifan Hongf2d557b2017-05-24 19:45:02 -0700438 .clientPids = info.clientPids,
Yifan Hong443df792017-05-09 18:49:45 -0700439 .arch = ARCH_UNKNOWN
440 }).first->second.arch |= fromBaseArchitecture(info.arch);
441 }
442 for (auto &&pair : entries) {
443 putEntry(LIST_DLLIB, std::move(pair.second));
444 }
445 });
446 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700447 err() << "Error: Failed to call list on getPassthroughServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700448 << ret.description() << std::endl;
449 return DUMP_ALL_LIBS_ERROR;
450 }
451 return OK;
452}
453
454Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
455 using namespace ::android::hardware;
456 using namespace ::android::hardware::details;
457 using namespace ::android::hidl::manager::V1_0;
458 using namespace ::android::hidl::base::V1_0;
459 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
460 for (const auto &info : infos) {
461 if (info.clientPids.size() <= 0) {
462 continue;
463 }
464 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
465 .interfaceName =
466 std::string{info.interfaceName.c_str()} + "/" +
467 std::string{info.instanceName.c_str()},
468 .transport = "passthrough",
469 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
470 .serverObjectAddress = NO_PTR,
471 .clientPids = info.clientPids,
472 .arch = fromBaseArchitecture(info.arch)
473 });
474 }
475 });
476 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700477 err() << "Error: Failed to call debugDump on defaultServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700478 << ret.description() << std::endl;
479 return DUMP_PASSTHROUGH_ERROR;
480 }
481 return OK;
482}
483
484Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
485 using namespace ::std;
486 using namespace ::android::hardware;
487 using namespace ::android::hidl::manager::V1_0;
488 using namespace ::android::hidl::base::V1_0;
489 const std::string mode = "hwbinder";
490
491 hidl_vec<hidl_string> fqInstanceNames;
492 // copying out for timeoutIPC
493 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
494 fqInstanceNames = names;
495 });
496 if (!listRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700497 err() << "Error: Failed to list services for " << mode << ": "
Yifan Hong443df792017-05-09 18:49:45 -0700498 << listRet.description() << std::endl;
499 return DUMP_BINDERIZED_ERROR;
500 }
501
502 Status status = OK;
503 // server pid, .ptr value of binder object, child pids
504 std::map<std::string, DebugInfo> allDebugInfos;
Steven Morelandd8e20192017-05-24 11:23:08 -0700505 std::map<pid_t, PidInfo> allPids;
Yifan Hong443df792017-05-09 18:49:45 -0700506 for (const auto &fqInstanceName : fqInstanceNames) {
507 const auto pair = splitFirst(fqInstanceName, '/');
508 const auto &serviceName = pair.first;
509 const auto &instanceName = pair.second;
510 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
511 if (!getRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700512 err() << "Warning: Skipping \"" << fqInstanceName << "\": "
Yifan Hong443df792017-05-09 18:49:45 -0700513 << "cannot be fetched from service manager:"
514 << getRet.description() << std::endl;
515 status |= DUMP_BINDERIZED_ERROR;
516 continue;
517 }
518 sp<IBase> service = getRet;
519 if (service == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700520 err() << "Warning: Skipping \"" << fqInstanceName << "\": "
Yifan Hong443df792017-05-09 18:49:45 -0700521 << "cannot be fetched from service manager (null)"
522 << std::endl;
523 status |= DUMP_BINDERIZED_ERROR;
524 continue;
525 }
526 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
527 allDebugInfos[fqInstanceName] = debugInfo;
528 if (debugInfo.pid >= 0) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700529 allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
Yifan Hong443df792017-05-09 18:49:45 -0700530 }
531 });
532 if (!debugRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700533 err() << "Warning: Skipping \"" << fqInstanceName << "\": "
Yifan Hong443df792017-05-09 18:49:45 -0700534 << "debugging information cannot be retrieved:"
535 << debugRet.description() << std::endl;
536 status |= DUMP_BINDERIZED_ERROR;
537 }
538 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700539
Yifan Hong443df792017-05-09 18:49:45 -0700540 for (auto &pair : allPids) {
541 pid_t serverPid = pair.first;
Steven Morelandd8e20192017-05-24 11:23:08 -0700542 if (!getPidInfo(serverPid, &allPids[serverPid])) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700543 err() << "Warning: no information for PID " << serverPid
Yifan Hong443df792017-05-09 18:49:45 -0700544 << ", are you root?" << std::endl;
545 status |= DUMP_BINDERIZED_ERROR;
546 }
547 }
548 for (const auto &fqInstanceName : fqInstanceNames) {
549 auto it = allDebugInfos.find(fqInstanceName);
550 if (it == allDebugInfos.end()) {
551 putEntry(HWSERVICEMANAGER_LIST, {
552 .interfaceName = fqInstanceName,
553 .transport = mode,
554 .serverPid = NO_PID,
555 .serverObjectAddress = NO_PTR,
556 .clientPids = {},
Steven Morelandd8e20192017-05-24 11:23:08 -0700557 .threadUsage = 0,
558 .threadCount = 0,
Yifan Hong443df792017-05-09 18:49:45 -0700559 .arch = ARCH_UNKNOWN
560 });
561 continue;
562 }
563 const DebugInfo &info = it->second;
Steven Morelandd8e20192017-05-24 11:23:08 -0700564 bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
565
Yifan Hong443df792017-05-09 18:49:45 -0700566 putEntry(HWSERVICEMANAGER_LIST, {
567 .interfaceName = fqInstanceName,
568 .transport = mode,
569 .serverPid = info.pid,
570 .serverObjectAddress = info.ptr,
Steven Morelandd8e20192017-05-24 11:23:08 -0700571 .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
572 .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
573 .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
Yifan Hong443df792017-05-09 18:49:45 -0700574 .arch = fromBaseArchitecture(info.arch),
575 });
576 }
577 return status;
578}
579
580Status ListCommand::fetch() {
581 Status status = OK;
Yifan Hong9881df92017-05-10 14:33:05 -0700582 auto bManager = mLshal.serviceManager();
Yifan Hong443df792017-05-09 18:49:45 -0700583 if (bManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700584 err() << "Failed to get defaultServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700585 status |= NO_BINDERIZED_MANAGER;
586 } else {
587 status |= fetchBinderized(bManager);
588 // Passthrough PIDs are registered to the binderized manager as well.
589 status |= fetchPassthrough(bManager);
590 }
591
Yifan Hong9881df92017-05-10 14:33:05 -0700592 auto pManager = mLshal.passthroughManager();
Yifan Hong443df792017-05-09 18:49:45 -0700593 if (pManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700594 err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700595 status |= NO_PASSTHROUGH_MANAGER;
596 } else {
597 status |= fetchAllLibraries(pManager);
598 }
599 return status;
600}
601
602Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
603 static struct option longOptions[] = {
604 // long options with short alternatives
605 {"help", no_argument, 0, 'h' },
606 {"interface", no_argument, 0, 'i' },
607 {"transport", no_argument, 0, 't' },
608 {"arch", no_argument, 0, 'r' },
609 {"pid", no_argument, 0, 'p' },
610 {"address", no_argument, 0, 'a' },
611 {"clients", no_argument, 0, 'c' },
Steven Morelandd8e20192017-05-24 11:23:08 -0700612 {"threads", no_argument, 0, 'e' },
Yifan Hong443df792017-05-09 18:49:45 -0700613 {"cmdline", no_argument, 0, 'm' },
614 {"debug", optional_argument, 0, 'd' },
615
616 // long options without short alternatives
617 {"sort", required_argument, 0, 's' },
618 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong6da06912017-05-12 16:56:43 -0700619 {"neat", no_argument, 0, 'n' },
Yifan Hong443df792017-05-09 18:49:45 -0700620 { 0, 0, 0, 0 }
621 };
622
Yifan Hongd4a77e82017-09-06 19:40:24 -0700623 std::vector<TableColumnType> selectedColumns;
624 bool enableCmdlines = false;
625
Yifan Hong443df792017-05-09 18:49:45 -0700626 int optionIndex;
627 int c;
628 // Lshal::parseArgs has set optind to the next option to parse
629 for (;;) {
630 // using getopt_long in case we want to add other options in the future
631 c = getopt_long(arg.argc, arg.argv,
Steven Morelandd8e20192017-05-24 11:23:08 -0700632 "hitrpacmde", longOptions, &optionIndex);
Yifan Hong443df792017-05-09 18:49:45 -0700633 if (c == -1) {
634 break;
635 }
636 switch (c) {
637 case 's': {
638 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
639 mSortColumn = TableEntry::sortByInterfaceName;
640 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
641 mSortColumn = TableEntry::sortByServerPid;
642 } else {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700643 err() << "Unrecognized sorting column: " << optarg << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700644 mLshal.usage(command);
645 return USAGE;
646 }
647 break;
648 }
649 case 'v': {
650 if (optarg) {
651 mFileOutput = new std::ofstream{optarg};
652 mOut = mFileOutput;
653 if (!mFileOutput.buf().is_open()) {
654 mErr << "Could not open file '" << optarg << "'." << std::endl;
655 return IO_ERROR;
656 }
657 }
658 mVintf = true;
659 }
660 case 'i': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700661 selectedColumns.push_back(TableColumnType::INTERFACE_NAME);
Yifan Hong443df792017-05-09 18:49:45 -0700662 break;
663 }
664 case 't': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700665 selectedColumns.push_back(TableColumnType::TRANSPORT);
Yifan Hong443df792017-05-09 18:49:45 -0700666 break;
667 }
668 case 'r': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700669 selectedColumns.push_back(TableColumnType::ARCH);
Yifan Hong443df792017-05-09 18:49:45 -0700670 break;
671 }
672 case 'p': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700673 selectedColumns.push_back(TableColumnType::SERVER_PID);
Yifan Hong443df792017-05-09 18:49:45 -0700674 break;
675 }
676 case 'a': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700677 selectedColumns.push_back(TableColumnType::SERVER_ADDR);
Yifan Hong443df792017-05-09 18:49:45 -0700678 break;
679 }
680 case 'c': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700681 selectedColumns.push_back(TableColumnType::CLIENT_PIDS);
Yifan Hong443df792017-05-09 18:49:45 -0700682 break;
683 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700684 case 'e': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700685 selectedColumns.push_back(TableColumnType::THREADS);
Steven Morelandd8e20192017-05-24 11:23:08 -0700686 break;
687 }
Yifan Hong443df792017-05-09 18:49:45 -0700688 case 'm': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700689 enableCmdlines = true;
Yifan Hong443df792017-05-09 18:49:45 -0700690 break;
691 }
692 case 'd': {
693 mEmitDebugInfo = true;
694
695 if (optarg) {
696 mFileOutput = new std::ofstream{optarg};
697 mOut = mFileOutput;
698 if (!mFileOutput.buf().is_open()) {
699 mErr << "Could not open file '" << optarg << "'." << std::endl;
700 return IO_ERROR;
701 }
702 chown(optarg, AID_SHELL, AID_SHELL);
703 }
704 break;
705 }
Yifan Hong6da06912017-05-12 16:56:43 -0700706 case 'n': {
707 mNeat = true;
708 break;
709 }
Yifan Hong443df792017-05-09 18:49:45 -0700710 case 'h': // falls through
711 default: // see unrecognized options
712 mLshal.usage(command);
713 return USAGE;
714 }
715 }
716 if (optind < arg.argc) {
717 // see non option
Yifan Hong76ac14a2017-09-08 14:59:04 -0700718 err() << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700719 mLshal.usage(command);
720 return USAGE;
721 }
722
723 if (mNeat && mEmitDebugInfo) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700724 err() << "Error: --neat should not be used with --debug." << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700725 mLshal.usage(command);
726 return USAGE;
Yifan Hong443df792017-05-09 18:49:45 -0700727 }
728
Yifan Hongd4a77e82017-09-06 19:40:24 -0700729 if (selectedColumns.empty()) {
730 selectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
Yifan Hong05494a52017-08-29 18:50:00 -0700731 TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
Yifan Hong443df792017-05-09 18:49:45 -0700732 }
Yifan Hongd4a77e82017-09-06 19:40:24 -0700733
734 if (enableCmdlines) {
735 for (size_t i = 0; i < selectedColumns.size(); ++i) {
736 if (selectedColumns[i] == TableColumnType::SERVER_PID) {
737 selectedColumns[i] = TableColumnType::SERVER_CMD;
738 }
739 if (selectedColumns[i] == TableColumnType::CLIENT_PIDS) {
740 selectedColumns[i] = TableColumnType::CLIENT_CMDS;
741 }
742 }
743 }
744
745 forEachTable([&selectedColumns] (Table& table) {
746 table.setSelectedColumns(selectedColumns);
747 });
748
Yifan Hong443df792017-05-09 18:49:45 -0700749 return OK;
750}
751
752Status ListCommand::main(const std::string &command, const Arg &arg) {
753 Status status = parseArgs(command, arg);
754 if (status != OK) {
755 return status;
756 }
757 status = fetch();
758 postprocess();
759 dump();
760 return status;
761}
762
763} // namespace lshal
764} // namespace android
Yifan Hong05494a52017-08-29 18:50:00 -0700765