blob: f1f0808f30fc7528a9908bd72286886b596a4f86 [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;
Yifan Hong22ea7b82017-09-14 18:07:43 -070042using ::android::hardware::hidl_vec;
43using ::android::hidl::base::V1_0::DebugInfo;
44using ::android::hidl::base::V1_0::IBase;
Yifan Hong443df792017-05-09 18:49:45 -070045using ::android::hidl::manager::V1_0::IServiceManager;
46
47namespace android {
48namespace lshal {
49
Yifan Hong76ac14a2017-09-08 14:59:04 -070050NullableOStream<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 Hong795b6ec2017-09-13 11:25:28 -070058std::string ListCommand::GetName() {
59 return "list";
60}
61std::string ListCommand::getSimpleDescription() const {
62 return "List HALs.";
63}
64
Yifan Hong8bf73162017-09-07 18:06:13 -070065std::string ListCommand::parseCmdline(pid_t pid) const {
Yifan Hong443df792017-05-09 18:49:45 -070066 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
67 std::string cmdline;
68 if (!ifs.is_open()) {
69 return "";
70 }
71 ifs >> cmdline;
72 return cmdline;
73}
74
75const std::string &ListCommand::getCmdline(pid_t pid) {
76 auto pair = mCmdlines.find(pid);
77 if (pair != mCmdlines.end()) {
78 return pair->second;
79 }
Yifan Hong8bf73162017-09-07 18:06:13 -070080 mCmdlines[pid] = parseCmdline(pid);
Yifan Hong443df792017-05-09 18:49:45 -070081 return mCmdlines[pid];
82}
83
84void ListCommand::removeDeadProcesses(Pids *pids) {
85 static const pid_t myPid = getpid();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070086 pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
Yifan Hong443df792017-05-09 18:49:45 -070087 return pid == myPid || this->getCmdline(pid).empty();
Yifan Hong61fb7bc2017-05-12 16:33:57 -070088 }), pids->end());
Yifan Hong443df792017-05-09 18:49:45 -070089}
90
Yifan Hong1243dde2017-09-14 17:49:30 -070091static bool scanBinderContext(pid_t pid,
Steven Morelandd8e20192017-05-24 11:23:08 -070092 const std::string &contextName,
93 std::function<void(const std::string&)> eachLine) {
94 std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
Yifan Hong443df792017-05-09 18:49:45 -070095 if (!ifs.is_open()) {
96 return false;
97 }
98
Steven Morelandd8e20192017-05-24 11:23:08 -070099 static const std::regex kContextLine("^context (\\w+)$");
Yifan Hong443df792017-05-09 18:49:45 -0700100
Steven Morelandd8e20192017-05-24 11:23:08 -0700101 bool isDesiredContext = false;
Yifan Hong443df792017-05-09 18:49:45 -0700102 std::string line;
103 std::smatch match;
104 while(getline(ifs, line)) {
Steven Morelandd8e20192017-05-24 11:23:08 -0700105 if (std::regex_search(line, match, kContextLine)) {
106 isDesiredContext = match.str(1) == contextName;
Yifan Hong443df792017-05-09 18:49:45 -0700107 continue;
108 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700109
110 if (!isDesiredContext) {
Yifan Hong443df792017-05-09 18:49:45 -0700111 continue;
112 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700113
114 eachLine(line);
Yifan Hong443df792017-05-09 18:49:45 -0700115 }
116 return true;
117}
118
Steven Morelandd8e20192017-05-24 11:23:08 -0700119bool ListCommand::getPidInfo(
120 pid_t serverPid, PidInfo *pidInfo) const {
121 static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
122 static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
123
124 std::smatch match;
125 return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
126 if (std::regex_search(line, match, kReferencePrefix)) {
127 const std::string &ptrString = "0x" + match.str(2); // use number after c
128 uint64_t ptr;
129 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
130 // Should not reach here, but just be tolerant.
Yifan Hong76ac14a2017-09-08 14:59:04 -0700131 err() << "Could not parse number " << ptrString << std::endl;
Steven Morelandd8e20192017-05-24 11:23:08 -0700132 return;
133 }
134 const std::string proc = " proc ";
135 auto pos = line.rfind(proc);
136 if (pos != std::string::npos) {
137 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
138 int32_t pid;
139 if (!::android::base::ParseInt(pidStr, &pid)) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700140 err() << "Could not parse number " << pidStr << std::endl;
Steven Morelandd8e20192017-05-24 11:23:08 -0700141 return;
142 }
143 pidInfo->refPids[ptr].push_back(pid);
144 }
145 }
146
147 return;
148 }
149
150 if (std::regex_search(line, match, kThreadPrefix)) {
151 // "1" is waiting in binder driver
152 // "2" is poll. It's impossible to tell if these are in use.
153 // and HIDL default code doesn't use it.
154 bool isInUse = match.str(1) != "1";
155 // "0" is a thread that has called into binder
156 // "1" is looper thread
157 // "2" is main looper thread
158 bool isHwbinderThread = match.str(2) != "0";
159
160 if (!isHwbinderThread) {
161 return;
162 }
163
164 if (isInUse) {
165 pidInfo->threadUsage++;
166 }
167
168 pidInfo->threadCount++;
169 return;
170 }
171
172 // not reference or thread line
173 return;
174 });
175}
176
Yifan Hong1243dde2017-09-14 17:49:30 -0700177const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
178 auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
179 if (pair.second /* did insertion take place? */) {
180 if (!getPidInfo(serverPid, &pair.first->second)) {
181 return nullptr;
182 }
183 }
184 return &pair.first->second;
185}
186
Yifan Hong443df792017-05-09 18:49:45 -0700187// Must process hwbinder services first, then passthrough services.
188void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
189 f(mServicesTable);
190 f(mPassthroughRefTable);
191 f(mImplementationsTable);
192}
193void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
194 f(mServicesTable);
195 f(mPassthroughRefTable);
196 f(mImplementationsTable);
197}
198
199void ListCommand::postprocess() {
200 forEachTable([this](Table &table) {
201 if (mSortColumn) {
202 std::sort(table.begin(), table.end(), mSortColumn);
203 }
204 for (TableEntry &entry : table) {
205 entry.serverCmdline = getCmdline(entry.serverPid);
206 removeDeadProcesses(&entry.clientPids);
207 for (auto pid : entry.clientPids) {
208 entry.clientCmdlines.push_back(this->getCmdline(pid));
209 }
210 }
211 });
212 // use a double for loop here because lshal doesn't care about efficiency.
213 for (TableEntry &packageEntry : mImplementationsTable) {
214 std::string packageName = packageEntry.interfaceName;
215 FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
216 if (!fqPackageName.isValid()) {
217 continue;
218 }
219 for (TableEntry &interfaceEntry : mPassthroughRefTable) {
220 if (interfaceEntry.arch != ARCH_UNKNOWN) {
221 continue;
222 }
223 FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
224 if (!interfaceName.isValid()) {
225 continue;
226 }
227 if (interfaceName.getPackageAndVersion() == fqPackageName) {
228 interfaceEntry.arch = packageEntry.arch;
229 }
230 }
231 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700232
233 mServicesTable.setDescription(
234 "All binderized services (registered services through hwservicemanager)");
235 mPassthroughRefTable.setDescription(
236 "All interfaces that getService() has ever return as a passthrough interface;\n"
237 "PIDs / processes shown below might be inaccurate because the process\n"
238 "might have relinquished the interface or might have died.\n"
239 "The Server / Server CMD column can be ignored.\n"
240 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
241 "the library and successfully fetched the passthrough implementation.");
242 mImplementationsTable.setDescription(
243 "All available passthrough implementations (all -impl.so files)");
Yifan Hong443df792017-05-09 18:49:45 -0700244}
245
Yifan Hong77c87822017-06-19 15:47:39 -0700246static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
247 for (vintf::Version& v : hal->versions) {
248 if (v.majorVer == version.majorVer) {
249 v.minorVer = std::max(v.minorVer, version.minorVer);
250 return true;
251 }
252 }
253 return false;
254}
255
Yifan Hongca3b6602017-09-07 16:44:27 -0700256void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
Yifan Hong236301c2017-06-19 12:27:08 -0700257 using vintf::operator|=;
Yifan Hongca3b6602017-09-07 16:44:27 -0700258 out << "<!-- " << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700259 << " This is a skeleton device manifest. Notes: " << std::endl
260 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
261 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
262 << " only hwbinder is shown." << std::endl
263 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
264 << " <interface> declared; users will have to write them by hand." << std::endl
Yifan Hong77c87822017-06-19 15:47:39 -0700265 << " 4. A HAL with lower minor version can be overridden by a HAL with" << std::endl
266 << " higher minor version if they have the same name and major version." << std::endl
267 << " 5. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
Yifan Hong443df792017-05-09 18:49:45 -0700268 << " is removed from the manifest file and written by assemble_vintf" << std::endl
269 << " at build time." << std::endl
270 << "-->" << std::endl;
271
272 vintf::HalManifest manifest;
273 forEachTable([this, &manifest] (const Table &table) {
274 for (const TableEntry &entry : table) {
275
276 std::string fqInstanceName = entry.interfaceName;
277
278 if (&table == &mImplementationsTable) {
279 // Quick hack to work around *'s
280 replaceAll(&fqInstanceName, '*', 'D');
281 }
282 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
283 FQName fqName(splittedFqInstanceName.first);
284 if (!fqName.isValid()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700285 err() << "Warning: '" << splittedFqInstanceName.first
Yifan Hong443df792017-05-09 18:49:45 -0700286 << "' is not a valid FQName." << std::endl;
287 continue;
288 }
289 // Strip out system libs.
290 if (fqName.inPackage("android.hidl") ||
291 fqName.inPackage("android.frameworks") ||
292 fqName.inPackage("android.system")) {
293 continue;
294 }
295 std::string interfaceName =
296 &table == &mImplementationsTable ? "" : fqName.name();
297 std::string instanceName =
298 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
299
300 vintf::Version version{fqName.getPackageMajorVersion(),
301 fqName.getPackageMinorVersion()};
302 vintf::Transport transport;
303 vintf::Arch arch;
304 if (entry.transport == "hwbinder") {
305 transport = vintf::Transport::HWBINDER;
306 arch = vintf::Arch::ARCH_EMPTY;
307 } else if (entry.transport == "passthrough") {
308 transport = vintf::Transport::PASSTHROUGH;
309 switch (entry.arch) {
310 case lshal::ARCH32:
311 arch = vintf::Arch::ARCH_32; break;
312 case lshal::ARCH64:
313 arch = vintf::Arch::ARCH_64; break;
314 case lshal::ARCH_BOTH:
315 arch = vintf::Arch::ARCH_32_64; break;
316 case lshal::ARCH_UNKNOWN: // fallthrough
317 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700318 err() << "Warning: '" << fqName.package()
Yifan Hong443df792017-05-09 18:49:45 -0700319 << "' doesn't have bitness info, assuming 32+64." << std::endl;
320 arch = vintf::Arch::ARCH_32_64;
321 }
322 } else {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700323 err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700324 continue;
325 }
326
327 bool done = false;
328 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
329 if (hal->transport() != transport) {
330 if (transport != vintf::Transport::PASSTHROUGH) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700331 err() << "Fatal: should not reach here. Generated result may be wrong for '"
Yifan Hong236301c2017-06-19 12:27:08 -0700332 << hal->name << "'."
Yifan Hong443df792017-05-09 18:49:45 -0700333 << std::endl;
334 }
335 done = true;
336 break;
337 }
Yifan Hong77c87822017-06-19 15:47:39 -0700338 if (findAndBumpVersion(hal, version)) {
Yifan Hong443df792017-05-09 18:49:45 -0700339 if (&table != &mImplementationsTable) {
340 hal->interfaces[interfaceName].name = interfaceName;
341 hal->interfaces[interfaceName].instances.insert(instanceName);
342 }
Yifan Hong236301c2017-06-19 12:27:08 -0700343 hal->transportArch.arch |= arch;
Yifan Hong443df792017-05-09 18:49:45 -0700344 done = true;
345 break;
346 }
347 }
348 if (done) {
349 continue; // to next TableEntry
350 }
351 decltype(vintf::ManifestHal::interfaces) interfaces;
352 if (&table != &mImplementationsTable) {
353 interfaces[interfaceName].name = interfaceName;
354 interfaces[interfaceName].instances.insert(instanceName);
355 }
356 if (!manifest.add(vintf::ManifestHal{
357 .format = vintf::HalFormat::HIDL,
358 .name = fqName.package(),
359 .versions = {version},
360 .transportArch = {transport, arch},
361 .interfaces = interfaces})) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700362 err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700363 }
364 }
365 });
Yifan Hongca3b6602017-09-07 16:44:27 -0700366 out << vintf::gHalManifestConverter(manifest);
Yifan Hong443df792017-05-09 18:49:45 -0700367}
368
Yifan Hong443df792017-05-09 18:49:45 -0700369static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
370 switch (a) {
371 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
372 return ARCH64;
373 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
374 return ARCH32;
375 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
376 default:
377 return ARCH_UNKNOWN;
378 }
379}
380
Yifan Hongca3b6602017-09-07 16:44:27 -0700381void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700382 if (mNeat) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700383 MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
Yifan Hongca3b6602017-09-07 16:44:27 -0700384 .createTextTable().dump(out.buf());
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700385 return;
386 }
387
Yifan Hongca3b6602017-09-07 16:44:27 -0700388 forEachTable([this, &out](const Table &table) {
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700389
Yifan Hongd4a77e82017-09-06 19:40:24 -0700390 // We're only interested in dumping debug info for already
391 // instantiated services. There's little value in dumping the
392 // debug info for a service we create on the fly, so we only operate
393 // on the "mServicesTable".
394 std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
395 if (mEmitDebugInfo && &table == &mServicesTable) {
396 emitDebugInfo = [this](const auto& iName) {
Yifan Hongca3b6602017-09-07 16:44:27 -0700397 std::stringstream ss;
Yifan Hongd4a77e82017-09-06 19:40:24 -0700398 auto pair = splitFirst(iName, '/');
Yifan Hongca3b6602017-09-07 16:44:27 -0700399 mLshal.emitDebugInfo(pair.first, pair.second, {}, ss,
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700400 NullableOStream<std::ostream>(nullptr));
Yifan Hongca3b6602017-09-07 16:44:27 -0700401 return ss.str();
Yifan Hongd4a77e82017-09-06 19:40:24 -0700402 };
Yifan Hong443df792017-05-09 18:49:45 -0700403 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700404 table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
405 out << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700406 });
Yifan Hong443df792017-05-09 18:49:45 -0700407}
408
Yifan Hongca3b6602017-09-07 16:44:27 -0700409Status ListCommand::dump() {
410 auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
411
412 if (mFileOutputPath.empty()) {
413 (*this.*dump)(out());
414 return OK;
Yifan Hong443df792017-05-09 18:49:45 -0700415 }
Yifan Hongca3b6602017-09-07 16:44:27 -0700416
417 std::ofstream fileOutput(mFileOutputPath);
418 if (!fileOutput.is_open()) {
419 err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
420 return IO_ERROR;
421 }
422 chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
423
424 (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
425
426 fileOutput.flush();
427 fileOutput.close();
428 return OK;
Yifan Hong443df792017-05-09 18:49:45 -0700429}
430
431void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
432 Table *table = nullptr;
433 switch (source) {
434 case HWSERVICEMANAGER_LIST :
435 table = &mServicesTable; break;
436 case PTSERVICEMANAGER_REG_CLIENT :
437 table = &mPassthroughRefTable; break;
438 case LIST_DLLIB :
439 table = &mImplementationsTable; break;
440 default:
Yifan Hong76ac14a2017-09-08 14:59:04 -0700441 err() << "Error: Unknown source of entry " << source << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700442 }
443 if (table) {
Yifan Hongd4a77e82017-09-06 19:40:24 -0700444 table->add(std::forward<TableEntry>(entry));
Yifan Hong443df792017-05-09 18:49:45 -0700445 }
446}
447
448Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
449 using namespace ::android::hardware;
450 using namespace ::android::hidl::manager::V1_0;
451 using namespace ::android::hidl::base::V1_0;
Yifan Hongf2d557b2017-05-24 19:45:02 -0700452 using std::literals::chrono_literals::operator""s;
453 auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hong443df792017-05-09 18:49:45 -0700454 std::map<std::string, TableEntry> entries;
455 for (const auto &info : infos) {
456 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
457 std::string{info.instanceName.c_str()};
458 entries.emplace(interfaceName, TableEntry{
459 .interfaceName = interfaceName,
460 .transport = "passthrough",
Yifan Hongf2d557b2017-05-24 19:45:02 -0700461 .clientPids = info.clientPids,
Yifan Hong443df792017-05-09 18:49:45 -0700462 }).first->second.arch |= fromBaseArchitecture(info.arch);
463 }
464 for (auto &&pair : entries) {
465 putEntry(LIST_DLLIB, std::move(pair.second));
466 }
467 });
468 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700469 err() << "Error: Failed to call list on getPassthroughServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700470 << ret.description() << std::endl;
471 return DUMP_ALL_LIBS_ERROR;
472 }
473 return OK;
474}
475
476Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
477 using namespace ::android::hardware;
478 using namespace ::android::hardware::details;
479 using namespace ::android::hidl::manager::V1_0;
480 using namespace ::android::hidl::base::V1_0;
481 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
482 for (const auto &info : infos) {
483 if (info.clientPids.size() <= 0) {
484 continue;
485 }
486 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
487 .interfaceName =
488 std::string{info.interfaceName.c_str()} + "/" +
489 std::string{info.instanceName.c_str()},
490 .transport = "passthrough",
491 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
Yifan Hong443df792017-05-09 18:49:45 -0700492 .clientPids = info.clientPids,
493 .arch = fromBaseArchitecture(info.arch)
494 });
495 }
496 });
497 if (!ret.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700498 err() << "Error: Failed to call debugDump on defaultServiceManager(): "
Yifan Hong443df792017-05-09 18:49:45 -0700499 << ret.description() << std::endl;
500 return DUMP_PASSTHROUGH_ERROR;
501 }
502 return OK;
503}
504
505Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
Yifan Hong443df792017-05-09 18:49:45 -0700506 const std::string mode = "hwbinder";
507
508 hidl_vec<hidl_string> fqInstanceNames;
509 // copying out for timeoutIPC
510 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
511 fqInstanceNames = names;
512 });
513 if (!listRet.isOk()) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700514 err() << "Error: Failed to list services for " << mode << ": "
Yifan Hong443df792017-05-09 18:49:45 -0700515 << listRet.description() << std::endl;
516 return DUMP_BINDERIZED_ERROR;
517 }
518
519 Status status = OK;
Yifan Hong22ea7b82017-09-14 18:07:43 -0700520 std::map<std::string, TableEntry> allTableEntries;
Yifan Hong443df792017-05-09 18:49:45 -0700521 for (const auto &fqInstanceName : fqInstanceNames) {
Yifan Hong22ea7b82017-09-14 18:07:43 -0700522 // create entry and default assign all fields.
523 TableEntry& entry = allTableEntries[fqInstanceName];
524 entry.interfaceName = fqInstanceName;
525 entry.transport = mode;
526
527 status |= fetchBinderizedEntry(manager, &entry);
528 }
529
530 for (auto& pair : allTableEntries) {
531 putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second));
532 }
533 return status;
534}
535
536Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
537 TableEntry *entry) {
538 Status status = OK;
539 const auto handleError = [&](Status additionalError, const std::string& msg) {
540 err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl;
541 status |= DUMP_BINDERIZED_ERROR | additionalError;
542 };
543
544 const auto pair = splitFirst(entry->interfaceName, '/');
545 const auto &serviceName = pair.first;
546 const auto &instanceName = pair.second;
547 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
548 if (!getRet.isOk()) {
549 handleError(TRANSACTION_ERROR,
550 "cannot be fetched from service manager:" + getRet.description());
551 return status;
552 }
553 sp<IBase> service = getRet;
554 if (service == nullptr) {
555 handleError(NO_INTERFACE, "cannot be fetched from service manager (null)");
556 return status;
557 }
558
559 // getDebugInfo
560 do {
561 DebugInfo debugInfo;
562 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
563 debugInfo = received;
Yifan Hong443df792017-05-09 18:49:45 -0700564 });
565 if (!debugRet.isOk()) {
Yifan Hong22ea7b82017-09-14 18:07:43 -0700566 handleError(TRANSACTION_ERROR,
567 "debugging information cannot be retrieved: " + debugRet.description());
568 break; // skip getPidInfo
Yifan Hong443df792017-05-09 18:49:45 -0700569 }
Steven Morelandd8e20192017-05-24 11:23:08 -0700570
Yifan Hong22ea7b82017-09-14 18:07:43 -0700571 entry->serverPid = debugInfo.pid;
572 entry->serverObjectAddress = debugInfo.ptr;
573 entry->arch = fromBaseArchitecture(debugInfo.arch);
Steven Morelandd8e20192017-05-24 11:23:08 -0700574
Yifan Hong22ea7b82017-09-14 18:07:43 -0700575 if (debugInfo.pid != NO_PID) {
576 const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
577 if (pidInfo == nullptr) {
578 handleError(IO_ERROR,
579 "no information for PID " + std::to_string(debugInfo.pid) +
580 ", are you root?");
581 break;
582 }
583 if (debugInfo.ptr != NO_PTR) {
584 auto it = pidInfo->refPids.find(debugInfo.ptr);
585 if (it != pidInfo->refPids.end()) {
586 entry->clientPids = it->second;
587 }
588 }
589 entry->threadUsage = pidInfo->threadUsage;
590 entry->threadCount = pidInfo->threadCount;
591 }
592 } while (0);
Yifan Hong443df792017-05-09 18:49:45 -0700593 return status;
594}
595
596Status ListCommand::fetch() {
597 Status status = OK;
Yifan Hong9881df92017-05-10 14:33:05 -0700598 auto bManager = mLshal.serviceManager();
Yifan Hong443df792017-05-09 18:49:45 -0700599 if (bManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700600 err() << "Failed to get defaultServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700601 status |= NO_BINDERIZED_MANAGER;
602 } else {
603 status |= fetchBinderized(bManager);
604 // Passthrough PIDs are registered to the binderized manager as well.
605 status |= fetchPassthrough(bManager);
606 }
607
Yifan Hong9881df92017-05-10 14:33:05 -0700608 auto pManager = mLshal.passthroughManager();
Yifan Hong443df792017-05-09 18:49:45 -0700609 if (pManager == nullptr) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700610 err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700611 status |= NO_PASSTHROUGH_MANAGER;
612 } else {
613 status |= fetchAllLibraries(pManager);
614 }
615 return status;
616}
617
Yifan Honga6b93f02017-09-13 16:53:37 -0700618void ListCommand::registerAllOptions() {
619 int v = mOptions.size();
620 // A list of acceptable command line options
621 // key: value returned by getopt_long
622 // long options with short alternatives
623 mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) {
624 return USAGE;
625 }, ""});
626 mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) {
627 thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
628 return OK;
629 }, "print the instance name column"});
630 mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
631 thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
632 return OK;
633 }, "print the transport mode column"});
634 mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) {
635 thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
636 return OK;
637 }, "print the bitness column"});
638 mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
639 thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
640 return OK;
641 }, "print the server PID, or server cmdline if -m is set"});
642 mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) {
643 thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
644 return OK;
645 }, "print the server object address column"});
646 mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) {
647 thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
648 return OK;
649 }, "print the client PIDs, or client cmdlines if -m is set"});
650 mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) {
651 thiz->mSelectedColumns.push_back(TableColumnType::THREADS);
652 return OK;
653 }, "print currently used/available threads\n(note, available threads created lazily)"});
654 mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) {
655 thiz->mEnableCmdlines = true;
656 return OK;
657 }, "print cmdline instead of PIDs"});
658 mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) {
659 thiz->mEmitDebugInfo = true;
660 if (arg) thiz->mFileOutputPath = arg;
661 return OK;
662 }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
663 "Writes to specified file if 'arg' is provided, otherwise stdout."});
664
665 // long options without short alternatives
666 mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
667 thiz->mVintf = true;
668 if (arg) thiz->mFileOutputPath = arg;
669 return OK;
670 }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."});
671 mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) {
672 if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) {
673 thiz->mSortColumn = TableEntry::sortByInterfaceName;
674 } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) {
675 thiz->mSortColumn = TableEntry::sortByServerPid;
676 } else {
677 thiz->err() << "Unrecognized sorting column: " << arg << std::endl;
678 return USAGE;
679 }
680 return OK;
681 }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."});
682 mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) {
683 thiz->mNeat = true;
684 return OK;
685 }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
686}
687
688// Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
689// the lifetime of "options" during the usage of the returned array.
690static std::unique_ptr<struct option[]> getLongOptions(
691 const ListCommand::RegisteredOptions& options,
692 int* longOptFlag) {
693 std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
694 int i = 0;
695 for (const auto& e : options) {
696 ret[i].name = e.longOption.c_str();
697 ret[i].has_arg = e.hasArg;
698 ret[i].flag = longOptFlag;
699 ret[i].val = e.val;
700
701 i++;
702 }
703 // getopt_long last option has all zeros
704 ret[i].name = NULL;
705 ret[i].has_arg = 0;
706 ret[i].flag = NULL;
707 ret[i].val = 0;
708
709 return ret;
710}
711
712// Create 'optstring' argument to getopt_long.
713static std::string getShortOptions(const ListCommand::RegisteredOptions& options) {
714 std::stringstream ss;
715 for (const auto& e : options) {
716 if (e.shortOption != '\0') {
717 ss << e.shortOption;
718 }
719 }
720 return ss.str();
721}
722
Yifan Honga8bedc62017-09-08 18:00:31 -0700723Status ListCommand::parseArgs(const Arg &arg) {
Yifan Hong443df792017-05-09 18:49:45 -0700724
Yifan Honga6b93f02017-09-13 16:53:37 -0700725 if (mOptions.empty()) {
726 registerAllOptions();
727 }
728 int longOptFlag;
729 std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag);
730 std::string shortOptions = getShortOptions(mOptions);
Yifan Hongd4a77e82017-09-06 19:40:24 -0700731
Yifan Honga8bedc62017-09-08 18:00:31 -0700732 // suppress output to std::err for unknown options
733 opterr = 0;
734
Yifan Hong443df792017-05-09 18:49:45 -0700735 int optionIndex;
736 int c;
737 // Lshal::parseArgs has set optind to the next option to parse
738 for (;;) {
Yifan Hong443df792017-05-09 18:49:45 -0700739 c = getopt_long(arg.argc, arg.argv,
Yifan Honga6b93f02017-09-13 16:53:37 -0700740 shortOptions.c_str(), longOptions.get(), &optionIndex);
Yifan Hong443df792017-05-09 18:49:45 -0700741 if (c == -1) {
742 break;
743 }
Yifan Honga6b93f02017-09-13 16:53:37 -0700744 const RegisteredOption* found = nullptr;
745 if (c == 0) {
746 // see long option
747 for (const auto& e : mOptions) {
748 if (longOptFlag == e.val) found = &e;
Yifan Hong443df792017-05-09 18:49:45 -0700749 }
Yifan Honga6b93f02017-09-13 16:53:37 -0700750 } else {
751 // see short option
752 for (const auto& e : mOptions) {
753 if (c == e.shortOption) found = &e;
754 }
Yifan Hong443df792017-05-09 18:49:45 -0700755 }
Yifan Honga6b93f02017-09-13 16:53:37 -0700756
757 if (found == nullptr) {
758 // see unrecognized options
Yifan Honga8bedc62017-09-08 18:00:31 -0700759 err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl;
Yifan Hong443df792017-05-09 18:49:45 -0700760 return USAGE;
761 }
Yifan Honga6b93f02017-09-13 16:53:37 -0700762
763 Status status = found->op(this, optarg);
764 if (status != OK) {
765 return status;
766 }
Yifan Hong443df792017-05-09 18:49:45 -0700767 }
768 if (optind < arg.argc) {
769 // see non option
Yifan Honga8bedc62017-09-08 18:00:31 -0700770 err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700771 return USAGE;
772 }
773
774 if (mNeat && mEmitDebugInfo) {
Yifan Hong76ac14a2017-09-08 14:59:04 -0700775 err() << "Error: --neat should not be used with --debug." << std::endl;
Yifan Hong1bc1e9f2017-08-29 17:28:12 -0700776 return USAGE;
Yifan Hong443df792017-05-09 18:49:45 -0700777 }
778
Yifan Honga6b93f02017-09-13 16:53:37 -0700779 if (mSelectedColumns.empty()) {
780 mSelectedColumns = {TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
Yifan Hong05494a52017-08-29 18:50:00 -0700781 TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
Yifan Hong443df792017-05-09 18:49:45 -0700782 }
Yifan Hongd4a77e82017-09-06 19:40:24 -0700783
Yifan Honga6b93f02017-09-13 16:53:37 -0700784 if (mEnableCmdlines) {
785 for (size_t i = 0; i < mSelectedColumns.size(); ++i) {
786 if (mSelectedColumns[i] == TableColumnType::SERVER_PID) {
787 mSelectedColumns[i] = TableColumnType::SERVER_CMD;
Yifan Hongd4a77e82017-09-06 19:40:24 -0700788 }
Yifan Honga6b93f02017-09-13 16:53:37 -0700789 if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) {
790 mSelectedColumns[i] = TableColumnType::CLIENT_CMDS;
Yifan Hongd4a77e82017-09-06 19:40:24 -0700791 }
792 }
793 }
794
Yifan Honga6b93f02017-09-13 16:53:37 -0700795 forEachTable([this] (Table& table) {
796 table.setSelectedColumns(this->mSelectedColumns);
Yifan Hongd4a77e82017-09-06 19:40:24 -0700797 });
798
Yifan Hong443df792017-05-09 18:49:45 -0700799 return OK;
800}
801
Yifan Honga8bedc62017-09-08 18:00:31 -0700802Status ListCommand::main(const Arg &arg) {
803 Status status = parseArgs(arg);
Yifan Hong443df792017-05-09 18:49:45 -0700804 if (status != OK) {
805 return status;
806 }
807 status = fetch();
808 postprocess();
Yifan Hongca3b6602017-09-07 16:44:27 -0700809 status |= dump();
Yifan Hong443df792017-05-09 18:49:45 -0700810 return status;
811}
812
Yifan Honga6b93f02017-09-13 16:53:37 -0700813static std::vector<std::string> splitString(const std::string &s, char c) {
814 std::vector<std::string> components;
815
816 size_t startPos = 0;
817 size_t matchPos;
818 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
819 components.push_back(s.substr(startPos, matchPos - startPos));
820 startPos = matchPos + 1;
821 }
822
823 if (startPos <= s.length()) {
824 components.push_back(s.substr(startPos));
825 }
826 return components;
827}
828
829const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
830 static const std::string empty{};
831 static const std::string optional{"[=<arg>]"};
832 static const std::string required{"=<arg>"};
833
834 if (hasArg == optional_argument) {
835 return optional;
836 }
837 if (hasArg == required_argument) {
838 return required;
839 }
840 return empty;
841}
842
Yifan Honga8bedc62017-09-08 18:00:31 -0700843void ListCommand::usage() const {
844
Yifan Honga6b93f02017-09-13 16:53:37 -0700845 err() << "list:" << std::endl
846 << " lshal" << std::endl
847 << " lshal list" << std::endl
848 << " List all hals with default ordering and columns (`lshal list -iepc`)" << std::endl
849 << " lshal list [-h|--help]" << std::endl
850 << " -h, --help: Print help message for list (`lshal help list`)" << std::endl
851 << " lshal [list] [OPTIONS...]" << std::endl;
852 for (const auto& e : mOptions) {
853 if (e.help.empty()) {
854 continue;
855 }
856 err() << " ";
857 if (e.shortOption != '\0')
858 err() << "-" << e.shortOption << e.getHelpMessageForArgument();
859 if (e.shortOption != '\0' && !e.longOption.empty())
860 err() << ", ";
861 if (!e.longOption.empty())
862 err() << "--" << e.longOption << e.getHelpMessageForArgument();
863 err() << ": ";
864 std::vector<std::string> lines = splitString(e.help, '\n');
865 for (const auto& line : lines) {
866 if (&line != &lines.front())
867 err() << " ";
868 err() << line << std::endl;
869 }
870 }
Yifan Honga8bedc62017-09-08 18:00:31 -0700871}
872
Yifan Hong443df792017-05-09 18:49:45 -0700873} // namespace lshal
874} // namespace android
Yifan Hong05494a52017-08-29 18:50:00 -0700875