1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //#undef NDEBUG // get DCHECK etc.
16 
17 
18 #include "common/debug.h"
19 #include "common/expected.h"
20 #include "perfetto/rx_producer.h"
21 
22 #include <android-base/unique_fd.h>
23 #include <android-base/parseint.h>
24 #include <android-base/file.h>
25 
26 #include "rxcpp/rx.hpp"
27 #include <iostream>
28 #include <optional>
29 
30 #include <sched.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <syscall.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 
37 using namespace iorap::perfetto;  // NOLINT
38 
39 #if defined(IORAP_PERFETTO_MAIN)
40 
Usage(char ** argv)41 void Usage(char** argv) {
42   std::cerr << "Usage: " << argv[0] << " [--config-proto=config.pb] [--duration-ms=5000] [--output-proto=output.pb]" << std::endl;
43   std::cerr << "" << std::endl;
44   std::cerr << "  Request a perfetto trace, blocking until it's complete. The resulting trace proto" << std::endl;
45   std::cerr << "  is output to stdout as text, or to --output-proto as a binary." << std::endl;
46   std::cerr << "" << std::endl;
47   std::cerr << "  Optional flags:" << std::endl;
48   std::cerr << "    --help,-h                  Print this Usage." << std::endl;
49   std::cerr << "    --output-proto $,-op $     Perfetto tracebuffer output file (default stdout)." << std::endl;
50   std::cerr << "    --config-proto $,-cp $     Path to binary protobuf config." << std::endl;
51   std::cerr << "    --duration-ms $,-dm $      How long to run trace for in milliseconds." << std::endl;
52   std::cerr << "    --simple                   Simplest possible perfetto state transitions (default off)." << std::endl;
53   std::cerr << "    --verbose,-v               Set verbosity (default off)." << std::endl;
54   std::cerr << "    --wait,-w                  Wait for key stroke before continuing (default off)." << std::endl;
55   exit(1);
56 }
57 
CreateCommandLinePerfettoDependenciesComponent(uint32_t duration_ms)58 PerfettoDependencies::Component CreateCommandLinePerfettoDependenciesComponent(
59     uint32_t duration_ms) {
60   // TODO: read from command line.
61   static const uint32_t kBufferSize = 4096;
62 
63   // TODO: remove this hack.
64   static const uint32_t kTraceDurationMs = duration_ms;
65 
66   // fruit: using 'bindInstance' causes a segfault every time.
67 #if 0
68 
69   // fruit: Can't use a stateful lambda, so use bindInstance instead of registerProvider.
70   auto config = PerfettoDependencies::CreateConfig(duration_ms,
71                                                    /*deferred_start*/true,
72                                                    kBufferSize);
73 
74   .... bindInstance(config);
75 #endif
76 
77   return fruit::createComponent()
78     .bind<PerfettoConsumer, PerfettoConsumerImpl>()
79     .registerProvider([]() /* -> TraceConfig */ {
80         return PerfettoDependencies::CreateConfig(kTraceDurationMs,
81                                                   /*deferred_start*/true,
82                                                   kBufferSize);
83     });
84 }
85 
CollectPerfettoTraceBufferViaAbstractions(RxProducerFactory & producer_factory,const std::string & arg_output_proto,const int arg_duration_ms)86 static void CollectPerfettoTraceBufferViaAbstractions(
87     RxProducerFactory& producer_factory,
88     const std::string& arg_output_proto,
89     const int arg_duration_ms) {
90   LOG(VERBOSE) << "CollectPerfettoTraceBufferViaAbstractions";
91 
92   // Don't create a subscriber to emit the PerfettoStreamCommand.
93   // RxCpp is "greedy" and consumes every possible item emitted (it doesn't support 'pull'). We want
94   // to operate on a (command,state) iteration every time, just like in a real scenario.
95   // Adding the 'interval' turns into a non-greedy version (i.e. push).
96 
97   // Immediately emit 'kStartTracing', wait and emit kStopTracing, wait and emit kShutdown.
98   // In reality, there would be a delay between all these events.
99   auto /*observable<PerfettoStreamCommand>*/ commands =
100       rxcpp::observable<>::just(PerfettoStreamCommand::kStartTracing)
101           // wait 1x
102           .concat(
103               // Pick a value longer than the perfetto config delay_ms, so that we send
104               // 'kShutdown' after tracing has already finished.
105               rxcpp::observable<>::interval(std::chrono::milliseconds(arg_duration_ms * 2))
106                   .take(2)  // kStopTracing, kShutdown.
107                   .map([](int value) {
108                          // value is 1,2,3,...
109                          return static_cast<PerfettoStreamCommand>(value);  // 1,2, ...
110                        })
111           );
112 
113   auto /*observable<PerfettoTraceProto>*/ trace_proto_stream =
114       producer_factory.CreateTraceStream(commands);
115 
116   trace_proto_stream
117     .observe_on(ObserveOnNewIoThread())  // Write data on an idle-class-priority thread.
118     .as_blocking()  // Wait for observable to terminate with on_completed or on_error.
119     .subscribe(/*on_next*/[arg_output_proto]
120       (PerfettoTraceProto trace_proto) {
121              if (!trace_proto.WriteFullyToFile(arg_output_proto)) {
122                LOG(ERROR) << "Failed to save TraceBuffer to " << arg_output_proto;
123              } else {
124                LOG(INFO) << "TraceBuffer saved to file: " << arg_output_proto;
125                LOG(INFO);
126                LOG(INFO) << "To print this in a human readable form, execute these commands:";
127                LOG(INFO) << "$> adb pull '" << arg_output_proto << "'";
128                LOG(INFO) << "$> trace_to_text systrace <filename.pb>";
129              }
130       },
131       /*on_error*/[](rxcpp::util::error_ptr err) {
132         LOG(ERROR) << "Perfetto trace proto collection error: " << rxcpp::util::what(err);
133       });
134 }
135 
136 namespace iorap::perfetto {
137 // Reach inside rx_producer.cc
138 // Not part of any headers because it's internal.
139 void CollectPerfettoTraceBufferImmediately(
140     RxProducerFactory& producer_factory,
141     const std::string& arg_output_proto);
142 }
143 
main(int argc,char ** argv)144 int main(int argc, char** argv) {
145   android::base::InitLogging(argv);
146   android::base::SetLogger(android::base::StderrLogger);
147 
148   bool wait_for_keystroke = false;
149   bool enable_verbose = false;
150 
151   std::string arg_output_proto;
152   std::string arg_config_proto;
153   uint32_t arg_duration_ms = 1000;
154   bool arg_simple = false;
155 
156   if (argc == 1) {
157     Usage(argv);
158   }
159 
160   for (int arg = 1; arg < argc; ++arg) {
161     std::string argstr = argv[arg];
162     bool has_arg_next = (arg+1)<argc;
163     std::string arg_next = has_arg_next ? argv[arg+1] : "";
164 
165     if (argstr == "--help" || argstr == "-h") {
166       Usage(argv);
167     } else if (argstr == "--output-proto" || argstr == "-op") {
168       if (!has_arg_next) {
169         std::cerr << "Missing --output-proto <value>" << std::endl;
170         return 1;
171       }
172       arg_output_proto = arg_next;
173       ++arg;
174     } else if (argstr == "--config-proto" || argstr == "-cp") {
175       if (!has_arg_next) {
176         std::cerr << "Missing --config-proto <value>" << std::endl;
177         return 1;
178       }
179       arg_config_proto = arg_next;
180       LOG(WARNING) << "TODO: parse configs from a file, not implemented yet.";
181       ++arg;
182     } else if (argstr == "--duration-ms" || argstr == "-dm") {
183       if (!has_arg_next) {
184         std::cerr << "Missing --duration-ms <value>" << std::endl;
185         return 1;
186       }
187       if (!android::base::ParseUint(arg_next.c_str(), /*out*/&arg_duration_ms)) {
188         std::cerr << "Invalid --duration-ms " << arg_next << ", reason: " << strerror(errno);
189         return 1;
190       }
191       ++arg;
192     } else if (argstr == "--simple") {
193       arg_simple = true;
194     } else if (argstr == "--verbose" || argstr == "-v") {
195       enable_verbose = true;
196     } else if (argstr == "--wait" || argstr == "-w") {
197       wait_for_keystroke = true;
198     }
199   }
200 
201   if (enable_verbose) {
202     android::base::SetMinimumLogSeverity(android::base::VERBOSE);
203 
204     LOG(VERBOSE) << "Verbose check";
205     LOG(VERBOSE) << "Debug check: " << ::iorap::kIsDebugBuild;
206   }
207 
208   // Useful to attach a debugger...
209   // 1) $> iorap-cmd-perfetto -w <args>
210   // 2) $> gdbclient <pid>
211   if (wait_for_keystroke) {
212     LOG(INFO) << "Self pid: " << getpid();
213     LOG(INFO) << "Press any key to continue...";
214     std::cin >> wait_for_keystroke;
215   }
216 
217   int return_code = 0;
218   // TODO: convert #on-error into a non-0 return code.
219 
220   PerfettoDependencies::Injector injector{
221       CreateCommandLinePerfettoDependenciesComponent,
222       arg_duration_ms
223   };
224   RxProducerFactory rx_producer_factory{/*borrow*/injector};
225 
226   if (arg_simple) {
227     // To debug any kind of low-level perfetto issues.
228     CollectPerfettoTraceBufferImmediately(/*inout*/rx_producer_factory, arg_output_proto);
229   } else {
230     // To debug our own iorap internal abstractions.
231     CollectPerfettoTraceBufferViaAbstractions(/*inout*/rx_producer_factory,
232                                               arg_output_proto,
233                                               arg_duration_ms);
234   }
235 
236   // Uncomment this if we want to leave the process around to inspect it from adb shell.
237   // sleep(100000);
238 
239   // 0 -> successfully wrote the TraceProto out to file.
240   // 1 -> failed along the way (#on_error and also see the error logs).
241   return return_code;
242 }
243 
244 #endif
245