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