xref: /aosp_15_r20/external/perfetto/src/profiling/memory/client_api_factory_standalone.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2020 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 "src/profiling/memory/client_api_factory.h"
18 
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/scoped_file.h"
21 #include "perfetto/ext/base/unix_socket.h"
22 #include "perfetto/ext/base/unix_task_runner.h"
23 #include "perfetto/ext/base/utils.h"
24 #include "perfetto/ext/base/watchdog.h"
25 #include "perfetto/heap_profile.h"
26 #include "perfetto/tracing/default_socket.h"
27 #include "src/profiling/common/proc_utils.h"
28 #include "src/profiling/memory/client.h"
29 #include "src/profiling/memory/heap_profile_internal.h"
30 #include "src/profiling/memory/heapprofd_producer.h"
31 
32 #include <string>
33 
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #include <unistd.h>
38 
39 // General approach:
40 // On loading this library, we fork off a process that runs heapprofd. We
41 // share a control socket pair (g_child_sock in client, srv_sock in service)
42 // which is used to:
43 // * Signal a new profiling session was started by sending a byte to
44 //   g_child_sock. This signal gets received in MonitorFd.
45 // * For each profiling session, send a new socket from the client to the
46 //   service. This happens in CreateClient.
47 
48 namespace perfetto {
49 namespace profiling {
50 namespace {
51 
52 base::UnixSocketRaw* g_client_sock;
53 
MonitorFdOnce()54 bool MonitorFdOnce() {
55   char buf[1];
56   ssize_t r = g_client_sock->Receive(buf, sizeof(buf));
57   if (r == 0) {
58     PERFETTO_ELOG("Server disconneced.");
59     return false;
60   }
61   if (r < 0) {
62     PERFETTO_PLOG("Receive failed.");
63     return true;
64   }
65   AHeapProfile_initSession(malloc, free);
66   return true;
67 }
68 
MonitorFd()69 void MonitorFd() {
70   g_client_sock->DcheckIsBlocking(true);
71   for (;;) {
72     bool cont = MonitorFdOnce();
73     if (!cont)
74       break;
75   }
76 }
77 
78 }  // namespace
79 
StartHeapprofdIfStatic()80 void StartHeapprofdIfStatic() {
81   pid_t pid = getpid();
82   std::string cmdline;
83 
84   if (!GetCmdlineForPID(pid, &cmdline)) {
85     PERFETTO_ELOG("Failed to get cmdline.");
86   }
87 
88   g_client_sock = new base::UnixSocketRaw();
89   base::UnixSocketRaw srv_sock;
90   base::UnixSocketRaw cli_sock;
91 
92   std::tie(cli_sock, srv_sock) = base::UnixSocketRaw::CreatePairPosix(
93       base::SockFamily::kUnix, base::SockType::kStream);
94 
95   if (!cli_sock || !srv_sock) {
96     PERFETTO_ELOG("Failed to create socket pair.");
97     return;
98   }
99   pid_t f = fork();
100 
101   if (f == -1) {
102     PERFETTO_PLOG("fork");
103     return;
104   }
105 
106   if (f != 0) {
107     int wstatus;
108     if (PERFETTO_EINTR(waitpid(f, &wstatus, 0)) == -1)
109       PERFETTO_PLOG("waitpid");
110 
111     *g_client_sock = std::move(cli_sock);
112 
113     const char* w = getenv("PERFETTO_HEAPPROFD_BLOCKING_INIT");
114     if (w && w[0] == '1') {
115       g_client_sock->DcheckIsBlocking(true);
116       MonitorFdOnce();
117     }
118 
119     std::thread th(MonitorFd);
120     th.detach();
121     return;
122   }
123 
124   daemon(/* nochdir= */ 0, /* noclose= */ 1);
125 
126   // On debug builds, we want to turn on crash reporting for heapprofd.
127 #if PERFETTO_BUILDFLAG(PERFETTO_STDERR_CRASH_DUMP)
128   base::EnableStacktraceOnCrashForDebug();
129 #endif
130 
131   cli_sock.ReleaseFd();
132 
133   // Leave stderr open for logging.
134   int null = open("/dev/null", O_RDWR);  // NOLINT(android-cloexec-open)
135   dup2(null, STDIN_FILENO);
136   dup2(null, STDOUT_FILENO);
137   if (null > STDERR_FILENO)
138     close(null);
139 
140   for (int i = STDERR_FILENO + 1; i < 512; ++i) {
141     if (i != srv_sock.fd())
142       close(i);
143   }
144 
145   srv_sock.SetBlocking(false);
146 
147   base::UnixTaskRunner task_runner;
148   base::Watchdog::GetInstance()->Start();  // crash on exceedingly long tasks
149   HeapprofdProducer producer(HeapprofdMode::kChild, &task_runner,
150                              /* exit_when_done= */ false);
151   producer.SetTargetProcess(pid, cmdline);
152   producer.ConnectWithRetries(GetProducerSocket());
153   // Signal MonitorFd in the parent process to start a session.
154   producer.SetDataSourceCallback([&srv_sock] { srv_sock.Send("x", 1); });
155   task_runner.AddFileDescriptorWatch(
156       srv_sock.fd(), [&task_runner, &producer, &srv_sock] {
157         base::ScopedFile fd;
158         char buf[1];
159         ssize_t r = srv_sock.Receive(buf, sizeof(buf), &fd, 1);
160         if (r == 0) {
161           PERFETTO_LOG("Child disconnected.");
162           producer.TerminateWhenDone();
163           task_runner.RemoveFileDescriptorWatch(srv_sock.fd());
164         }
165         if (r == -1 && !base::IsAgain(errno)) {
166           PERFETTO_PLOG("Receive");
167         }
168         if (fd) {
169           producer.AdoptSocket(std::move(fd));
170         }
171       });
172   task_runner.Run();
173   // We currently do not Quit the task_runner, but if we ever do it will be
174   // very hard to debug if we don't exit here.
175   exit(0);
176 }
177 
178 // This is called by AHeapProfile_initSession (client_api.cc) to construct a
179 // client.
ConstructClient(UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator)180 std::shared_ptr<Client> ConstructClient(
181     UnhookedAllocator<perfetto::profiling::Client> unhooked_allocator) {
182   if (!g_client_sock)
183     return nullptr;
184 
185   std::shared_ptr<perfetto::profiling::Client> client;
186   base::UnixSocketRaw srv_session_sock;
187   base::UnixSocketRaw client_session_sock;
188 
189   std::tie(srv_session_sock, client_session_sock) =
190       base::UnixSocketRaw::CreatePairPosix(base::SockFamily::kUnix,
191                                            base::SockType::kStream);
192   if (!client_session_sock || !srv_session_sock) {
193     PERFETTO_ELOG("Failed to create socket pair.");
194     return nullptr;
195   }
196   base::ScopedFile srv_fd = srv_session_sock.ReleaseFd();
197   int fd = srv_fd.get();
198   // Send the session socket to the service.
199   g_client_sock->Send(" ", 1, &fd, 1);
200   return perfetto::profiling::Client::CreateAndHandshake(
201       std::move(client_session_sock), unhooked_allocator);
202 }
203 
204 }  // namespace profiling
205 }  // namespace perfetto
206