• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 "src/perfetto_cmd/perfetto_cmd.h"
18 
19 #include "perfetto/base/build_config.h"
20 #include "perfetto/base/proc_utils.h"
21 #include "perfetto/ext/base/scoped_file.h"
22 #include "perfetto/ext/base/string_splitter.h"
23 
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <time.h>
29 
30 // For dup() (and _setmode() on windows).
31 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
32 #include <fcntl.h>
33 #include <io.h>
34 #else
35 #include <unistd.h>
36 #endif
37 
38 #include <chrono>
39 #include <fstream>
40 #include <iostream>
41 #include <iterator>
42 #include <random>
43 #include <sstream>
44 #include <thread>
45 
46 #include "perfetto/base/compiler.h"
47 #include "perfetto/base/logging.h"
48 #include "perfetto/base/time.h"
49 #include "perfetto/ext/base/android_utils.h"
50 #include "perfetto/ext/base/ctrl_c_handler.h"
51 #include "perfetto/ext/base/file_utils.h"
52 #include "perfetto/ext/base/getopt.h"
53 #include "perfetto/ext/base/pipe.h"
54 #include "perfetto/ext/base/string_view.h"
55 #include "perfetto/ext/base/temp_file.h"
56 #include "perfetto/ext/base/thread_utils.h"
57 #include "perfetto/ext/base/utils.h"
58 #include "perfetto/ext/base/uuid.h"
59 #include "perfetto/ext/base/version.h"
60 #include "perfetto/ext/traced/traced.h"
61 #include "perfetto/ext/tracing/core/basic_types.h"
62 #include "perfetto/ext/tracing/core/trace_packet.h"
63 #include "perfetto/ext/tracing/ipc/default_socket.h"
64 #include "perfetto/protozero/proto_utils.h"
65 #include "perfetto/tracing/core/data_source_descriptor.h"
66 #include "perfetto/tracing/core/trace_config.h"
67 #include "perfetto/tracing/core/tracing_service_state.h"
68 #include "src/android_stats/statsd_logging_helper.h"
69 #include "src/perfetto_cmd/bugreport_path.h"
70 #include "src/perfetto_cmd/config.h"
71 #include "src/perfetto_cmd/packet_writer.h"
72 #include "src/perfetto_cmd/pbtxt_to_pb.h"
73 #include "src/perfetto_cmd/rate_limiter.h"
74 #include "src/perfetto_cmd/trigger_producer.h"
75 
76 #include "protos/perfetto/common/ftrace_descriptor.gen.h"
77 #include "protos/perfetto/common/tracing_service_state.gen.h"
78 #include "protos/perfetto/common/track_event_descriptor.gen.h"
79 
80 namespace perfetto {
81 namespace {
82 
83 perfetto::PerfettoCmd* g_perfetto_cmd;
84 
85 const uint32_t kOnTraceDataTimeoutMs = 3000;
86 const uint32_t kCloneTimeoutMs = 10000;
87 
88 class LoggingErrorReporter : public ErrorReporter {
89  public:
LoggingErrorReporter(std::string file_name,const char * config)90   LoggingErrorReporter(std::string file_name, const char* config)
91       : file_name_(file_name), config_(config) {}
92 
AddError(size_t row,size_t column,size_t length,const std::string & message)93   void AddError(size_t row,
94                 size_t column,
95                 size_t length,
96                 const std::string& message) override {
97     parsed_successfully_ = false;
98     std::string line = ExtractLine(row - 1).ToStdString();
99     if (!line.empty() && line[line.length() - 1] == '\n') {
100       line.erase(line.length() - 1);
101     }
102 
103     std::string guide(column + length, ' ');
104     for (size_t i = column; i < column + length; i++) {
105       guide[i - 1] = i == column ? '^' : '~';
106     }
107     fprintf(stderr, "%s:%zu:%zu error: %s\n", file_name_.c_str(), row, column,
108             message.c_str());
109     fprintf(stderr, "%s\n", line.c_str());
110     fprintf(stderr, "%s\n", guide.c_str());
111   }
112 
Success() const113   bool Success() const { return parsed_successfully_; }
114 
115  private:
ExtractLine(size_t line)116   base::StringView ExtractLine(size_t line) {
117     const char* start = config_;
118     const char* end = config_;
119 
120     for (size_t i = 0; i < line + 1; i++) {
121       start = end;
122       char c;
123       while ((c = *end++) && c != '\n')
124         ;
125     }
126     return base::StringView(start, static_cast<size_t>(end - start));
127   }
128 
129   bool parsed_successfully_ = true;
130   std::string file_name_;
131   const char* config_;
132 };
133 
ParseTraceConfigPbtxt(const std::string & file_name,const std::string & pbtxt,TraceConfig * config)134 bool ParseTraceConfigPbtxt(const std::string& file_name,
135                            const std::string& pbtxt,
136                            TraceConfig* config) {
137   LoggingErrorReporter reporter(file_name, pbtxt.c_str());
138   std::vector<uint8_t> buf = PbtxtToPb(pbtxt, &reporter);
139   if (!reporter.Success())
140     return false;
141   if (!config->ParseFromArray(buf.data(), buf.size()))
142     return false;
143   return true;
144 }
145 
IsUserBuild()146 bool IsUserBuild() {
147 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
148   std::string build_type = base::GetAndroidProp("ro.build.type");
149   if (build_type.empty()) {
150     PERFETTO_ELOG("Unable to read ro.build.type: assuming user build");
151     return true;
152   }
153   return build_type == "user";
154 #else
155   return false;
156 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
157 }
158 
ConvertRateLimiterResponseToAtom(RateLimiter::ShouldTraceResponse resp)159 std::optional<PerfettoStatsdAtom> ConvertRateLimiterResponseToAtom(
160     RateLimiter::ShouldTraceResponse resp) {
161   switch (resp) {
162     case RateLimiter::kNotAllowedOnUserBuild:
163       return PerfettoStatsdAtom::kCmdUserBuildTracingNotAllowed;
164     case RateLimiter::kFailedToInitState:
165       return PerfettoStatsdAtom::kCmdFailedToInitGuardrailState;
166     case RateLimiter::kInvalidState:
167       return PerfettoStatsdAtom::kCmdInvalidGuardrailState;
168     case RateLimiter::kHitUploadLimit:
169       return PerfettoStatsdAtom::kCmdHitUploadLimit;
170     case RateLimiter::kOkToTrace:
171       return std::nullopt;
172   }
173   PERFETTO_FATAL("For GCC");
174 }
175 
176 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
177 // Reports trace with `uuid` being finalized to the trace marker.
178 //
179 // This reimplements parts of android libcutils, because:
180 // * libcutils is not exposed to the NDK and cannot be used from standalone
181 //   perfetto
182 // * libcutils atrace uses properties to enable tags, which are not required in
183 //   this case.
ReportFinalizeTraceUuidToAtrace(const base::Uuid & uuid)184 void ReportFinalizeTraceUuidToAtrace(const base::Uuid& uuid) {
185   base::ScopedFile file =
186       base::OpenFile("/sys/kernel/tracing/trace_marker", O_WRONLY);
187   if (!file) {
188     file = base::OpenFile("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
189     if (!file) {
190       return;
191     }
192   }
193   base::StackString<100> uuid_slice("N|%d|OtherTraces|finalize-uuid-%s",
194                                     base::GetProcessId(),
195                                     uuid.ToPrettyString().c_str());
196   PERFETTO_EINTR(write(file.get(), uuid_slice.c_str(), uuid_slice.len()));
197 }
198 #endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
199 
200 }  // namespace
201 
202 const char* kStateDir = "/data/misc/perfetto-traces";
203 
PerfettoCmd()204 PerfettoCmd::PerfettoCmd() {
205   // Only the main thread instance on the main thread will receive ctrl-c.
206   if (!g_perfetto_cmd)
207     g_perfetto_cmd = this;
208 }
209 
~PerfettoCmd()210 PerfettoCmd::~PerfettoCmd() {
211   if (g_perfetto_cmd == this) {
212     g_perfetto_cmd = nullptr;
213     if (ctrl_c_handler_installed_) {
214       task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
215     }
216   }
217 }
218 
PrintUsage(const char * argv0)219 void PerfettoCmd::PrintUsage(const char* argv0) {
220   fprintf(stderr, R"(
221 Usage: %s
222   --background     -d      : Exits immediately and continues in the background.
223                              Prints the PID of the bg process. The printed PID
224                              can used to gracefully terminate the tracing
225                              session by issuing a `kill -TERM $PRINTED_PID`.
226   --background-wait -D     : Like --background, but waits (up to 30s) for all
227                              data sources to be started before exiting. Exit
228                              code is zero if a successful acknowledgement is
229                              received, non-zero otherwise (error or timeout).
230   --clone TSID             : Creates a read-only clone of an existing tracing
231                              session, identified by its ID (see --query).
232   --config         -c      : /path/to/trace/config/file or - for stdin
233   --out            -o      : /path/to/out/trace/file or - for stdout
234                              If using CLONE_SNAPSHOT triggers, each snapshot
235                              will be saved in a new file with a counter suffix
236                              (e.g., file.0, file.1, file.2).
237   --txt                    : Parse config as pbtxt. Not for production use.
238                              Not a stable API.
239   --query                  : Queries the service state and prints it as
240                              human-readable text.
241   --query-raw              : Like --query, but prints raw proto-encoded bytes
242                              of tracing_service_state.proto.
243   --help           -h
244 
245 Light configuration flags: (only when NOT using -c/--config)
246   --time           -t      : Trace duration N[s,m,h] (default: 10s)
247   --buffer         -b      : Ring buffer size N[mb,gb] (default: 32mb)
248   --size           -s      : Max file size N[mb,gb]
249                             (default: in-memory ring-buffer only)
250   --app            -a      : Android (atrace) app name
251   FTRACE_GROUP/FTRACE_NAME : Record ftrace event (e.g. sched/sched_switch)
252   ATRACE_CAT               : Record ATRACE_CAT (e.g. wm) (Android only)
253 
254 Statsd-specific and other Android-only flags:
255   --alert-id           : ID of the alert that triggered this trace.
256   --config-id          : ID of the triggering config.
257   --config-uid         : UID of app which registered the config.
258   --subscription-id    : ID of the subscription that triggered this trace.
259   --upload             : Upload trace.
260   --dropbox        TAG : DEPRECATED: Use --upload instead
261                          TAG should always be set to 'perfetto'.
262   --save-for-bugreport : If a trace with bugreport_score > 0 is running, it
263                          saves it into a file. Outputs the path when done.
264   --no-guardrails      : Ignore guardrails triggered when using --upload
265                          (testing only).
266   --reset-guardrails   : Resets the state of the guardails and exits
267                          (testing only).
268 
269 Detach mode. DISCOURAGED, read https://perfetto.dev/docs/concepts/detached-mode
270   --detach=key          : Detach from the tracing session with the given key.
271   --attach=key [--stop] : Re-attach to the session (optionally stop tracing
272                           once reattached).
273   --is_detached=key     : Check if the session can be re-attached.
274                           Exit code:  0:Yes, 2:No, 1:Error.
275 )", /* this comment fixes syntax highlighting in some editors */
276           argv0);
277 }
278 
ParseCmdlineAndMaybeDaemonize(int argc,char ** argv)279 std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc,
280                                                               char** argv) {
281 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
282   umask(0000);  // make sure that file creation is not affected by umask.
283 #endif
284   enum LongOption {
285     OPT_ALERT_ID = 1000,
286     OPT_BUGREPORT,
287     OPT_CLONE,
288     OPT_CONFIG_ID,
289     OPT_CONFIG_UID,
290     OPT_SUBSCRIPTION_ID,
291     OPT_RESET_GUARDRAILS,
292     OPT_PBTXT_CONFIG,
293     OPT_DROPBOX,
294     OPT_UPLOAD,
295     OPT_IGNORE_GUARDRAILS,
296     OPT_DETACH,
297     OPT_ATTACH,
298     OPT_IS_DETACHED,
299     OPT_STOP,
300     OPT_QUERY,
301     OPT_QUERY_RAW,
302     OPT_VERSION,
303   };
304   static const option long_options[] = {
305       {"help", no_argument, nullptr, 'h'},
306       {"config", required_argument, nullptr, 'c'},
307       {"out", required_argument, nullptr, 'o'},
308       {"background", no_argument, nullptr, 'd'},
309       {"background-wait", no_argument, nullptr, 'D'},
310       {"time", required_argument, nullptr, 't'},
311       {"buffer", required_argument, nullptr, 'b'},
312       {"size", required_argument, nullptr, 's'},
313       {"app", required_argument, nullptr, 'a'},
314       {"no-guardrails", no_argument, nullptr, OPT_IGNORE_GUARDRAILS},
315       {"txt", no_argument, nullptr, OPT_PBTXT_CONFIG},
316       {"upload", no_argument, nullptr, OPT_UPLOAD},
317       {"dropbox", required_argument, nullptr, OPT_DROPBOX},
318       {"alert-id", required_argument, nullptr, OPT_ALERT_ID},
319       {"config-id", required_argument, nullptr, OPT_CONFIG_ID},
320       {"config-uid", required_argument, nullptr, OPT_CONFIG_UID},
321       {"subscription-id", required_argument, nullptr, OPT_SUBSCRIPTION_ID},
322       {"reset-guardrails", no_argument, nullptr, OPT_RESET_GUARDRAILS},
323       {"detach", required_argument, nullptr, OPT_DETACH},
324       {"attach", required_argument, nullptr, OPT_ATTACH},
325       {"clone", required_argument, nullptr, OPT_CLONE},
326       {"is_detached", required_argument, nullptr, OPT_IS_DETACHED},
327       {"stop", no_argument, nullptr, OPT_STOP},
328       {"query", no_argument, nullptr, OPT_QUERY},
329       {"query-raw", no_argument, nullptr, OPT_QUERY_RAW},
330       {"version", no_argument, nullptr, OPT_VERSION},
331       {"save-for-bugreport", no_argument, nullptr, OPT_BUGREPORT},
332       {nullptr, 0, nullptr, 0}};
333 
334   std::string config_file_name;
335   std::string trace_config_raw;
336   bool parse_as_pbtxt = false;
337   TraceConfig::StatsdMetadata statsd_metadata;
338   limiter_.reset(new RateLimiter());
339 
340   ConfigOptions config_options;
341   bool has_config_options = false;
342 
343   if (argc <= 1) {
344     PrintUsage(argv[0]);
345     return 1;
346   }
347 
348   optind = 1;  // Reset getopt state. It's reused by the snapshot thread.
349   for (;;) {
350     int option =
351         getopt_long(argc, argv, "hc:o:dDt:b:s:a:", long_options, nullptr);
352 
353     if (option == -1)
354       break;  // EOF.
355 
356     if (option == 'c') {
357       config_file_name = std::string(optarg);
358       if (strcmp(optarg, "-") == 0) {
359 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
360         // We don't want the runtime to replace "\n" with "\r\n" on `std::cin`.
361         _setmode(_fileno(stdin), _O_BINARY);
362 #endif
363         std::istreambuf_iterator<char> begin(std::cin), end;
364         trace_config_raw.assign(begin, end);
365       } else if (strcmp(optarg, ":test") == 0) {
366         TraceConfig test_config;
367         ConfigOptions opts;
368         opts.time = "2s";
369         opts.categories.emplace_back("sched/sched_switch");
370         opts.categories.emplace_back("power/cpu_idle");
371         opts.categories.emplace_back("power/cpu_frequency");
372         opts.categories.emplace_back("power/gpu_frequency");
373         PERFETTO_CHECK(CreateConfigFromOptions(opts, &test_config));
374         trace_config_raw = test_config.SerializeAsString();
375       } else if (strcmp(optarg, ":mem") == 0) {
376         // This is used by OnCloneSnapshotTriggerReceived(), which passes the
377         // original trace config as a member field. This is needed because, in
378         // the new PerfettoCmd instance, we need to know upfront trace config
379         // fields that affect the behaviour of perfetto_cmd, e.g., the guardrail
380         // overrides, the unique_session_name, the reporter API package etc.
381         PERFETTO_CHECK(!snapshot_config_.empty());
382         trace_config_raw = snapshot_config_;
383       } else {
384         if (!base::ReadFile(optarg, &trace_config_raw)) {
385           PERFETTO_PLOG("Could not open %s", optarg);
386           return 1;
387         }
388       }
389       continue;
390     }
391 
392     if (option == 'o') {
393       trace_out_path_ = optarg;
394       continue;
395     }
396 
397     if (option == 'd') {
398       background_ = true;
399       continue;
400     }
401 
402     if (option == 'D') {
403       background_ = true;
404       background_wait_ = true;
405       continue;
406     }
407 
408     if (option == OPT_CLONE) {
409       clone_tsid_ = static_cast<TracingSessionID>(atoll(optarg));
410       continue;
411     }
412 
413     if (option == 't') {
414       has_config_options = true;
415       config_options.time = std::string(optarg);
416       continue;
417     }
418 
419     if (option == 'b') {
420       has_config_options = true;
421       config_options.buffer_size = std::string(optarg);
422       continue;
423     }
424 
425     if (option == 's') {
426       has_config_options = true;
427       config_options.max_file_size = std::string(optarg);
428       continue;
429     }
430 
431     if (option == 'a') {
432       config_options.atrace_apps.push_back(std::string(optarg));
433       has_config_options = true;
434       continue;
435     }
436 
437     if (option == OPT_UPLOAD) {
438 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
439       upload_flag_ = true;
440       continue;
441 #else
442       PERFETTO_ELOG("--upload is only supported on Android");
443       return 1;
444 #endif
445     }
446 
447     if (option == OPT_DROPBOX) {
448 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
449       PERFETTO_CHECK(optarg);
450       upload_flag_ = true;
451       continue;
452 #else
453       PERFETTO_ELOG("--dropbox is only supported on Android");
454       return 1;
455 #endif
456     }
457 
458     if (option == OPT_PBTXT_CONFIG) {
459       parse_as_pbtxt = true;
460       continue;
461     }
462 
463     if (option == OPT_IGNORE_GUARDRAILS) {
464       ignore_guardrails_ = true;
465       continue;
466     }
467 
468     if (option == OPT_RESET_GUARDRAILS) {
469       PERFETTO_CHECK(limiter_->ClearState());
470       PERFETTO_ILOG("Guardrail state cleared");
471       return 0;
472     }
473 
474     if (option == OPT_ALERT_ID) {
475       statsd_metadata.set_triggering_alert_id(atoll(optarg));
476       continue;
477     }
478 
479     if (option == OPT_CONFIG_ID) {
480       statsd_metadata.set_triggering_config_id(atoll(optarg));
481       continue;
482     }
483 
484     if (option == OPT_CONFIG_UID) {
485       statsd_metadata.set_triggering_config_uid(atoi(optarg));
486       continue;
487     }
488 
489     if (option == OPT_SUBSCRIPTION_ID) {
490       statsd_metadata.set_triggering_subscription_id(atoll(optarg));
491       continue;
492     }
493 
494     if (option == OPT_DETACH) {
495       detach_key_ = std::string(optarg);
496       PERFETTO_CHECK(!detach_key_.empty());
497       continue;
498     }
499 
500     if (option == OPT_ATTACH) {
501       attach_key_ = std::string(optarg);
502       PERFETTO_CHECK(!attach_key_.empty());
503       continue;
504     }
505 
506     if (option == OPT_IS_DETACHED) {
507       attach_key_ = std::string(optarg);
508       redetach_once_attached_ = true;
509       PERFETTO_CHECK(!attach_key_.empty());
510       continue;
511     }
512 
513     if (option == OPT_STOP) {
514       stop_trace_once_attached_ = true;
515       continue;
516     }
517 
518     if (option == OPT_QUERY) {
519       query_service_ = true;
520       continue;
521     }
522 
523     if (option == OPT_QUERY_RAW) {
524       query_service_ = true;
525       query_service_output_raw_ = true;
526       continue;
527     }
528 
529     if (option == OPT_VERSION) {
530       printf("%s\n", base::GetVersionString());
531       return 0;
532     }
533 
534     if (option == OPT_BUGREPORT) {
535       bugreport_ = true;
536       continue;
537     }
538 
539     PrintUsage(argv[0]);
540     return 1;
541   }
542 
543   for (ssize_t i = optind; i < argc; i++) {
544     has_config_options = true;
545     config_options.categories.push_back(argv[i]);
546   }
547 
548   if (query_service_ && (is_detach() || is_attach() || background_)) {
549     PERFETTO_ELOG("--query cannot be combined with any other argument");
550     return 1;
551   }
552 
553   if (is_detach() && is_attach()) {
554     PERFETTO_ELOG("--attach and --detach are mutually exclusive");
555     return 1;
556   }
557 
558   if (is_detach() && background_) {
559     PERFETTO_ELOG("--detach and --background are mutually exclusive");
560     return 1;
561   }
562 
563   if (stop_trace_once_attached_ && !is_attach()) {
564     PERFETTO_ELOG("--stop is supported only in combination with --attach");
565     return 1;
566   }
567 
568   if (bugreport_ && (is_attach() || is_detach() || query_service_ ||
569                      has_config_options || background_wait_)) {
570     PERFETTO_ELOG("--save-for-bugreport cannot take any other argument");
571     return 1;
572   }
573 
574   // --save-for-bugreport is the equivalent of:
575   // --clone kBugreportSessionId -o /data/misc/perfetto-traces/bugreport/...
576   if (bugreport_ && trace_out_path_.empty()) {
577     clone_tsid_ = kBugreportSessionId;
578     trace_out_path_ = GetBugreportTracePath();
579   }
580 
581   // Parse the trace config. It can be either:
582   // 1) A proto-encoded file/stdin (-c ...).
583   // 2) A proto-text file/stdin (-c ... --txt).
584   // 3) A set of option arguments (-t 10s -s 10m).
585   // The only cases in which a trace config is not expected is --attach.
586   // For this we are just acting on already existing sessions.
587   trace_config_.reset(new TraceConfig());
588 
589   bool parsed = false;
590   const bool will_trace_or_trigger = !is_attach() && !query_service_;
591   if (!will_trace_or_trigger) {
592     if ((!trace_config_raw.empty() || has_config_options)) {
593       PERFETTO_ELOG("Cannot specify a trace config with this option");
594       return 1;
595     }
596   } else if (has_config_options) {
597     if (!trace_config_raw.empty()) {
598       PERFETTO_ELOG(
599           "Cannot specify both -c/--config and any of --time, --size, "
600           "--buffer, --app, ATRACE_CAT, FTRACE_EVENT");
601       return 1;
602     }
603     parsed = CreateConfigFromOptions(config_options, trace_config_.get());
604   } else {
605     if (trace_config_raw.empty() && !clone_tsid_) {
606       PERFETTO_ELOG("The TraceConfig is empty");
607       return 1;
608     }
609     PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
610     if (parse_as_pbtxt) {
611       parsed = ParseTraceConfigPbtxt(config_file_name, trace_config_raw,
612                                      trace_config_.get());
613     } else {
614       parsed = trace_config_->ParseFromString(trace_config_raw);
615     }
616   }
617 
618   if (parsed) {
619     *trace_config_->mutable_statsd_metadata() = std::move(statsd_metadata);
620     trace_config_raw.clear();
621   } else if (will_trace_or_trigger && !clone_tsid_) {
622     PERFETTO_ELOG("The trace config is invalid, bailing out.");
623     return 1;
624   }
625 
626   if (trace_config_->trace_uuid_lsb() == 0 &&
627       trace_config_->trace_uuid_msb() == 0) {
628     base::Uuid uuid = base::Uuidv4();
629     if (trace_config_->statsd_metadata().triggering_subscription_id()) {
630       uuid.set_lsb(
631           trace_config_->statsd_metadata().triggering_subscription_id());
632     }
633     uuid_ = uuid.ToString();
634     trace_config_->set_trace_uuid_msb(uuid.msb());
635     trace_config_->set_trace_uuid_lsb(uuid.lsb());
636   } else {
637     base::Uuid uuid(trace_config_->trace_uuid_lsb(),
638                     trace_config_->trace_uuid_msb());
639     uuid_ = uuid.ToString();
640   }
641 
642   const auto& delay = trace_config_->cmd_trace_start_delay();
643   if (delay.has_max_delay_ms() != delay.has_min_delay_ms()) {
644     PERFETTO_ELOG("cmd_trace_start_delay field is only partially specified.");
645     return 1;
646   }
647 
648   bool has_incidentd_package =
649       !trace_config_->incident_report_config().destination_package().empty();
650   if (has_incidentd_package && !upload_flag_) {
651     PERFETTO_ELOG(
652         "Unexpected IncidentReportConfig without --dropbox / --upload.");
653     return 1;
654   }
655 
656   bool has_android_reporter_package = !trace_config_->android_report_config()
657                                            .reporter_service_package()
658                                            .empty();
659   if (has_android_reporter_package && !upload_flag_) {
660     PERFETTO_ELOG(
661         "Unexpected AndroidReportConfig without --dropbox / --upload.");
662     return 1;
663   }
664 
665   if (has_incidentd_package && has_android_reporter_package) {
666     PERFETTO_ELOG(
667         "Only one of IncidentReportConfig and AndroidReportConfig "
668         "allowed in the same config.");
669     return 1;
670   }
671 
672   // If the upload flag is set, we can only be doing one of three things:
673   // 1. Reporting to either incidentd or Android framework.
674   // 2. Skipping incidentd/Android report because it was explicitly
675   //    specified in the config.
676   // 3. Activating triggers.
677   bool incidentd_valid =
678       has_incidentd_package ||
679       trace_config_->incident_report_config().skip_incidentd();
680   bool android_report_valid =
681       has_android_reporter_package ||
682       trace_config_->android_report_config().skip_report();
683   bool has_triggers = !trace_config_->activate_triggers().empty();
684   if (upload_flag_ && !incidentd_valid && !android_report_valid &&
685       !has_triggers) {
686     PERFETTO_ELOG(
687         "One of IncidentReportConfig, AndroidReportConfig or activate_triggers "
688         "must be specified with --dropbox / --upload.");
689     return 1;
690   }
691 
692   // Only save to incidentd if:
693   // 1) |destination_package| is set
694   // 2) |skip_incidentd| is absent or false.
695   // 3) we are not simply activating triggers.
696   save_to_incidentd_ =
697       has_incidentd_package &&
698       !trace_config_->incident_report_config().skip_incidentd() &&
699       !has_triggers;
700 
701   // Only report to the Andorid framework if:
702   // 1) |reporter_service_package| is set
703   // 2) |skip_report| is absent or false.
704   // 3) we are not simply activating triggers.
705   report_to_android_framework_ =
706       has_android_reporter_package &&
707       !trace_config_->android_report_config().skip_report() && !has_triggers;
708 
709   // Respect the wishes of the config with respect to statsd logging or fall
710   // back on the presence of the --upload flag if not set.
711   switch (trace_config_->statsd_logging()) {
712     case TraceConfig::STATSD_LOGGING_ENABLED:
713       statsd_logging_ = true;
714       break;
715     case TraceConfig::STATSD_LOGGING_DISABLED:
716       statsd_logging_ = false;
717       break;
718     case TraceConfig::STATSD_LOGGING_UNSPECIFIED:
719       statsd_logging_ = upload_flag_;
720       break;
721   }
722   trace_config_->set_statsd_logging(statsd_logging_
723                                         ? TraceConfig::STATSD_LOGGING_ENABLED
724                                         : TraceConfig::STATSD_LOGGING_DISABLED);
725 
726   // Set up the output file. Either --out or --upload are expected, with the
727   // only exception of --attach. In this case the output file is passed when
728   // detaching.
729   if (!trace_out_path_.empty() && upload_flag_) {
730     PERFETTO_ELOG(
731         "Can't log to a file (--out) and incidentd (--upload) at the same "
732         "time");
733     return 1;
734   }
735 
736   if (!trace_config_->output_path().empty()) {
737     if (!trace_out_path_.empty() || upload_flag_) {
738       PERFETTO_ELOG(
739           "Can't pass --out or --upload if output_path is set in the "
740           "trace config");
741       return 1;
742     }
743     if (base::FileExists(trace_config_->output_path())) {
744       PERFETTO_ELOG(
745           "The output_path must not exist, the service cannot overwrite "
746           "existing files for security reasons. Remove %s or use a different "
747           "path.",
748           trace_config_->output_path().c_str());
749       return 1;
750     }
751   }
752 
753   // |activate_triggers| in the trace config is shorthand for trigger_perfetto.
754   // In this case we don't intend to send any trace config to the service,
755   // rather use that as a signal to the cmdline client to connect as a producer
756   // and activate triggers.
757   if (has_triggers) {
758     for (const auto& trigger : trace_config_->activate_triggers()) {
759       triggers_to_activate_.push_back(trigger);
760     }
761     trace_config_.reset(new TraceConfig());
762   }
763 
764   bool open_out_file = true;
765   if (!will_trace_or_trigger) {
766     open_out_file = false;
767     if (!trace_out_path_.empty() || upload_flag_) {
768       PERFETTO_ELOG("Can't pass an --out file (or --upload) with this option");
769       return 1;
770     }
771   } else if (!triggers_to_activate_.empty() ||
772              (trace_config_->write_into_file() &&
773               !trace_config_->output_path().empty())) {
774     open_out_file = false;
775   } else if (trace_out_path_.empty() && !upload_flag_) {
776     PERFETTO_ELOG("Either --out or --upload is required");
777     return 1;
778   } else if (is_detach() && !trace_config_->write_into_file()) {
779     // In detached mode we must pass the file descriptor to the service and
780     // let that one write the trace. We cannot use the IPC readback code path
781     // because the client process is about to exit soon after detaching.
782     // We could support detach && !write_into_file, but that would make the
783     // cmdline logic more complex. The feasible configurations are:
784     // 1. Using write_into_file and passing the file path on the --detach call.
785     // 2. Using pure ring-buffer mode, setting write_into_file = false and
786     //    passing the output file path to the --attach call.
787     // This is too complicated and harder to reason about, so we support only 1.
788     // Traceur gets around this by always setting write_into_file and specifying
789     // file_write_period_ms = 1week (which effectively means: write into the
790     // file only at the end of the trace) to achieve ring buffer traces.
791     PERFETTO_ELOG(
792         "TraceConfig's write_into_file must be true when using --detach");
793     return 1;
794   }
795   if (open_out_file) {
796     if (!OpenOutputFile())
797       return 1;
798     if (!trace_config_->write_into_file())
799       packet_writer_ = CreateFilePacketWriter(trace_out_stream_.get());
800   }
801 
802   // TODO(b/281043457): this code path will go away after Android U. Compression
803   // has been moved to the service. This code is here only as a fallback in case
804   // of bugs in the U timeframe.
805   if (trace_config_->compress_from_cli() &&
806       trace_config_->compression_type() ==
807           TraceConfig::COMPRESSION_TYPE_DEFLATE) {
808     if (packet_writer_) {
809 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
810       packet_writer_ = CreateZipPacketWriter(std::move(packet_writer_));
811 #else
812       PERFETTO_ELOG("Cannot compress. Zlib not enabled in the build config");
813 #endif
814     } else {
815       PERFETTO_ELOG("Cannot compress when tracing directly to file.");
816     }
817   }
818 
819   bool will_trace_indefinitely =
820       trace_config_->duration_ms() == 0 &&
821       trace_config_->trigger_config().trigger_timeout_ms() == 0;
822   if (will_trace_indefinitely && save_to_incidentd_ && !ignore_guardrails_) {
823     PERFETTO_ELOG("Can't trace indefinitely when tracing to Incidentd.");
824     return 1;
825   }
826 
827   if (will_trace_indefinitely && report_to_android_framework_ &&
828       !ignore_guardrails_) {
829     PERFETTO_ELOG(
830         "Can't trace indefinitely when reporting to Android framework.");
831     return 1;
832   }
833 
834   if (background_) {
835     if (background_wait_) {
836 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
837       background_wait_pipe_ = base::Pipe::Create(base::Pipe::kRdNonBlock);
838 #endif
839     }
840 
841     PERFETTO_CHECK(!snapshot_thread_);  // No threads before demonization.
842     base::Daemonize([this]() -> int {
843       background_wait_pipe_.wr.reset();
844 
845       if (background_wait_) {
846         return WaitOnBgProcessPipe();
847       }
848 
849       return 0;
850     });
851     background_wait_pipe_.rd.reset();
852   }
853 
854   return std::nullopt;  // Continues in ConnectToServiceRunAndMaybeNotify()
855                         // below.
856 }
857 
NotifyBgProcessPipe(BgProcessStatus status)858 void PerfettoCmd::NotifyBgProcessPipe(BgProcessStatus status) {
859 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
860   if (!background_wait_pipe_.wr) {
861     return;
862   }
863   static_assert(sizeof status == 1, "Enum bigger than one byte");
864   PERFETTO_EINTR(write(background_wait_pipe_.wr.get(), &status, 1));
865   background_wait_pipe_.wr.reset();
866 #else   // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
867   base::ignore_result(status);
868 #endif  //! PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
869 }
870 
WaitOnBgProcessPipe()871 PerfettoCmd::BgProcessStatus PerfettoCmd::WaitOnBgProcessPipe() {
872 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
873   base::ScopedPlatformHandle fd = std::move(background_wait_pipe_.rd);
874   PERFETTO_CHECK(fd);
875 
876   BgProcessStatus msg;
877   static_assert(sizeof msg == 1, "Enum bigger than one byte");
878   std::array<pollfd, 1> pollfds = {pollfd{fd.get(), POLLIN, 0}};
879 
880   int ret = PERFETTO_EINTR(poll(&pollfds[0], pollfds.size(), 30000 /*ms*/));
881   PERFETTO_CHECK(ret >= 0);
882   if (ret == 0) {
883     fprintf(stderr, "Timeout waiting for all data sources to start\n");
884     return kBackgroundTimeout;
885   }
886   ssize_t read_ret = PERFETTO_EINTR(read(fd.get(), &msg, 1));
887   PERFETTO_CHECK(read_ret >= 0);
888   if (read_ret == 0) {
889     fprintf(stderr, "Background process didn't report anything\n");
890     return kBackgroundOtherError;
891   }
892 
893   if (msg != kBackgroundOk) {
894     fprintf(stderr, "Background process failed, BgProcessStatus=%d\n",
895             static_cast<int>(msg));
896     return msg;
897   }
898 #endif  //! PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
899 
900   return kBackgroundOk;
901 }
902 
ConnectToServiceRunAndMaybeNotify()903 int PerfettoCmd::ConnectToServiceRunAndMaybeNotify() {
904   int exit_code = ConnectToServiceAndRun();
905 
906   NotifyBgProcessPipe(exit_code == 0 ? kBackgroundOk : kBackgroundOtherError);
907 
908   return exit_code;
909 }
910 
ConnectToServiceAndRun()911 int PerfettoCmd::ConnectToServiceAndRun() {
912   // If we are just activating triggers then we don't need to rate limit,
913   // connect as a consumer or run the trace. So bail out after processing all
914   // the options.
915   if (!triggers_to_activate_.empty()) {
916     LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate_);
917 
918     bool finished_with_success = false;
919     auto weak_this = weak_factory_.GetWeakPtr();
920     TriggerProducer producer(
921         &task_runner_,
922         [weak_this, &finished_with_success](bool success) {
923           if (!weak_this)
924             return;
925           finished_with_success = success;
926           weak_this->task_runner_.Quit();
927         },
928         &triggers_to_activate_);
929     task_runner_.Run();
930     if (!finished_with_success) {
931       LogTriggerEvents(PerfettoTriggerAtom::kCmdTriggerFail,
932                        triggers_to_activate_);
933     }
934     return finished_with_success ? 0 : 1;
935   }  // if (triggers_to_activate_)
936 
937   if (query_service_) {
938     consumer_endpoint_ =
939         ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
940     task_runner_.Run();
941     return 1;  // We can legitimately get here if the service disconnects.
942   }
943 
944   RateLimiter::Args args{};
945   args.is_user_build = IsUserBuild();
946   args.is_uploading = save_to_incidentd_ || report_to_android_framework_;
947   args.current_time = base::GetWallTimeS();
948   args.ignore_guardrails = ignore_guardrails_;
949   args.allow_user_build_tracing = trace_config_->allow_user_build_tracing();
950   args.unique_session_name = trace_config_->unique_session_name();
951   args.max_upload_bytes_override =
952       trace_config_->guardrail_overrides().max_upload_per_day_bytes();
953 
954   if (!args.unique_session_name.empty())
955     base::MaybeSetThreadName("p-" + args.unique_session_name);
956 
957   expected_duration_ms_ = trace_config_->duration_ms();
958   if (!expected_duration_ms_) {
959     uint32_t timeout_ms = trace_config_->trigger_config().trigger_timeout_ms();
960     uint32_t max_stop_delay_ms = 0;
961     for (const auto& trigger : trace_config_->trigger_config().triggers()) {
962       max_stop_delay_ms = std::max(max_stop_delay_ms, trigger.stop_delay_ms());
963     }
964     expected_duration_ms_ = timeout_ms + max_stop_delay_ms;
965   }
966 
967   const auto& delay = trace_config_->cmd_trace_start_delay();
968   if (delay.has_min_delay_ms()) {
969     PERFETTO_DCHECK(delay.has_max_delay_ms());
970     std::random_device r;
971     std::minstd_rand minstd(r());
972     std::uniform_int_distribution<uint32_t> dist(delay.min_delay_ms(),
973                                                  delay.max_delay_ms());
974     std::this_thread::sleep_for(std::chrono::milliseconds(dist(minstd)));
975   }
976 
977   if (trace_config_->trigger_config().trigger_timeout_ms() == 0) {
978     LogUploadEvent(PerfettoStatsdAtom::kTraceBegin);
979   } else {
980     LogUploadEvent(PerfettoStatsdAtom::kBackgroundTraceBegin);
981   }
982 
983   auto err_atom = ConvertRateLimiterResponseToAtom(limiter_->ShouldTrace(args));
984   if (err_atom) {
985     LogUploadEvent(err_atom.value());
986     return 1;
987   }
988 
989 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
990   if (!background_ && !is_detach() && !upload_flag_ &&
991       triggers_to_activate_.empty() && !isatty(STDIN_FILENO) &&
992       !isatty(STDERR_FILENO)) {
993     fprintf(stderr,
994             "Warning: No PTY. CTRL+C won't gracefully stop the trace. If you "
995             "are running perfetto via adb shell, use the -tt arg (adb shell "
996             "-t perfetto ...) or consider using the helper script "
997             "tools/record_android_trace from the Perfetto repository.\n\n");
998   }
999 #endif
1000 
1001   consumer_endpoint_ =
1002       ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
1003   SetupCtrlCSignalHandler();
1004   task_runner_.Run();
1005 
1006   return limiter_->OnTraceDone(args, update_guardrail_state_, bytes_written_)
1007              ? 0
1008              : 1;
1009 }
1010 
OnConnect()1011 void PerfettoCmd::OnConnect() {
1012   connected_ = true;
1013   LogUploadEvent(PerfettoStatsdAtom::kOnConnect);
1014 
1015   uint32_t events_mask = 0;
1016   if (GetTriggerMode(*trace_config_) ==
1017       TraceConfig::TriggerConfig::CLONE_SNAPSHOT) {
1018     events_mask |= ObservableEvents::TYPE_CLONE_TRIGGER_HIT;
1019   }
1020   if (background_wait_) {
1021     events_mask |= ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED;
1022   }
1023   if (events_mask) {
1024     consumer_endpoint_->ObserveEvents(events_mask);
1025   }
1026 
1027   if (query_service_) {
1028     consumer_endpoint_->QueryServiceState(
1029         [this](bool success, const TracingServiceState& svc_state) {
1030           PrintServiceState(success, svc_state);
1031           fflush(stdout);
1032           exit(success ? 0 : 1);
1033         });
1034     return;
1035   }
1036 
1037   if (is_attach()) {
1038     consumer_endpoint_->Attach(attach_key_);
1039     return;
1040   }
1041 
1042   if (clone_tsid_.has_value()) {
1043     task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
1044                                  kCloneTimeoutMs);
1045     consumer_endpoint_->CloneSession(*clone_tsid_);
1046     return;
1047   }
1048 
1049   if (expected_duration_ms_) {
1050     PERFETTO_LOG("Connected to the Perfetto traced service, TTL: %ds",
1051                  (expected_duration_ms_ + 999) / 1000);
1052   } else {
1053     PERFETTO_LOG("Connected to the Perfetto traced service, starting tracing");
1054   }
1055 
1056   PERFETTO_DCHECK(trace_config_);
1057   trace_config_->set_enable_extra_guardrails(
1058       (save_to_incidentd_ || report_to_android_framework_) &&
1059       !ignore_guardrails_);
1060 
1061   // Set the statsd logging flag if we're uploading
1062 
1063   base::ScopedFile optional_fd;
1064   if (trace_config_->write_into_file() && trace_config_->output_path().empty())
1065     optional_fd.reset(dup(fileno(*trace_out_stream_)));
1066 
1067   consumer_endpoint_->EnableTracing(*trace_config_, std::move(optional_fd));
1068 
1069   if (is_detach()) {
1070     consumer_endpoint_->Detach(detach_key_);  // Will invoke OnDetach() soon.
1071     return;
1072   }
1073 
1074   // Failsafe mechanism to avoid waiting indefinitely if the service hangs.
1075   // Note: when using prefer_suspend_clock_for_duration the actual duration
1076   // might be < expected_duration_ms_ measured in in wall time. But this is fine
1077   // because the resulting timeout will be conservative (it will be accurate
1078   // if the device never suspends, and will be more lax if it does).
1079   if (expected_duration_ms_) {
1080     uint32_t trace_timeout = expected_duration_ms_ + 60000 +
1081                              trace_config_->flush_timeout_ms() +
1082                              trace_config_->data_source_stop_timeout_ms();
1083     task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
1084                                  trace_timeout);
1085   }
1086 }
1087 
OnDisconnect()1088 void PerfettoCmd::OnDisconnect() {
1089   if (connected_) {
1090     PERFETTO_LOG("Disconnected from the traced service");
1091   } else {
1092 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
1093     static const char kDocUrl[] =
1094         "https://perfetto.dev/docs/quickstart/android-tracing";
1095 #else
1096     static const char kDocUrl[] =
1097         "https://perfetto.dev/docs/quickstart/linux-tracing";
1098 #endif
1099     PERFETTO_LOG(
1100         "Could not connect to the traced socket %s. Ensure traced is "
1101         "running or use tracebox. See %s.",
1102         GetConsumerSocket(), kDocUrl);
1103   }
1104 
1105   connected_ = false;
1106   task_runner_.Quit();
1107 }
1108 
OnTimeout()1109 void PerfettoCmd::OnTimeout() {
1110   PERFETTO_ELOG("Timed out while waiting for trace from the service, aborting");
1111   LogUploadEvent(PerfettoStatsdAtom::kOnTimeout);
1112   task_runner_.Quit();
1113 }
1114 
CheckTraceDataTimeout()1115 void PerfettoCmd::CheckTraceDataTimeout() {
1116   if (trace_data_timeout_armed_) {
1117     PERFETTO_ELOG("Timed out while waiting for OnTraceData, aborting");
1118     FinalizeTraceAndExit();
1119   }
1120   trace_data_timeout_armed_ = true;
1121   task_runner_.PostDelayedTask(
1122       std::bind(&PerfettoCmd::CheckTraceDataTimeout, this),
1123       kOnTraceDataTimeoutMs);
1124 }
1125 
OnTraceData(std::vector<TracePacket> packets,bool has_more)1126 void PerfettoCmd::OnTraceData(std::vector<TracePacket> packets, bool has_more) {
1127   trace_data_timeout_armed_ = false;
1128 
1129   if (!packet_writer_->WritePackets(packets)) {
1130     PERFETTO_ELOG("Failed to write packets");
1131     FinalizeTraceAndExit();
1132   }
1133 
1134   if (!has_more)
1135     FinalizeTraceAndExit();  // Reached end of trace.
1136 }
1137 
OnTracingDisabled(const std::string & error)1138 void PerfettoCmd::OnTracingDisabled(const std::string& error) {
1139   ReadbackTraceDataAndQuit(error);
1140 }
1141 
ReadbackTraceDataAndQuit(const std::string & error)1142 void PerfettoCmd::ReadbackTraceDataAndQuit(const std::string& error) {
1143   if (!error.empty()) {
1144     // Some of these errors (e.g. unique session name already exists) are soft
1145     // errors and likely to happen in nominal condition. As such they shouldn't
1146     // be marked as "E" in the event log. Hence why LOG and not ELOG here.
1147     PERFETTO_LOG("Service error: %s", error.c_str());
1148 
1149     // In case of errors don't leave a partial file around. This happens
1150     // frequently in the case of --save-for-bugreport if there is no eligible
1151     // trace. See also b/279753347 .
1152     if (bytes_written_ == 0 && !trace_out_path_.empty() &&
1153         trace_out_path_ != "-") {
1154       remove(trace_out_path_.c_str());
1155     }
1156 
1157     // Update guardrail state even if we failed. This is for two
1158     // reasons:
1159     // 1. Keeps compatibility with pre-stats code which used to
1160     // ignore errors from the service and always update state.
1161     // 2. We want to prevent failure storms and the guardrails help
1162     // by preventing tracing too frequently with the same session.
1163     update_guardrail_state_ = true;
1164     task_runner_.Quit();
1165     return;
1166   }
1167 
1168   // Make sure to only log this atom if |error| is empty; traced
1169   // would have logged a terminal error atom corresponding to |error|
1170   // and we don't want to log anything after that.
1171   LogUploadEvent(PerfettoStatsdAtom::kOnTracingDisabled);
1172 
1173   if (trace_config_->write_into_file()) {
1174     // If write_into_file == true, at this point the passed file contains
1175     // already all the packets.
1176     return FinalizeTraceAndExit();
1177   }
1178 
1179   trace_data_timeout_armed_ = false;
1180   CheckTraceDataTimeout();
1181 
1182   // This will cause a bunch of OnTraceData callbacks. The last one will
1183   // save the file and exit.
1184   consumer_endpoint_->ReadBuffers();
1185 }
1186 
FinalizeTraceAndExit()1187 void PerfettoCmd::FinalizeTraceAndExit() {
1188   LogUploadEvent(PerfettoStatsdAtom::kFinalizeTraceAndExit);
1189   packet_writer_.reset();
1190 
1191   if (trace_out_stream_) {
1192     fseek(*trace_out_stream_, 0, SEEK_END);
1193     off_t sz = ftell(*trace_out_stream_);
1194     if (sz > 0)
1195       bytes_written_ = static_cast<size_t>(sz);
1196   }
1197 
1198   if (save_to_incidentd_) {
1199 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
1200     SaveTraceIntoIncidentOrCrash();
1201 #endif
1202   } else if (report_to_android_framework_) {
1203 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
1204     ReportTraceToAndroidFrameworkOrCrash();
1205 #endif
1206   } else {
1207     trace_out_stream_.reset();
1208     if (trace_config_->write_into_file()) {
1209       // trace_out_path_ might be empty in the case of --attach.
1210       PERFETTO_LOG("Trace written into the output file");
1211     } else {
1212       PERFETTO_LOG("Wrote %" PRIu64 " bytes into %s", bytes_written_,
1213                    trace_out_path_ == "-" ? "stdout" : trace_out_path_.c_str());
1214     }
1215   }
1216 
1217 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
1218   // When multiple traces are being recorded at the same time, this is used to
1219   // correlate one trace with another.
1220   ReportFinalizeTraceUuidToAtrace(base::Uuid(uuid_));
1221 #endif
1222 
1223   update_guardrail_state_ = true;
1224   task_runner_.Quit();
1225 }
1226 
OpenOutputFile()1227 bool PerfettoCmd::OpenOutputFile() {
1228   base::ScopedFile fd;
1229   if (trace_out_path_.empty()) {
1230 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
1231     fd = CreateUnlinkedTmpFile();
1232 #endif
1233   } else if (trace_out_path_ == "-") {
1234     fd.reset(dup(fileno(stdout)));
1235   } else {
1236     fd = base::OpenFile(trace_out_path_, O_RDWR | O_CREAT | O_TRUNC, 0600);
1237   }
1238   if (!fd) {
1239     PERFETTO_PLOG(
1240         "Failed to open %s. If you get permission denied in "
1241         "/data/misc/perfetto-traces, the file might have been "
1242         "created by another user, try deleting it first.",
1243         trace_out_path_.c_str());
1244     return false;
1245   }
1246   trace_out_stream_.reset(fdopen(fd.release(), "wb"));
1247   PERFETTO_CHECK(trace_out_stream_);
1248   return true;
1249 }
1250 
SetupCtrlCSignalHandler()1251 void PerfettoCmd::SetupCtrlCSignalHandler() {
1252   // Only the main thread instance should handle CTRL+C.
1253   if (g_perfetto_cmd != this)
1254     return;
1255   ctrl_c_handler_installed_ = true;
1256   base::InstallCtrlCHandler([] {
1257     if (!g_perfetto_cmd)
1258       return;
1259     g_perfetto_cmd->SignalCtrlC();
1260   });
1261   auto weak_this = weak_factory_.GetWeakPtr();
1262   task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [weak_this] {
1263     if (!weak_this)
1264       return;
1265     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
1266     weak_this->ctrl_c_evt_.Clear();
1267     weak_this->consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
1268       if (!weak_this)
1269         return;
1270       if (!flush_success)
1271         PERFETTO_ELOG("Final flush unsuccessful.");
1272       weak_this->consumer_endpoint_->DisableTracing();
1273     });
1274   });
1275 }
1276 
OnDetach(bool success)1277 void PerfettoCmd::OnDetach(bool success) {
1278   if (!success) {
1279     PERFETTO_ELOG("Session detach failed");
1280     exit(1);
1281   }
1282   exit(0);
1283 }
1284 
OnAttach(bool success,const TraceConfig & trace_config)1285 void PerfettoCmd::OnAttach(bool success, const TraceConfig& trace_config) {
1286   if (!success) {
1287     if (!redetach_once_attached_) {
1288       // Print an error message if attach fails, with the exception of the
1289       // --is_detached case, where we want to silently return.
1290       PERFETTO_ELOG("Session re-attach failed. Check service logs for details");
1291     }
1292     // Keep this exit code distinguishable from the general error code so
1293     // --is_detached can tell the difference between a general error and the
1294     // not-detached case.
1295     exit(2);
1296   }
1297 
1298   if (redetach_once_attached_) {
1299     consumer_endpoint_->Detach(attach_key_);  // Will invoke OnDetach() soon.
1300     return;
1301   }
1302 
1303   trace_config_.reset(new TraceConfig(trace_config));
1304   PERFETTO_DCHECK(trace_config_->write_into_file());
1305 
1306   if (stop_trace_once_attached_) {
1307     auto weak_this = weak_factory_.GetWeakPtr();
1308     consumer_endpoint_->Flush(0, [weak_this](bool flush_success) {
1309       if (!weak_this)
1310         return;
1311       if (!flush_success)
1312         PERFETTO_ELOG("Final flush unsuccessful.");
1313       weak_this->consumer_endpoint_->DisableTracing();
1314     });
1315   }
1316 }
1317 
OnTraceStats(bool,const TraceStats &)1318 void PerfettoCmd::OnTraceStats(bool /*success*/,
1319                                const TraceStats& /*trace_config*/) {
1320   // TODO(eseckler): Support GetTraceStats().
1321 }
1322 
OnSessionCloned(const OnSessionClonedArgs & args)1323 void PerfettoCmd::OnSessionCloned(const OnSessionClonedArgs& args) {
1324   PERFETTO_DLOG("Cloned tracing session %" PRIu64 ", success=%d",
1325                 clone_tsid_.value_or(0), args.success);
1326   std::string full_error;
1327   if (!args.success) {
1328     full_error = "Failed to clone tracing session " +
1329                  std::to_string(clone_tsid_.value_or(0)) + ": " + args.error;
1330   }
1331 
1332   // Kick off the readback and file finalization (as if we started tracing and
1333   // reached the duration_ms timeout).
1334   uuid_ = args.uuid.ToString();
1335   ReadbackTraceDataAndQuit(full_error);
1336 }
1337 
PrintServiceState(bool success,const TracingServiceState & svc_state)1338 void PerfettoCmd::PrintServiceState(bool success,
1339                                     const TracingServiceState& svc_state) {
1340   if (!success) {
1341     PERFETTO_ELOG("Failed to query the service state");
1342     return;
1343   }
1344 
1345   if (query_service_output_raw_) {
1346     std::string str = svc_state.SerializeAsString();
1347     fwrite(str.data(), 1, str.size(), stdout);
1348     return;
1349   }
1350 
1351   printf(
1352       "\x1b[31mNot meant for machine consumption. Use --query-raw for "
1353       "scripts.\x1b[0m\n\n");
1354   printf(
1355       "Service: %s\n"
1356       "Tracing sessions: %d (started: %d)\n",
1357       svc_state.tracing_service_version().c_str(), svc_state.num_sessions(),
1358       svc_state.num_sessions_started());
1359 
1360   printf(R"(
1361 
1362 PRODUCER PROCESSES CONNECTED:
1363 
1364 ID         PID        UID        NAME                             SDK
1365 ==         ===        ===        ====                             ===
1366 )");
1367   for (const auto& producer : svc_state.producers()) {
1368     printf("%-10d %-10d %-10d %-32s %s\n", producer.id(), producer.pid(),
1369            producer.uid(), producer.name().c_str(),
1370            producer.sdk_version().c_str());
1371   }
1372 
1373   printf(R"(
1374 
1375 DATA SOURCES REGISTERED:
1376 
1377 NAME                                     PRODUCER                     DETAILS
1378 ===                                      ========                     ========
1379 )");
1380   for (const auto& ds : svc_state.data_sources()) {
1381     char producer_id_and_name[128]{};
1382     const int ds_producer_id = ds.producer_id();
1383     for (const auto& producer : svc_state.producers()) {
1384       if (producer.id() == ds_producer_id) {
1385         base::SprintfTrunc(producer_id_and_name, sizeof(producer_id_and_name),
1386                            "%s (%d)", producer.name().c_str(), ds_producer_id);
1387         break;
1388       }
1389     }
1390 
1391     printf("%-40s %-40s ", ds.ds_descriptor().name().c_str(),
1392            producer_id_and_name);
1393     // Print the category names for clients using the track event SDK.
1394     if (!ds.ds_descriptor().track_event_descriptor_raw().empty()) {
1395       const std::string& raw = ds.ds_descriptor().track_event_descriptor_raw();
1396       protos::gen::TrackEventDescriptor desc;
1397       if (desc.ParseFromArray(raw.data(), raw.size())) {
1398         for (const auto& cat : desc.available_categories()) {
1399           printf("%s,", cat.name().c_str());
1400         }
1401       }
1402     } else if (!ds.ds_descriptor().ftrace_descriptor_raw().empty()) {
1403       const std::string& raw = ds.ds_descriptor().ftrace_descriptor_raw();
1404       protos::gen::FtraceDescriptor desc;
1405       if (desc.ParseFromArray(raw.data(), raw.size())) {
1406         for (const auto& cat : desc.atrace_categories()) {
1407           printf("%s,", cat.name().c_str());
1408         }
1409       }
1410     }
1411     printf("\n");
1412   }  // for data_sources()
1413 
1414   if (svc_state.supports_tracing_sessions()) {
1415     printf(R"(
1416 
1417 TRACING SESSIONS:
1418 
1419 ID      UID     STATE      BUF (#) KB   DUR (s)   #DS  STARTED  NAME
1420 ===     ===     =====      ==========   =======   ===  =======  ====
1421 )");
1422     for (const auto& sess : svc_state.tracing_sessions()) {
1423       uint32_t buf_tot_kb = 0;
1424       for (uint32_t kb : sess.buffer_size_kb())
1425         buf_tot_kb += kb;
1426       int sec =
1427           static_cast<int>((sess.start_realtime_ns() / 1000000000) % 86400);
1428       int h = sec / 3600;
1429       int m = (sec - (h * 3600)) / 60;
1430       int s = (sec - h * 3600 - m * 60);
1431       printf("%-7" PRIu64 " %-7d %-10s (%d) %-8u %-9u %-4u %02d:%02d:%02d %s\n",
1432              sess.id(), sess.consumer_uid(), sess.state().c_str(),
1433              sess.buffer_size_kb_size(), buf_tot_kb, sess.duration_ms() / 1000,
1434              sess.num_data_sources(), h, m, s,
1435              sess.unique_session_name().c_str());
1436     }  // for tracing_sessions()
1437 
1438     int sessions_listed = static_cast<int>(svc_state.tracing_sessions().size());
1439     if (sessions_listed != svc_state.num_sessions() &&
1440         base::GetCurrentUserId() != 0) {
1441       printf(
1442           "\n"
1443           "NOTE: Some tracing sessions are not reported in the list above.\n"
1444           "This is likely because they are owned by a different UID.\n"
1445           "If you want to list all session, run again this command as root.\n");
1446     }
1447   }  // if (supports_tracing_sessions)
1448 }
1449 
OnObservableEvents(const ObservableEvents & observable_events)1450 void PerfettoCmd::OnObservableEvents(
1451     const ObservableEvents& observable_events) {
1452   if (observable_events.all_data_sources_started()) {
1453     NotifyBgProcessPipe(kBackgroundOk);
1454   }
1455   if (observable_events.has_clone_trigger_hit()) {
1456     int64_t tsid = observable_events.clone_trigger_hit().tracing_session_id();
1457     OnCloneSnapshotTriggerReceived(static_cast<TracingSessionID>(tsid));
1458   }
1459 }
1460 
OnCloneSnapshotTriggerReceived(TracingSessionID tsid)1461 void PerfettoCmd::OnCloneSnapshotTriggerReceived(TracingSessionID tsid) {
1462   PERFETTO_DLOG("Creating snapshot for tracing session %" PRIu64, tsid);
1463 
1464   // Only the main thread instance should be handling snapshots.
1465   // We should never end up in a state where each secondary PerfettoCmd
1466   // instance handles other snapshots and creates other threads.
1467   PERFETTO_CHECK(g_perfetto_cmd == this);
1468 
1469   std::string cmdline;
1470   auto add_argv = [&cmdline](const std::string& str) {
1471     cmdline.append(str);
1472     cmdline.append("\0", 1);
1473   };
1474   add_argv("perfetto");
1475   add_argv("--config");
1476   add_argv(":mem");  // Use the copied config from `snapshot_config_`.
1477   add_argv("--clone");
1478   add_argv(std::to_string(tsid));
1479   if (upload_flag_) {
1480     add_argv("--upload");
1481   } else if (!trace_out_path_.empty()) {
1482     add_argv("--out");
1483     add_argv(trace_out_path_ + "." + std::to_string(snapshot_count_++));
1484   } else {
1485     PERFETTO_FATAL("Cannot use CLONE_SNAPSHOT with the current cmdline args");
1486   }
1487 
1488   if (!snapshot_thread_) {
1489     // The destructor of the main-thread's PerfettoCmdMain will destroy and
1490     // join the secondary thread that we are crating here.
1491     snapshot_thread_.reset(new base::ThreadTaskRunner(
1492         base::ThreadTaskRunner::CreateAndStart("snapshot")));
1493   }
1494 
1495   // We need to pass a copy of the trace config to the new PerfettoCmd instance
1496   // because the trace config defines a bunch of properties that are used by the
1497   // cmdline client (reporter API package, guardrails, etc).
1498   std::string trace_config_copy = trace_config_->SerializeAsString();
1499 
1500   snapshot_thread_->PostTask([tsid, cmdline, trace_config_copy] {
1501     int argc = 0;
1502     char* argv[32];
1503     // `splitter` needs to live on the stack for the whole scope as it owns the
1504     // underlying string storage (that gets std::moved) passed PerfettoCmd.
1505     base::StringSplitter splitter(std::move(cmdline), '\0');
1506     while (splitter.Next()) {
1507       argv[argc++] = splitter.cur_token();
1508       PERFETTO_CHECK(static_cast<size_t>(argc) < base::ArraySize(argv));
1509     }
1510     perfetto::PerfettoCmd cmd;
1511     cmd.snapshot_config_ = std::move(trace_config_copy);
1512     auto cmdline_res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
1513     PERFETTO_CHECK(!cmdline_res.has_value());  // No daemonization expected.
1514     int res = cmd.ConnectToServiceRunAndMaybeNotify();
1515     if (res)
1516       PERFETTO_ELOG("Cloning session %" PRIu64 " failed (%d)", tsid, res);
1517   });
1518 }
1519 
LogUploadEvent(PerfettoStatsdAtom atom)1520 void PerfettoCmd::LogUploadEvent(PerfettoStatsdAtom atom) {
1521   if (!statsd_logging_)
1522     return;
1523   base::Uuid uuid(uuid_);
1524   android_stats::MaybeLogUploadEvent(atom, uuid.lsb(), uuid.msb());
1525 }
1526 
LogTriggerEvents(PerfettoTriggerAtom atom,const std::vector<std::string> & trigger_names)1527 void PerfettoCmd::LogTriggerEvents(
1528     PerfettoTriggerAtom atom,
1529     const std::vector<std::string>& trigger_names) {
1530   if (!statsd_logging_)
1531     return;
1532   android_stats::MaybeLogTriggerEvents(atom, trigger_names);
1533 }
1534 
PerfettoCmdMain(int argc,char ** argv)1535 int PERFETTO_EXPORT_ENTRYPOINT PerfettoCmdMain(int argc, char** argv) {
1536   perfetto::PerfettoCmd cmd;
1537   auto opt_res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
1538   if (opt_res.has_value())
1539     return *opt_res;
1540   return cmd.ConnectToServiceRunAndMaybeNotify();
1541 }
1542 
1543 }  // namespace perfetto
1544