Igor Murashkin | b250cd8 | 2019-01-09 11:33:52 -0800 | [diff] [blame^] | 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 |