xref: /aosp_15_r20/external/perfetto/src/tracebox/tracebox.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2021 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 <algorithm>
18 #include <cstdint>
19 #include <cstdio>
20 #include <cstdlib>
21 #include <cstring>
22 #include <string>
23 
24 #include "perfetto/base/build_config.h"
25 #include "perfetto/base/logging.h"
26 #include "perfetto/base/proc_utils.h"
27 #include "perfetto/ext/base/file_utils.h"
28 #include "perfetto/ext/base/pipe.h"
29 #include "perfetto/ext/base/subprocess.h"
30 #include "perfetto/ext/base/utils.h"
31 #include "perfetto/ext/traced/traced.h"
32 #include "src/perfetto_cmd/perfetto_cmd.h"
33 #include "src/websocket_bridge/websocket_bridge.h"
34 
35 #if PERFETTO_BUILDFLAG(PERFETTO_TRACED_PERF)
36 #include "src/profiling/perf/traced_perf.h"
37 #endif
38 
39 namespace perfetto {
40 namespace {
41 
42 struct Applet {
43   using MainFunction = int (*)(int /*argc*/, char** /*argv*/);
44   const char* name;
45   MainFunction entrypoint;
46 };
47 
48 const Applet g_applets[]{
49     {"traced", ServiceMain},
50     {"traced_probes", ProbesMain},
51 #if PERFETTO_BUILDFLAG(PERFETTO_TRACED_PERF)
52     {"traced_perf", TracedPerfMain},
53 #endif
54     {"perfetto", PerfettoCmdMain},
55     {"trigger_perfetto", TriggerPerfettoMain},
56     {"websocket_bridge", WebsocketBridgeMain},
57 };
58 
PrintUsage()59 void PrintUsage() {
60   printf(R"(Welcome to Perfetto tracing!
61 
62 Tracebox is a bundle containing all the tracing services and the perfetto
63 cmdline client in one binary. It can be used either to spawn manually the
64 various subprocess or in "autostart" mode, which will take care of starting
65 and tearing down the services for you.
66 
67 Usage in autostart mode:
68   tracebox -t 10s -o trace_file.perfetto-trace sched/sched_switch
69   See tracebox --help for more options.
70 
71 Usage in manual mode:
72   tracebox applet_name [args ...]  (e.g. ./tracebox traced --help)
73   Applets:)");
74 
75   for (const Applet& applet : g_applets)
76     printf(" %s", applet.name);
77 
78   printf(R"(
79 
80 See also:
81   * https://perfetto.dev/docs/
82   * The config editor in the record page of https://ui.perfetto.dev/
83 )");
84 }
85 
PrintTraceboxUsage()86 void PrintTraceboxUsage() {
87   printf(R"(
88 Tracebox-specific args
89   --system-sockets      : Forces the use of system-sockets when using autostart
90                           mode. Cannot be used in applet mode.
91 )");
92 }
93 
TraceboxMain(int argc,char ** argv)94 int TraceboxMain(int argc, char** argv) {
95   // Manual mode: if either the 1st argument (argv[1]) or the exe name (argv[0])
96   // match the name of an applet, directly invoke that without further
97   // modifications.
98 
99   // Extract the file name from argv[0].
100   char* slash = strrchr(argv[0], '/');
101   char* argv0 = slash ? slash + 1 : argv[0];
102 
103   for (const Applet& applet : g_applets) {
104     if (!strcmp(argv0, applet.name))
105       return applet.entrypoint(argc, argv);
106     if (argc > 1 && !strcmp(argv[1], applet.name))
107       return applet.entrypoint(argc - 1, &argv[1]);
108   }
109 
110   // If no matching applet is found, switch to the autostart mode. In this mode
111   // we make tracebox behave like the cmdline client (without needing to prefix
112   // it with "perfetto"), but will also start traced and traced_probes.
113   // As part of this we also use a different namespace for the producer/consumer
114   // sockets, to avoid clashing with the system daemon.
115 
116   if (argc <= 1) {
117     PrintUsage();
118     return 1;
119   }
120 
121   auto* end = std::remove_if(argv, argv + argc, [](char* arg) {
122     return !strcmp(arg, "--system-sockets");
123   });
124   if (end < (argv + argc - 1)) {
125     PERFETTO_ELOG("Cannot specify --system-sockets multiple times");
126     return 1;
127   }
128   if (bool system_sockets = end == (argv + argc - 1); system_sockets) {
129     argc--;
130   } else {
131     auto pid_str = std::to_string(static_cast<uint64_t>(base::GetProcessId()));
132 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
133     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
134     // Use an unlinked abstract domain socket on Linux/Android.
135     std::string consumer_socket = "@traced-c-" + pid_str;
136     std::string producer_socket = "@traced-p-" + pid_str;
137 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
138     std::string consumer_socket = "/tmp/traced-c-" + pid_str;
139     std::string producer_socket = "/tmp/traced-p-" + pid_str;
140 #else
141     PERFETTO_FATAL("The autostart mode is not supported on this platform");
142 #endif
143 
144     // If the caller has set the PERFETTO_*_SOCK_NAME, respect those.
145     if (const char* env = getenv("PERFETTO_CONSUMER_SOCK_NAME"); env) {
146       consumer_socket = env;
147     }
148     if (const char* env = getenv("PERFETTO_PRODUCER_SOCK_NAME"); env) {
149       producer_socket = env;
150     }
151     base::SetEnv("PERFETTO_CONSUMER_SOCK_NAME", consumer_socket);
152     base::SetEnv("PERFETTO_PRODUCER_SOCK_NAME", producer_socket);
153   }
154 
155   PerfettoCmd perfetto_cmd;
156 
157   // If the cmdline parsing fails, stop here, no need to spawn services.
158   // It will daemonize if --background. In that case the subprocesses will be
159   // spawned by the damonized cmdline client, which is what we want so killing
160   // the backgrounded cmdline client will also kill the other services, as they
161   // will live in the same background session.
162   auto opt_res = perfetto_cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
163   if (opt_res.has_value()) {
164     if (*opt_res != 0) {
165       PrintTraceboxUsage();
166     }
167     return *opt_res;
168   }
169 
170   std::string self_path = base::GetCurExecutablePath();
171   base::Subprocess traced({self_path, "traced"});
172 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
173   // |traced_sync_pipe| is used to synchronize with traced socket creation.
174   // traced will write "1" and close the FD when the IPC socket is listening
175   // (or traced crashed).
176   base::Pipe traced_sync_pipe = base::Pipe::Create();
177   int traced_fd = *traced_sync_pipe.wr;
178   base::SetEnv("TRACED_NOTIFY_FD", std::to_string(traced_fd));
179   traced.args.preserve_fds.emplace_back(traced_fd);
180   // Create a new process group so CTRL-C is delivered only to the cmdline
181   // process (the tracebox one) and not to traced. traced will still exit once
182   // the main process exits, but this allows graceful stopping of the trace
183   // without abruptedly killing traced{,probes} when hitting CTRL+C.
184   traced.args.posix_proc_group_id = 0;  // 0 = start a new process group.
185 #endif
186   traced.Start();
187 
188 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
189   traced_sync_pipe.wr.reset();
190 
191   std::string traced_notify_msg;
192   base::ReadPlatformHandle(*traced_sync_pipe.rd, &traced_notify_msg);
193   if (traced_notify_msg != "1")
194     PERFETTO_FATAL("The tracing service failed unexpectedly. Check the logs");
195 #endif
196 
197   base::Subprocess traced_probes(
198       {self_path, "traced_probes", "--reset-ftrace"});
199   // Put traced_probes in the same process group as traced. Same reason (CTRL+C)
200   // but it's not worth creating a new group.
201   traced_probes.args.posix_proc_group_id = traced.pid();
202 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
203   // |traced_probes_sync_pipe| is used to synchronize with traced socket
204   // creation. traced will write "1" and close the FD when the IPC socket is
205   // listening (or traced crashed).
206   base::Pipe traced_probes_sync_pipe = base::Pipe::Create();
207   int traced_probes_fd = *traced_probes_sync_pipe.wr;
208   base::SetEnv("TRACED_PROBES_NOTIFY_FD", std::to_string(traced_probes_fd));
209   traced_probes.args.preserve_fds.emplace_back(traced_probes_fd);
210 #endif
211   traced_probes.Start();
212 
213 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
214   traced_probes_sync_pipe.wr.reset();
215 
216   std::string traced_probes_notify_msg;
217   base::ReadPlatformHandle(*traced_probes_sync_pipe.rd,
218                            &traced_probes_notify_msg);
219   if (traced_probes_notify_msg != "1")
220     PERFETTO_FATAL(
221         "The traced_probes service failed unexpectedly. Check the logs");
222 #endif
223 
224 #if PERFETTO_BUILDFLAG(PERFETTO_TRACED_PERF)
225   base::Subprocess traced_perf({self_path, "traced_perf"});
226   // Put traced_perf in the same process group as traced. Same reason (CTRL+C)
227   // but it's not worth creating a new group.
228   traced_perf.args.posix_proc_group_id = traced.pid();
229 
230   base::Pipe traced_perf_sync_pipe = base::Pipe::Create();
231   int traced_perf_fd = *traced_perf_sync_pipe.wr;
232   base::SetEnv("TRACED_PERF_NOTIFY_FD", std::to_string(traced_perf_fd));
233   traced_perf.args.preserve_fds.emplace_back(traced_perf_fd);
234   traced_perf.Start();
235   traced_perf_sync_pipe.wr.reset();
236 
237   std::string traced_perf_notify_msg;
238   base::ReadPlatformHandle(*traced_perf_sync_pipe.rd, &traced_perf_notify_msg);
239   if (traced_perf_notify_msg != "1")
240     PERFETTO_FATAL(
241         "The traced_perf service failed unexpectedly. Check the logs");
242 #endif
243 
244   perfetto_cmd.ConnectToServiceRunAndMaybeNotify();
245   return 0;
246 }
247 
248 }  // namespace
249 }  // namespace perfetto
250 
main(int argc,char ** argv)251 int main(int argc, char** argv) {
252   return perfetto::TraceboxMain(argc, argv);
253 }
254