blob: e6d10bfc060fc8b2c3b46c9146f7f9b2b6b0b2c5 [file] [log] [blame]
Yifan Hongb0dde932017-02-10 17:49:58 -08001/*
2 * Copyright (C) 2016 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 "Lshal.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>
30#include <hidl/ServiceManagement.h>
31
32using ::android::hardware::hidl_string;
33using ::android::hidl::manager::V1_0::IServiceManager;
34
35namespace android {
36namespace lshal {
37
38template <typename A, typename C, typename D, typename E, typename F>
39void printColumn(std::ostream &stream,
40 const A &a, const C &c, const D &d, const E &, const F &f) {
41 using namespace ::std;
42 stream << left
43 << setw(80) << a << "\t"
44 << setw(10) << c << "\t"
45 << setw(5) << d << "\t"
46 // TODO(b/34984175): enable selecting columns
47 // << setw(16) << e << "\t"
48 << setw(0) << f
49 << endl;
50}
51
52template <typename A>
53std::string join(const A &components, const std::string &separator) {
54 std::stringstream out;
55 bool first = true;
56 for (const auto &component : components) {
57 if (!first) {
58 out << separator;
59 }
60 out << component;
61
62 first = false;
63 }
64 return out.str();
65}
66
67static std::string toHexString(uint64_t t) {
68 std::ostringstream os;
69 os << std::hex << std::setfill('0') << std::setw(16) << t;
70 return os.str();
71}
72
73static std::pair<hidl_string, hidl_string> split(const hidl_string &s, char c) {
74 const char *pos = strchr(s.c_str(), c);
75 if (pos == nullptr) {
76 return {s, {}};
77 }
78 return {hidl_string(s.c_str(), pos - s.c_str()), hidl_string(pos + 1)};
79}
80
81static std::vector<std::string> split(const std::string &s, char c) {
82 std::vector<std::string> components{};
83 size_t startPos = 0;
84 size_t matchPos;
85 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
86 components.push_back(s.substr(startPos, matchPos - startPos));
87 startPos = matchPos + 1;
88 }
89
90 if (startPos <= s.length()) {
91 components.push_back(s.substr(startPos));
92 }
93 return components;
94}
95
96bool Lshal::getReferencedPids(
97 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
98
99 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
100 if (!ifs.is_open()) {
101 return false;
102 }
103
104 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
105
106 std::string line;
107 std::smatch match;
108 while(getline(ifs, line)) {
109 if (!std::regex_search(line, match, prefix)) {
110 // the line doesn't start with the correct prefix
111 continue;
112 }
113 std::string ptrString = "0x" + match.str(2); // use number after c
114 uint64_t ptr;
115 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
116 // Should not reach here, but just be tolerant.
117 mErr << "Could not parse number " << ptrString << std::endl;
118 continue;
119 }
120 const std::string proc = " proc ";
121 auto pos = line.rfind(proc);
122 if (pos != std::string::npos) {
123 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
124 int32_t pid;
125 if (!::android::base::ParseInt(pidStr, &pid)) {
126 mErr << "Could not parse number " << pidStr << std::endl;
127 continue;
128 }
129 (*objects)[ptr].push_back(pid);
130 }
131 }
132 }
133 return true;
134}
135
136void Lshal::dump() const {
137 mOut << "All services:" << std::endl;
138 printColumn(mOut, "Interface", "Transport", "Server", "PTR", "Clients");
139 for (const auto &entry : mTable) {
140 printColumn(mOut, entry.interfaceName,
141 entry.transport,
142 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
143 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
144 join(entry.clientPids, " "));
145 }
146}
147
148void Lshal::putEntry(TableEntry &&entry) {
149 mTable.push_back(std::forward<TableEntry>(entry));
150}
151
152Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
153 using namespace ::android::hardware;
154 using namespace ::android::hidl::manager::V1_0;
155 using namespace ::android::hidl::base::V1_0;
156 auto ret = manager->list([&] (const auto &fqInstanceNames) {
157 for (const auto &fqInstanceName : fqInstanceNames) {
158 putEntry({
159 .interfaceName = fqInstanceName,
160 .transport = "passthrough",
161 .serverPid = NO_PID,
162 .serverObjectAddress = NO_PTR,
163 .clientPids = {}
164 });
165 }
166 });
167 if (!ret.isOk()) {
168 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
169 << ret.description() << std::endl;
170 return DUMP_ALL_LIBS_ERROR;
171 }
172 return OK;
173}
174
175Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
176 using namespace ::android::hardware;
177 using namespace ::android::hidl::manager::V1_0;
178 using namespace ::android::hidl::base::V1_0;
179 auto ret = manager->debugDump([&] (const auto &infos) {
180 for (const auto &info : infos) {
181 putEntry({
182 .interfaceName =
183 std::string{info.interfaceName.c_str()} + "/" +
184 std::string{info.instanceName.c_str()},
185 .transport = "passthrough",
186 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
187 .serverObjectAddress = NO_PTR,
188 .clientPids = info.clientPids
189 });
190 }
191 });
192 if (!ret.isOk()) {
193 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
194 << ret.description() << std::endl;
195 return DUMP_PASSTHROUGH_ERROR;
196 }
197 return OK;
198}
199
200Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
201 using namespace ::std;
202 using namespace ::android::hardware;
203 using namespace ::android::hidl::manager::V1_0;
204 using namespace ::android::hidl::base::V1_0;
205 const std::string mode = "hwbinder";
206 Status status = OK;
207 auto listRet = manager->list([&] (const auto &fqInstanceNames) {
208 // server pid, .ptr value of binder object, child pids
209 std::map<std::string, DebugInfo> allDebugInfos;
210 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
211 for (const auto &fqInstanceName : fqInstanceNames) {
212 const auto pair = split(fqInstanceName, '/');
213 const auto &serviceName = pair.first;
214 const auto &instanceName = pair.second;
215 auto getRet = manager->get(serviceName, instanceName);
216 if (!getRet.isOk()) {
217 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
218 << "cannot be fetched from service manager:"
219 << getRet.description() << std::endl;
220 status |= DUMP_BINDERIZED_ERROR;
221 continue;
222 }
223 sp<IBase> service = getRet;
224 if (service == nullptr) {
225 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
226 << "cannot be fetched from service manager (null)";
227 status |= DUMP_BINDERIZED_ERROR;
228 continue;
229 }
230 auto debugRet = service->getDebugInfo([&] (const auto &debugInfo) {
231 allDebugInfos[fqInstanceName] = debugInfo;
232 if (debugInfo.pid >= 0) {
233 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
234 }
235 });
236 if (!debugRet.isOk()) {
237 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
238 << "debugging information cannot be retrieved:"
239 << debugRet.description() << std::endl;
240 status |= DUMP_BINDERIZED_ERROR;
241 }
242 }
243 for (auto &pair : allPids) {
244 pid_t serverPid = pair.first;
245 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
246 mErr << "Warning: no information for PID " << serverPid
247 << ", are you root?" << std::endl;
248 status |= DUMP_BINDERIZED_ERROR;
249 }
250 }
251 for (const auto &fqInstanceName : fqInstanceNames) {
252 auto it = allDebugInfos.find(fqInstanceName);
253 if (it == allDebugInfos.end()) {
254 putEntry({
255 .interfaceName = fqInstanceName,
256 .transport = mode,
257 .serverPid = NO_PID,
258 .serverObjectAddress = NO_PTR,
259 .clientPids = {}
260 });
261 continue;
262 }
263 const DebugInfo &info = it->second;
264 putEntry({
265 .interfaceName = fqInstanceName,
266 .transport = mode,
267 .serverPid = info.pid,
268 .serverObjectAddress = info.ptr,
269 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
270 ? Pids{} : allPids[info.pid][info.ptr]
271 });
272 }
273
274 });
275 if (!listRet.isOk()) {
276 mErr << "Error: Failed to list services for " << mode << ": "
277 << listRet.description() << std::endl;
278 status |= DUMP_BINDERIZED_ERROR;
279 }
280 return status;
281}
282
283Status Lshal::fetch() {
284 Status status = OK;
285 auto bManager = ::android::hardware::defaultServiceManager();
286 if (bManager == nullptr) {
287 mErr << "Failed to get defaultServiceManager()!" << std::endl;
288 status |= NO_BINDERIZED_MANAGER;
289 } else {
290 status |= fetchBinderized(bManager);
291 // Passthrough PIDs are registered to the binderized manager as well.
292 status |= fetchPassthrough(bManager);
293 }
294
295 auto pManager = ::android::hardware::getPassthroughServiceManager();
296 if (pManager == nullptr) {
297 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
298 status |= NO_PASSTHROUGH_MANAGER;
299 } else {
300 status |= fetchAllLibraries(pManager);
301 }
302 return status;
303}
304
305void Lshal::usage() const {
306 mErr
307 << "usage: lshal" << std::endl
308 << " To dump all hals." << std::endl
309 << "or:" << std::endl
310 << " lshal [-h|--help]" << std::endl
311 << " -h, --help: show this help information." << std::endl;
312}
313
314Status Lshal::parseArgs(int argc, char **argv) {
315 static struct option longOptions[] = {
316 {"help", no_argument, 0, 'h' },
317 { 0, 0, 0, 0 }
318 };
319
320 int optionIndex;
321 int c;
322 optind = 1;
323 for (;;) {
324 // using getopt_long in case we want to add other options in the future
325 c = getopt_long(argc, argv, "h", longOptions, &optionIndex);
326 if (c == -1) {
327 break;
328 }
329 switch (c) {
330 case 'h': // falls through
331 default: // see unrecognized options
332 usage();
333 return USAGE;
334 }
335 }
336 return OK;
337}
338
339int Lshal::main(int argc, char **argv) {
340 Status status = parseArgs(argc, argv);
341 if (status != OK) {
342 return status;
343 }
344 status = fetch();
345 dump();
346 return status;
347}
348
349} // namespace lshal
350} // namespace android
351
352int main(int argc, char **argv) {
353 return ::android::lshal::Lshal{}.main(argc, argv);
354}