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