blob: 4550e410a0baec3a0e1edf0eca903aa94f5f3352 [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
Yifan Hong8bf73162017-09-07 18:06:13 -070058std::string ListCommand::parseCmdline(pid_t pid) const {
Yifan Hong443df792017-05-09 18:49:45 -070059 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 }
Yifan Hong8bf73162017-09-07 18:06:13 -070073 mCmdlines[pid] = parseCmdline(pid);
Yifan Hong443df792017-05-09 18:49:45 -070074 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 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700215
216 mServicesTable.setDescription(
217 "All binderized services (registered services through hwservicemanager)");
218 mPassthroughRefTable.setDescription(
219 "All interfaces that getService() has ever return as a passthrough interface;\n"
220 "PIDs / processes shown below might be inaccurate because the process\n"
221 "might have relinquished the interface or might have died.\n"
222 "The Server / Server CMD column can be ignored.\n"
223 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
224 "the library and successfully fetched the passthrough implementation.");
225 mImplementationsTable.setDescription(
226 "All available passthrough implementations (all -impl.so files)");
Yifan Hong443df792017-05-09 18:49:45 -0700227}
228
Yifan Hong77c87822017-06-19 15:47:39 -0700229static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
230 for (vintf::Version& v : hal->versions) {
231 if (v.majorVer == version.majorVer) {
232 v.minorVer = std::max(v.minorVer, version.minorVer);
233 return true;
234 }
235 }
236 return false;
237}
238
Yifan Hongca3b6602017-09-07 16:44:27 -0700239void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
Yifan Hong236301c2017-06-19 12:27:08 -0700240 using vintf::operator|=;
Yifan Hongca3b6602017-09-07 16:44:27 -0700241 out << "<!-- " << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700242 << " This is a skeleton device manifest. Notes: " << std::endl
243 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
244 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
245 << " only hwbinder is shown." << std::endl
246 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
247 << " <interface> declared; users will have to write them by hand." << std::endl
Yifan Hong77c87822017-06-19 15:47:39 -0700248 << " 4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
249 << " higher minor version if they have the same name and major version." << std::endl
250 << " 5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700251 << " is removed from the manifest file and written by assemble_vintf" << std::endl
252 << " at build time." << std::endl
253 << "-->" << std::endl;
254
255 vintf::HalManifest manifest;
256 forEachTable([this, &manifest] (const Table &table) {
257 for (const TableEntry &entry : table) {
258
259 std::string fqInstanceName = entry.interfaceName;
260
261 if (&table == &mImplementationsTable) {
262 // Quick hack to work around *'s
263 replaceAll(&fqInstanceName, '*', 'D');
264 }
265 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
266 FQName fqName(splittedFqInstanceName.first);
267 if (!fqName.isValid()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700268 err() << "Warning: '" << splittedFqInstanceName.first
Yifan Hong443df792017-05-09 18:49:45 -0700269 << "' is not a valid FQName." << std::endl;
270 continue;
271 }
272 // Strip out system libs.
273 if (fqName.inPackage("android.hidl") ||
274 fqName.inPackage("android.frameworks") ||
275 fqName.inPackage("android.system")) {
276 continue;
277 }
278 std::string interfaceName =
279 &table == &mImplementationsTable ? "" : fqName.name();
280 std::string instanceName =
281 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
282
283 vintf::Version version{fqName.getPackageMajorVersion(),
284 fqName.getPackageMinorVersion()};
285 vintf::Transport transport;
286 vintf::Arch arch;
287 if (entry.transport == "hwbinder") {
288 transport = vintf::Transport::HWBINDER;
289 arch = vintf::Arch::ARCH_EMPTY;
290 } else if (entry.transport == "passthrough") {
291 transport = vintf::Transport::PASSTHROUGH;
292 switch (entry.arch) {
293 case lshal::ARCH32:
294 arch = vintf::Arch::ARCH_32; break;
295 case lshal::ARCH64:
296 arch = vintf::Arch::ARCH_64; break;
297 case lshal::ARCH_BOTH:
298 arch = vintf::Arch::ARCH_32_64; break;
299 case lshal::ARCH_UNKNOWN: // fallthrough
300 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700301 err() << "Warning: '" << fqName.package()
Yifan Hong443df792017-05-09 18:49:45 -0700302 << "' doesn't have bitness info, assuming 32+64." << std::endl;
303 arch = vintf::Arch::ARCH_32_64;
304 }
305 } else {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700306 err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700307 continue;
308 }
309
310 bool done = false;
311 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
312 if (hal->transport() != transport) {
313 if (transport != vintf::Transport::PASSTHROUGH) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700314 err() << "Fatal: should not reach here. Generated result may be wrong for '"
Yifan Hong236301c2017-06-19 12:27:08 -0700315 << hal->name << "'."
Yifan Hong443df792017-05-09 18:49:45 -0700316 << std::endl;
317 }
318 done = true;
319 break;
320 }
Yifan Hong77c87822017-06-19 15:47:39 -0700321 if (findAndBumpVersion(hal, version)) {
Yifan Hong443df792017-05-09 18:49:45 -0700322 if (&table != &mImplementationsTable) {
323 hal->interfaces[interfaceName].name = interfaceName;
324 hal->interfaces[interfaceName].instances.insert(instanceName);
325 }
Yifan Hong236301c2017-06-19 12:27:08 -0700326 hal->transportArch.arch |= arch;
Yifan Hong443df792017-05-09 18:49:45 -0700327 done = true;
328 break;
329 }
330 }
331 if (done) {
332 continue; // to next TableEntry
333 }
334 decltype(vintf::ManifestHal::interfaces) interfaces;
335 if (&table != &mImplementationsTable) {
336 interfaces[interfaceName].name = interfaceName;
337 interfaces[interfaceName].instances.insert(instanceName);
338 }
339 if (!manifest.add(vintf::ManifestHal{
340 .format = vintf::HalFormat::HIDL,
341 .name = fqName.package(),
342 .versions = {version},
343 .transportArch = {transport, arch},
344 .interfaces = interfaces})) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700345 err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700346 }
347 }
348 });
Yifan Hongca3b6602017-09-07 16:44:27 -0700349 out << vintf::gHalManifestConverter(manifest);
Yifan Hong443df792017-05-09 18:49:45 -0700350}
351
Yifan Hong443df792017-05-09 18:49:45 -0700352static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
353 switch (a) {
354 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
355 return ARCH64;
356 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
357 return ARCH32;
358 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
359 default:
360 return ARCH_UNKNOWN;
361 }
362}
363
Yifan Hongca3b6602017-09-07 16:44:27 -0700364void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700365 if (mNeat) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700366 MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
Yifan Hongca3b6602017-09-07 16:44:27 -0700367 .createTextTable().dump(out.buf());
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700368 return;
369 }
370
Yifan Hongca3b6602017-09-07 16:44:27 -0700371 forEachTable([this, &out](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 Hongca3b6602017-09-07 16:44:27 -0700380 std::stringstream ss;
Yifan Hongd4a77e82017-09-06 19:40:24 -0700381 auto pair = splitFirst(iName, '/');
Yifan Hongca3b6602017-09-07 16:44:27 -0700382 mLshal.emitDebugInfo(pair.first, pair.second, {}, ss,
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700383 NullableOStream<std::ostream>(nullptr));
Yifan Hongca3b6602017-09-07 16:44:27 -0700384 return ss.str();
Yifan Hongd4a77e82017-09-06 19:40:24 -0700385 };
Yifan Hong443df792017-05-09 18:49:45 -0700386 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700387 table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
388 out << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700389 });
Yifan Hong443df792017-05-09 18:49:45 -0700390}
391
Yifan Hongca3b6602017-09-07 16:44:27 -0700392Status ListCommand::dump() {
393 auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
394
395 if (mFileOutputPath.empty()) {
396 (*this.*dump)(out());
397 return OK;
Yifan Hong443df792017-05-09 18:49:45 -0700398 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700399
400 std::ofstream fileOutput(mFileOutputPath);
401 if (!fileOutput.is_open()) {
402 err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
403 return IO_ERROR;
404 }
405 chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
406
407 (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
408
409 fileOutput.flush();
410 fileOutput.close();
411 return OK;
Yifan Hong443df792017-05-09 18:49:45 -0700412}
413
414void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
415 Table *table = nullptr;
416 switch (source) {
417 case HWSERVICEMANAGER_LIST :
418 table = &mServicesTable; break;
419 case PTSERVICEMANAGER_REG_CLIENT :
420 table = &mPassthroughRefTable; break;
421 case LIST_DLLIB :
422 table = &mImplementationsTable; break;
423 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700424 err() << "Error: Unknown source of entry " << source << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700425 }
426 if (table) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700427 table->add(std::forward<TableEntry>(entry));
Yifan Hong443df792017-05-09 18:49:45 -0700428 }
429}
430
431Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
432 using namespace ::android::hardware;
433 using namespace ::android::hidl::manager::V1_0;
434 using namespace ::android::hidl::base::V1_0;
Yifan Hongf2d557b2017-05-24 19:45:02 -0700435 using std::literals::chrono_literals::operator""s;
436 auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hong443df792017-05-09 18:49:45 -0700437 std::map<std::string, TableEntry> entries;
438 for (const auto &info : infos) {
439 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
440 std::string{info.instanceName.c_str()};
441 entries.emplace(interfaceName, TableEntry{
442 .interfaceName = interfaceName,
443 .transport = "passthrough",
444 .serverPid = NO_PID,
445 .serverObjectAddress = NO_PTR,
Yifan Hongf2d557b2017-05-24 19:45:02 -0700446 .clientPids = info.clientPids,
Yifan Hong443df792017-05-09 18:49:45 -0700447 .arch = ARCH_UNKNOWN
448 }).first->second.arch |= fromBaseArchitecture(info.arch);
449 }
450 for (auto &&pair : entries) {
451 putEntry(LIST_DLLIB, std::move(pair.second));
452 }
453 });
454 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700455 err() << "Error: Failed to call list on getPassthroughServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700456 << ret.description() << std::endl;
457 return DUMP_ALL_LIBS_ERROR;
458 }
459 return OK;
460}
461
462Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
463 using namespace ::android::hardware;
464 using namespace ::android::hardware::details;
465 using namespace ::android::hidl::manager::V1_0;
466 using namespace ::android::hidl::base::V1_0;
467 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
468 for (const auto &info : infos) {
469 if (info.clientPids.size() <= 0) {
470 continue;
471 }
472 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
473 .interfaceName =
474 std::string{info.interfaceName.c_str()} + "/" +
475 std::string{info.instanceName.c_str()},
476 .transport = "passthrough",
477 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
478 .serverObjectAddress = NO_PTR,
479 .clientPids = info.clientPids,
480 .arch = fromBaseArchitecture(info.arch)
481 });
482 }
483 });
484 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700485 err() << "Error: Failed to call debugDump on defaultServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700486 << ret.description() << std::endl;
487 return DUMP_PASSTHROUGH_ERROR;
488 }
489 return OK;
490}
491
492Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
493 using namespace ::std;
494 using namespace ::android::hardware;
495 using namespace ::android::hidl::manager::V1_0;
496 using namespace ::android::hidl::base::V1_0;
497 const std::string mode = "hwbinder";
498
499 hidl_vec<hidl_string> fqInstanceNames;
500 // copying out for timeoutIPC
501 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
502 fqInstanceNames = names;
503 });
504 if (!listRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700505 err() << "Error: Failed to list services for " << mode << ": "
Yifan Hong443df792017-05-09 18:49:45 -0700506 << listRet.description() << std::endl;
507 return DUMP_BINDERIZED_ERROR;
508 }
509
510 Status status = OK;
511 // server pid, .ptr value of binder object, child pids
512 std::map<std::string, DebugInfo> allDebugInfos;
Steven Morelandd8e20192017-05-24 11:23:08 -0700513 std::map<pid_t, PidInfo> allPids;
Yifan Hong443df792017-05-09 18:49:45 -0700514 for (const auto &fqInstanceName : fqInstanceNames) {
515 const auto pair = splitFirst(fqInstanceName, '/');
516 const auto &serviceName = pair.first;
517 const auto &instanceName = pair.second;
518 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
519 if (!getRet.isOk()) {
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:"
522 << getRet.description() << std::endl;
523 status |= DUMP_BINDERIZED_ERROR;
524 continue;
525 }
526 sp<IBase> service = getRet;
527 if (service == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700528 err() << "Warning: Skipping \"" << fqInstanceName << "\": "
Yifan Hong443df792017-05-09 18:49:45 -0700529 << "cannot be fetched from service manager (null)"
530 << std::endl;
531 status |= DUMP_BINDERIZED_ERROR;
532 continue;
533 }
534 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
535 allDebugInfos[fqInstanceName] = debugInfo;
536 if (debugInfo.pid >= 0) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700537 allPids[static_cast<pid_t>(debugInfo.pid)] = PidInfo();
Yifan Hong443df792017-05-09 18:49:45 -0700538 }
539 });
540 if (!debugRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700541 err() << "Warning: Skipping \"" << fqInstanceName << "\": "
Yifan Hong443df792017-05-09 18:49:45 -0700542 << "debugging information cannot be retrieved:"
543 << debugRet.description() << std::endl;
544 status |= DUMP_BINDERIZED_ERROR;
545 }
546 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700547
Yifan Hong443df792017-05-09 18:49:45 -0700548 for (auto &pair : allPids) {
549 pid_t serverPid = pair.first;
Steven Morelandd8e20192017-05-24 11:23:08 -0700550 if (!getPidInfo(serverPid, &allPids[serverPid])) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700551 err() << "Warning: no information for PID " << serverPid
Yifan Hong443df792017-05-09 18:49:45 -0700552 << ", are you root?" << std::endl;
553 status |= DUMP_BINDERIZED_ERROR;
554 }
555 }
556 for (const auto &fqInstanceName : fqInstanceNames) {
557 auto it = allDebugInfos.find(fqInstanceName);
558 if (it == allDebugInfos.end()) {
559 putEntry(HWSERVICEMANAGER_LIST, {
560 .interfaceName = fqInstanceName,
561 .transport = mode,
562 .serverPid = NO_PID,
563 .serverObjectAddress = NO_PTR,
564 .clientPids = {},
Steven Morelandd8e20192017-05-24 11:23:08 -0700565 .threadUsage = 0,
566 .threadCount = 0,
Yifan Hong443df792017-05-09 18:49:45 -0700567 .arch = ARCH_UNKNOWN
568 });
569 continue;
570 }
571 const DebugInfo &info = it->second;
Steven Morelandd8e20192017-05-24 11:23:08 -0700572 bool writePidInfo = info.pid != NO_PID && info.ptr != NO_PTR;
573
Yifan Hong443df792017-05-09 18:49:45 -0700574 putEntry(HWSERVICEMANAGER_LIST, {
575 .interfaceName = fqInstanceName,
576 .transport = mode,
577 .serverPid = info.pid,
578 .serverObjectAddress = info.ptr,
Steven Morelandd8e20192017-05-24 11:23:08 -0700579 .clientPids = writePidInfo ? allPids[info.pid].refPids[info.ptr] : Pids{},
580 .threadUsage = writePidInfo ? allPids[info.pid].threadUsage : 0,
581 .threadCount = writePidInfo ? allPids[info.pid].threadCount : 0,
Yifan Hong443df792017-05-09 18:49:45 -0700582 .arch = fromBaseArchitecture(info.arch),
583 });
584 }
585 return status;
586}
587
588Status ListCommand::fetch() {
589 Status status = OK;
Yifan Hong9881df92017-05-10 14:33:05 -0700590 auto bManager = mLshal.serviceManager();
Yifan Hong443df792017-05-09 18:49:45 -0700591 if (bManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700592 err() << "Failed to get defaultServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700593 status |= NO_BINDERIZED_MANAGER;
594 } else {
595 status |= fetchBinderized(bManager);
596 // Passthrough PIDs are registered to the binderized manager as well.
597 status |= fetchPassthrough(bManager);
598 }
599
Yifan Hong9881df92017-05-10 14:33:05 -0700600 auto pManager = mLshal.passthroughManager();
Yifan Hong443df792017-05-09 18:49:45 -0700601 if (pManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700602 err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700603 status |= NO_PASSTHROUGH_MANAGER;
604 } else {
605 status |= fetchAllLibraries(pManager);
606 }
607 return status;
608}
609
610Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
611 static struct option longOptions[] = {
612 // long options with short alternatives
613 {"help", no_argument, 0, 'h' },
614 {"interface", no_argument, 0, 'i' },
615 {"transport", no_argument, 0, 't' },
616 {"arch", no_argument, 0, 'r' },
617 {"pid", no_argument, 0, 'p' },
618 {"address", no_argument, 0, 'a' },
619 {"clients", no_argument, 0, 'c' },
Steven Morelandd8e20192017-05-24 11:23:08 -0700620 {"threads", no_argument, 0, 'e' },
Yifan Hong443df792017-05-09 18:49:45 -0700621 {"cmdline", no_argument, 0, 'm' },
622 {"debug", optional_argument, 0, 'd' },
623
624 // long options without short alternatives
625 {"sort", required_argument, 0, 's' },
626 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong6da06912017-05-12 16:56:43 -0700627 {"neat", no_argument, 0, 'n' },
Yifan Hong443df792017-05-09 18:49:45 -0700628 { 0, 0, 0, 0 }
629 };
630
Yifan Hongd4a77e82017-09-06 19:40:24 -0700631 std::vector<TableColumnType> selectedColumns;
632 bool enableCmdlines = false;
633
Yifan Hong443df792017-05-09 18:49:45 -0700634 int optionIndex;
635 int c;
636 // Lshal::parseArgs has set optind to the next option to parse
637 for (;;) {
638 // using getopt_long in case we want to add other options in the future
639 c = getopt_long(arg.argc, arg.argv,
Steven Morelandd8e20192017-05-24 11:23:08 -0700640 "hitrpacmde", longOptions, &optionIndex);
Yifan Hong443df792017-05-09 18:49:45 -0700641 if (c == -1) {
642 break;
643 }
644 switch (c) {
645 case 's': {
646 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
647 mSortColumn = TableEntry::sortByInterfaceName;
648 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
649 mSortColumn = TableEntry::sortByServerPid;
650 } else {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700651 err() << "Unrecognized sorting column: " << optarg << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700652 mLshal.usage(command);
653 return USAGE;
654 }
655 break;
656 }
657 case 'v': {
Yifan Hong443df792017-05-09 18:49:45 -0700658 mVintf = true;
Yifan Hongca3b6602017-09-07 16:44:27 -0700659 if (optarg) mFileOutputPath = optarg;
660 break;
Yifan Hong443df792017-05-09 18:49:45 -0700661 }
662 case 'i': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700663 selectedColumns.push_back(TableColumnType::INTERFACE_NAME);
Yifan Hong443df792017-05-09 18:49:45 -0700664 break;
665 }
666 case 't': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700667 selectedColumns.push_back(TableColumnType::TRANSPORT);
Yifan Hong443df792017-05-09 18:49:45 -0700668 break;
669 }
670 case 'r': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700671 selectedColumns.push_back(TableColumnType::ARCH);
Yifan Hong443df792017-05-09 18:49:45 -0700672 break;
673 }
674 case 'p': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700675 selectedColumns.push_back(TableColumnType::SERVER_PID);
Yifan Hong443df792017-05-09 18:49:45 -0700676 break;
677 }
678 case 'a': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700679 selectedColumns.push_back(TableColumnType::SERVER_ADDR);
Yifan Hong443df792017-05-09 18:49:45 -0700680 break;
681 }
682 case 'c': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700683 selectedColumns.push_back(TableColumnType::CLIENT_PIDS);
Yifan Hong443df792017-05-09 18:49:45 -0700684 break;
685 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700686 case 'e': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700687 selectedColumns.push_back(TableColumnType::THREADS);
Steven Morelandd8e20192017-05-24 11:23:08 -0700688 break;
689 }
Yifan Hong443df792017-05-09 18:49:45 -0700690 case 'm': {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700691 enableCmdlines = true;
Yifan Hong443df792017-05-09 18:49:45 -0700692 break;
693 }
694 case 'd': {
695 mEmitDebugInfo = true;
Yifan Hongca3b6602017-09-07 16:44:27 -0700696 if (optarg) mFileOutputPath = optarg;
Yifan Hong443df792017-05-09 18:49:45 -0700697 break;
698 }
Yifan Hong6da06912017-05-12 16:56:43 -0700699 case 'n': {
700 mNeat = true;
701 break;
702 }
Yifan Hong443df792017-05-09 18:49:45 -0700703 case 'h': // falls through
704 default: // see unrecognized options
705 mLshal.usage(command);
706 return USAGE;
707 }
708 }
709 if (optind < arg.argc) {
710 // see non option
Yifan Hong76ac14a2017-09-08 14:59:04 -0700711 err() << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700712 mLshal.usage(command);
713 return USAGE;
714 }
715
716 if (mNeat && mEmitDebugInfo) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700717 err() << "Error: --neat should not be used with --debug." << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700718 mLshal.usage(command);
719 return USAGE;
Yifan Hong443df792017-05-09 18:49:45 -0700720 }
721
Yifan Hongd4a77e82017-09-06 19:40:24 -0700722 if (selectedColumns.empty()) {
723 selectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
Yifan Hong05494a52017-08-29 18:50:00 -0700724 TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
Yifan Hong443df792017-05-09 18:49:45 -0700725 }
Yifan Hongd4a77e82017-09-06 19:40:24 -0700726
727 if (enableCmdlines) {
728 for (size_t i = 0; i < selectedColumns.size(); ++i) {
729 if (selectedColumns[i] == TableColumnType::SERVER_PID) {
730 selectedColumns[i] = TableColumnType::SERVER_CMD;
731 }
732 if (selectedColumns[i] == TableColumnType::CLIENT_PIDS) {
733 selectedColumns[i] = TableColumnType::CLIENT_CMDS;
734 }
735 }
736 }
737
738 forEachTable([&selectedColumns] (Table& table) {
739 table.setSelectedColumns(selectedColumns);
740 });
741
Yifan Hong443df792017-05-09 18:49:45 -0700742 return OK;
743}
744
745Status ListCommand::main(const std::string &command, const Arg &arg) {
746 Status status = parseArgs(command, arg);
747 if (status != OK) {
748 return status;
749 }
750 status = fetch();
751 postprocess();
Yifan Hongca3b6602017-09-07 16:44:27 -0700752 status |= dump();
Yifan Hong443df792017-05-09 18:49:45 -0700753 return status;
754}
755
756} // namespace lshal
757} // namespace android
Yifan Hong05494a52017-08-29 18:50:00 -0700758