• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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