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