blob: 0b1056e4f94a2cdfec614dc0cc8f73b0b9a9d1f5 [file] [log] [blame]
Igor Murashkinb250cd82019-01-09 11:33:52 -08001// 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
37using namespace iorap::perfetto; // NOLINT
38
39#if defined(IORAP_PERFETTO_MAIN)
40
41void 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
58PerfettoDependencies::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
86static 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
136namespace iorap::perfetto {
137// Reach inside rx_producer.cc
138// Not part of any headers because it's internal.
139void CollectPerfettoTraceBufferImmediately(
140 RxProducerFactory& producer_factory,
141 const std::string& arg_output_proto);
142}
143
144int 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