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