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