1 /*
2 * Copyright (C) 2022 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 <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <binder/BpBinder.h>
20 #include <binder/IServiceManager.h>
21 #include <binder/Parcel.h>
22 #include <binder/RecordedTransaction.h>
23 #include <signal.h>
24 #include <iostream>
25 #include <fstream>
26 #include <sstream>
27 #include "include/Analyzer.h"
28
29 using android::IBinder;
30 using android::NO_ERROR;
31 using android::sp;
32 using android::status_t;
33 using android::String16;
34 using android::String8;
35 using android::aidl::Analyzer;
36 using android::base::unique_fd;
37 using android::binder::debug::RecordedTransaction;
38 using std::string;
39 namespace {
40
41 static std::atomic_uint gCtrlCCount = 0;
42 static constexpr unsigned kCtrlCLimit = 3;
43 static const char kStandardRecordingPath[] = "/data/local/recordings/";
44
getRecordingPath(const string & serviceName)45 string getRecordingPath(const string& serviceName) {
46 // Service names may contain '/', replace them with '.' to avoid interpreting as path
47 string filename = serviceName;
48 std::replace(filename.begin(), filename.end(), '/', '.');
49 return kStandardRecordingPath + filename;
50 }
51
startRecording(const string & serviceName)52 status_t startRecording(const string& serviceName) {
53 sp<IBinder> binder =
54 android::defaultServiceManager()->checkService(String16(serviceName.c_str()));
55 if (binder == nullptr) {
56 return android::BAD_VALUE;
57 }
58
59 auto filePath = getRecordingPath(serviceName);
60 if (auto mkdir_return = mkdir(kStandardRecordingPath, 0666);
61 mkdir_return != 0 && errno != EEXIST) {
62 std::cout << "Failed to create recordings directory.\n";
63 return android::NO_ERROR;
64 }
65
66 int openFlags = O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC | O_BINARY;
67 unique_fd fd(open(filePath.c_str(), openFlags, 0666));
68 if (!fd.ok()) {
69 std::cout << "Failed to open file for recording with error: " << strerror(errno) << '\n';
70 return android::BAD_VALUE;
71 }
72
73 // TODO (b/245804633): this still requires setenforce 0, but nothing above does
74 if (status_t err = binder->remoteBinder()->startRecordingBinder(fd); err != android::NO_ERROR) {
75 auto checkSE = std::ifstream("/sys/fs/selinux/enforce");
76 bool recommendSetenforce = false;
77 if ((checkSE.rdstate() & std::ifstream::failbit) != 0) {
78 std::cout << "Failed to determine selinux state.";
79 recommendSetenforce = true;
80 } else {
81 char seState = checkSE.get();
82 if (checkSE.good()) {
83 if (seState == '1') {
84 std::cout << "SELinux must be permissive.";
85 recommendSetenforce = true;
86 } else if (seState == '0') {
87 std::cout << "SELinux is permissive. Failing for some other reason.\n";
88 }
89 } else {
90 std::cout << "Failed to determine SELinux state.";
91 recommendSetenforce = true;
92 }
93 }
94 if (recommendSetenforce) {
95 std::cout << " Try running:\n\n setenforce 0\n\n";
96 }
97 std::cout << "Failed to start recording with error: " << android::statusToString(err) << '\n';
98 return err;
99 } else {
100 std::cout << "Recording started successfully.\n";
101 return android::NO_ERROR;
102 }
103 }
104
stopRecording(const string & serviceName)105 status_t stopRecording(const string& serviceName) {
106 sp<IBinder> binder =
107 android::defaultServiceManager()->checkService(String16(serviceName.c_str()));
108 if (binder == nullptr) {
109 return android::BAD_VALUE;
110 }
111
112 if (status_t err = binder->remoteBinder()->stopRecordingBinder(); err != NO_ERROR) {
113 std::cout << "Failed to stop recording with error: " << err << '\n';
114 return err;
115 }
116
117 std::cout << "Recording stopped successfully.\n";
118 return NO_ERROR;
119 }
120
printTransaction(const RecordedTransaction & transaction)121 void printTransaction(const RecordedTransaction& transaction) {
122 auto& analyzers = Analyzer::getAnalyzers();
123
124 auto analyzer = analyzers.find(transaction.getInterfaceName());
125 if (analyzer != analyzers.end()) {
126 (analyzer->second)
127 ->getAnalyzeFunction()(transaction.getCode(), transaction.getDataParcel(),
128 transaction.getReplyParcel());
129 } else {
130 std::cout << "No analyzer:";
131 std::cout << " interface: " << transaction.getInterfaceName() << "\n";
132 std::cout << " code: " << transaction.getCode() << "\n";
133 std::cout << " data: " << transaction.getDataParcel().dataSize() << " bytes\n";
134 std::cout << " reply: " << transaction.getReplyParcel().dataSize() << " bytes\n";
135 }
136 std::cout << " status: " << transaction.getReturnedStatus() << "\n\n";
137 }
138
inspectRecording(const string & path)139 status_t inspectRecording(const string& path) {
140 auto& analyzers = Analyzer::getAnalyzers();
141
142 unique_fd fd(open(path.c_str(), O_RDONLY));
143 if (!fd.ok()) {
144 std::cout << "Failed to open recording file with error: " << strerror(errno) << '\n';
145 return android::BAD_VALUE;
146 }
147
148 int i = 1;
149 while (auto transaction = RecordedTransaction::fromFile(fd)) {
150 std::cout << "Transaction " << i << ":\n";
151 printTransaction(transaction.value());
152 i++;
153 }
154 return NO_ERROR;
155 }
156
incrementCtrlCCount(int signum)157 void incrementCtrlCCount(int signum) {
158 if (++gCtrlCCount > kCtrlCLimit) {
159 std::cout
160 << "Ctrl+C multiple times, but could not quit application. If recording still running, you "
161 "might stop it manually.\n";
162 exit(signum);
163 }
164 }
165
listenToFile(const string & serviceName)166 status_t listenToFile(const string& serviceName) {
167 auto filePath = getRecordingPath(serviceName);
168 unique_fd listenFd(open(filePath.c_str(), O_RDONLY));
169 if (!listenFd.ok()) {
170 std::cout << "Failed to open listening file with error: " << strerror(errno) << '\n';
171 return android::BAD_VALUE;
172 }
173
174 auto& analyzers = Analyzer::getAnalyzers();
175
176 signal(SIGINT, incrementCtrlCCount);
177 std::cout << "Starting to listen:\n";
178 int i = 1;
179 while (gCtrlCCount == 0) {
180 auto transaction = RecordedTransaction::fromFile(listenFd);
181 if (!transaction) {
182 sleep(1);
183 continue;
184 }
185 std::cout << "Transaction " << i << ":\n";
186 printTransaction(transaction.value());
187 i++;
188 }
189 return NO_ERROR;
190 }
191
replayFile(const sp<IBinder> & binder,const string & path)192 status_t replayFile(const sp<IBinder>& binder, const string& path) {
193 auto& analyzers = Analyzer::getAnalyzers();
194
195 unique_fd fd(open(path.c_str(), O_RDONLY));
196 if (!fd.ok()) {
197 std::cout << "Failed to open recording file with error: " << strerror(errno) << '\n';
198 return android::BAD_VALUE;
199 }
200
201 int failureCount = 0;
202 int i = 1;
203 while (auto transaction = RecordedTransaction::fromFile(fd)) {
204 std::cout << "Replaying Transaction " << i << ":\n";
205 printTransaction(transaction.value());
206
207 android::Parcel send, reply;
208 send.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
209 android::status_t status = binder->remoteBinder()->transact(transaction->getCode(), send,
210 &reply, transaction->getFlags());
211 if (status != transaction->getReturnedStatus()) {
212 std::cout << "Failure: Expected status " << transaction->getReturnedStatus()
213 << " but received status " << status << "\n\n";
214 failureCount++;
215 } else {
216 std::cout << "Transaction replayed correctly."
217 << "\n\n";
218 }
219 i++;
220 }
221 std::cout << i << " transactions replayed.\n";
222 if (failureCount > 0) {
223 std::cout << failureCount << " transactions had unexpected status. See logs for details.\n";
224 return android::UNKNOWN_ERROR;
225 } else {
226 return NO_ERROR;
227 }
228 }
229
listAvailableInterfaces(int,char **)230 status_t listAvailableInterfaces(int, char**) {
231 auto& analyzers = Analyzer::getAnalyzers();
232 std::cout << "Available Interfaces (" << analyzers.size() << "):\n";
233 for (auto a = analyzers.begin(); a != analyzers.end(); a++) {
234 std::cout << " " << a->second->getInterfaceName() << '\n';
235 }
236 return NO_ERROR;
237 }
238
239 struct AnalyzerCommand {
240 std::function<status_t(int, char*[])> command;
241 std::string overview;
242 std::string compactArguments;
243 std::string helpDetail;
244 };
245
246 status_t helpCommandEntryPoint(int argc, char* argv[]);
247
248 const AnalyzerCommand helpCommand = {helpCommandEntryPoint, "Show help information.", "<command>",
249 ""};
250
251 const AnalyzerCommand listCommand = {listAvailableInterfaces,
252 "Prints a list of available interfaces.", "", ""};
253
startRecordingAllBinders()254 status_t startRecordingAllBinders() {
255 auto services = android::defaultServiceManager()->listServices();
256 for (auto service : services) {
257 std::string serviceName = String8(service.c_str()).c_str();
258 // Print failed service name. Don't exit early because it would leave the previous successful
259 // services recording.
260 if (status_t result = startRecording(serviceName); result != NO_ERROR) {
261 std::cout << "Failed to start binder recording on service : " << service << std::endl;
262 }
263 }
264
265 return NO_ERROR;
266 }
267
startCommandEntryPoint(int argc,char * argv[])268 status_t startCommandEntryPoint(int argc, char* argv[]) {
269 if (argc != 3) {
270 helpCommandEntryPoint(argc, argv);
271 return android::BAD_VALUE;
272 }
273
274 string startOption = argv[2];
275 if (startOption == "--all") {
276 return startRecordingAllBinders();
277 }
278 return startRecording(startOption);
279 }
280
281 const AnalyzerCommand startCommand = {startCommandEntryPoint,
282 "Start recording Binder transactions from a given service. "
283 "Use --all to start recoding all binders.",
284 "<service>, --all",
285 " <service>\tService to record. See 'dumpsys -l'"};
286
stopRecordingAllBinders()287 status_t stopRecordingAllBinders() {
288 auto services = android::defaultServiceManager()->listServices();
289 for (auto service : services) {
290 std::string serviceName = String8(service.c_str()).c_str();
291 // Print failed service name. Don't exit early because it would leave the other recordings on.
292 if (status_t result = stopRecording(serviceName); result != NO_ERROR) {
293 std::cout << "Failed to stop binder recording on service : " << service << std::endl;
294 }
295 }
296
297 return NO_ERROR;
298 }
299
stopCommandEntryPoint(int argc,char * argv[])300 status_t stopCommandEntryPoint(int argc, char* argv[]) {
301 if (argc != 3) {
302 helpCommandEntryPoint(argc, argv);
303 return android::BAD_VALUE;
304 }
305 string stopOption = argv[2];
306 if (stopOption == "--all") {
307 stopRecordingAllBinders();
308 }
309
310 return stopRecording(stopOption);
311 }
312
313 const AnalyzerCommand stopCommand = {
314 stopCommandEntryPoint,
315 "Stops recording Binder transactions from a given process. (See 'start') Use --all to stop "
316 "recoding all binders",
317 "<service>, --all",
318 " <service>\tService to stop recording; <service> argument to previous 'start' command."};
319
inspectCommandEntryPoint(int argc,char * argv[])320 status_t inspectCommandEntryPoint(int argc, char* argv[]) {
321 if (argc != 3) {
322 helpCommandEntryPoint(argc, argv);
323 return android::BAD_VALUE;
324 }
325 std::string path = kStandardRecordingPath + string(argv[2]);
326
327 return inspectRecording(path);
328 }
329
330 const AnalyzerCommand inspectCommand = {
331 inspectCommandEntryPoint,
332 "Writes the binder transactions in <file-name> to stdout in a human-friendly format.",
333 "<file-name>",
334 " <file-name>\tA recording in /data/local/recordings/, and the name of the service"};
335
listenCommandEntryPoint(int argc,char * argv[])336 status_t listenCommandEntryPoint(int argc, char* argv[]) {
337 if (argc != 3) {
338 helpCommandEntryPoint(argc, argv);
339 return android::BAD_VALUE;
340 }
341
342 string serviceName = argv[2];
343 if (status_t startErr = startRecording(serviceName); startErr != NO_ERROR) {
344 return startErr;
345 }
346
347 status_t listenStatus = listenToFile(serviceName);
348
349 if (status_t stopErr = stopRecording(serviceName); stopErr != NO_ERROR) {
350 return stopErr;
351 }
352
353 return listenStatus;
354 }
355
356 const AnalyzerCommand listenCommand = {
357 listenCommandEntryPoint,
358 "Starts recording binder transactions in <service> and writes transactions to "
359 "stdout.",
360 "<service>", " <service>\t?\n"};
361
replayFunction(int argc,char * argv[])362 int replayFunction(int argc, char* argv[]) {
363 if (argc != 4) {
364 return helpCommandEntryPoint(argc, argv);
365 }
366
367 sp<IBinder> binder = android::defaultServiceManager()->checkService(String16(argv[2]));
368 std::string path = kStandardRecordingPath + string(argv[3]);
369
370 return replayFile(binder, path);
371 }
372
373 const AnalyzerCommand replayCommand = {
374 replayFunction, "No overview", "<service> <file-name>",
375 " <service>\t?\n"
376 " <file-name>\tThe name of a file in /data/local/recordings/"};
377
378 auto& commands = *new std::map<std::string, AnalyzerCommand>{
379 {"start", startCommand}, {"stop", stopCommand}, {"inspect", inspectCommand},
380 {"listen", listenCommand}, {"replay", replayCommand}, {"help", helpCommand}};
381
printGeneralHelp(std::string & toolName)382 void printGeneralHelp(std::string& toolName) {
383 std::cout << "USAGE: " << toolName << " <command> [<args>]\n\n";
384 std::cout << "COMMANDS:\n";
385 // Display overview this many characters from the start of a line.
386 // Subtract the length of the command name to calculate padding.
387 const size_t commandOverviewDisplayAlignment = 12;
388 for (const auto& command : commands) {
389 if (command.first == "help") {
390 continue;
391 }
392 std::cout << " " << command.first
393 << std::string(commandOverviewDisplayAlignment - command.first.length(), ' ')
394 << command.second.overview << "\n";
395 }
396 std::cout << "\n See '" << toolName << " help <command>' for detailed help.\n";
397 }
398
helpCommandEntryPoint(int argc,char * argv[])399 status_t helpCommandEntryPoint(int argc, char* argv[]) {
400 std::string toolName = argv[0];
401
402 if (argc < 2) {
403 printGeneralHelp(toolName);
404 return 0;
405 }
406
407 std::string commandName = argv[1];
408
409 if (commandName == "help") {
410 if (argc < 3) {
411 printGeneralHelp(toolName);
412 return 0;
413 }
414 commandName = argv[2];
415 } else {
416 commandName = argv[1];
417 }
418
419 auto command = commands.find(commandName);
420 if (command == commands.end()) {
421 std::cout << "Unrecognized command: " << commandName << "\n";
422 printGeneralHelp(toolName);
423 return -1;
424 }
425
426 std::cout << "OVERVIEW: " << command->second.overview << "\n\n";
427 std::cout << "USAGE: " << toolName << " " << commandName << " "
428 << command->second.compactArguments << "\n\n";
429 std::cout << "ARGUMENTS:\n" << command->second.helpDetail << "\n";
430
431 return 0;
432 }
433
434 } // namespace
435
main(int argc,char * argv[])436 int main(int argc, char* argv[]) {
437 std::string toolName = argv[0];
438
439 auto& analyzers = Analyzer::getAnalyzers();
440 if (analyzers.size() >= 1) {
441 commands["list"] = listCommand;
442 }
443
444 if (argc < 2 ||
445 (argc >= 2 && ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)))) {
446 // General help
447 printGeneralHelp(toolName);
448 return 0;
449 }
450
451 auto command = commands.find(argv[1]);
452 if (command == commands.end()) {
453 std::cout << "Unrecognized command: " << argv[1] << "\n";
454 printGeneralHelp(toolName);
455 return -1;
456 }
457
458 return command->second.command(argc, argv);
459 }
460