xref: /aosp_15_r20/external/perfetto/src/profiling/memory/client.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2018 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.h"
18 
19 #include <signal.h>
20 #include <sys/prctl.h>
21 #include <sys/syscall.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <algorithm>
26 #include <atomic>
27 #include <cinttypes>
28 #include <new>
29 
30 #include <unwindstack/Regs.h>
31 #include <unwindstack/RegsGetLocal.h>
32 
33 #include "perfetto/base/build_config.h"
34 #include "perfetto/base/compiler.h"
35 #include "perfetto/base/logging.h"
36 #include "perfetto/base/thread_utils.h"
37 #include "perfetto/base/time.h"
38 #include "perfetto/ext/base/file_utils.h"
39 #include "perfetto/ext/base/scoped_file.h"
40 #include "perfetto/ext/base/string_utils.h"
41 #include "perfetto/ext/base/unix_socket.h"
42 #include "perfetto/ext/base/utils.h"
43 #include "src/profiling/memory/sampler.h"
44 #include "src/profiling/memory/scoped_spinlock.h"
45 #include "src/profiling/memory/shared_ring_buffer.h"
46 #include "src/profiling/memory/wire_protocol.h"
47 
48 namespace perfetto {
49 namespace profiling {
50 namespace {
51 
52 const char kSingleByte[1] = {'x'};
53 constexpr auto kResendBackoffUs = 100;
54 
IsMainThread()55 inline bool IsMainThread() {
56   return getpid() == base::GetThreadId();
57 }
58 
UnsetDumpable(int)59 int UnsetDumpable(int) {
60   prctl(PR_SET_DUMPABLE, 0);
61   return 0;
62 }
63 
Contained(const StackRange & base,const char * ptr)64 bool Contained(const StackRange& base, const char* ptr) {
65   return (ptr >= base.begin && ptr < base.end);
66 }
67 
68 }  // namespace
69 
GetMaxTries(const ClientConfiguration & client_config)70 uint64_t GetMaxTries(const ClientConfiguration& client_config) {
71   if (!client_config.block_client)
72     return 1u;
73   if (client_config.block_client_timeout_us == 0)
74     return kInfiniteTries;
75   return std::max<uint64_t>(
76       1ul, client_config.block_client_timeout_us / kResendBackoffUs);
77 }
78 
GetThreadStackRange()79 StackRange GetThreadStackRange() {
80   // In glibc pthread_getattr_np can call realloc, even for a non-main-thread.
81   // This is fine, because the heapprofd wrapper for glibc prevents re-entering
82   // malloc.
83   pthread_attr_t attr;
84   if (pthread_getattr_np(pthread_self(), &attr) != 0)
85     return {nullptr, nullptr};
86   base::ScopedResource<pthread_attr_t*, pthread_attr_destroy, nullptr> cleanup(
87       &attr);
88 
89   char* stackaddr;
90   size_t stacksize;
91   if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(&stackaddr),
92                             &stacksize) != 0)
93     return {nullptr, nullptr};
94   return {stackaddr, stackaddr + stacksize};
95 }
96 
GetSigAltStackRange()97 StackRange GetSigAltStackRange() {
98   stack_t altstack;
99 
100   if (sigaltstack(nullptr, &altstack) == -1) {
101     PERFETTO_PLOG("sigaltstack");
102     return {nullptr, nullptr};
103   }
104 
105   if ((altstack.ss_flags & SS_ONSTACK) == 0) {
106     return {nullptr, nullptr};
107   }
108 
109   return {static_cast<char*>(altstack.ss_sp),
110           static_cast<char*>(altstack.ss_sp) + altstack.ss_size};
111 }
112 
113 // The implementation of pthread_getattr_np for the main thread on bionic uses
114 // malloc, so we cannot use it in GetStackEnd, which we use inside of
115 // RecordMalloc (which is called from malloc). We would re-enter malloc if we
116 // used it.
117 //
118 // This is why we find the stack base for the main-thread when constructing
119 // the client and remember it.
GetMainThreadStackRange()120 StackRange GetMainThreadStackRange() {
121   base::ScopedFstream maps(fopen("/proc/self/maps", "re"));
122   if (!maps) {
123     return {nullptr, nullptr};
124   }
125   while (!feof(*maps)) {
126     char line[1024];
127     char* data = fgets(line, sizeof(line), *maps);
128     if (data != nullptr && strstr(data, "[stack]")) {
129       char* sep = strstr(data, "-");
130       if (sep == nullptr)
131         continue;
132 
133       char* min = reinterpret_cast<char*>(strtoll(data, nullptr, 16));
134       char* max = reinterpret_cast<char*>(strtoll(sep + 1, nullptr, 16));
135       return {min, max};
136     }
137   }
138   return {nullptr, nullptr};
139 }
140 
141 // static
ConnectToHeapprofd(const std::string & sock_name)142 std::optional<base::UnixSocketRaw> Client::ConnectToHeapprofd(
143     const std::string& sock_name) {
144   auto sock = base::UnixSocketRaw::CreateMayFail(base::SockFamily::kUnix,
145                                                  base::SockType::kStream);
146   if (!sock || !sock.Connect(sock_name)) {
147     PERFETTO_PLOG("Failed to connect to %s", sock_name.c_str());
148     return std::nullopt;
149   }
150   if (!sock.SetTxTimeout(kClientSockTimeoutMs)) {
151     PERFETTO_PLOG("Failed to set send timeout for %s", sock_name.c_str());
152     return std::nullopt;
153   }
154   if (!sock.SetRxTimeout(kClientSockTimeoutMs)) {
155     PERFETTO_PLOG("Failed to set receive timeout for %s", sock_name.c_str());
156     return std::nullopt;
157   }
158   return std::move(sock);
159 }
160 
161 // static
CreateAndHandshake(base::UnixSocketRaw sock,UnhookedAllocator<Client> unhooked_allocator)162 std::shared_ptr<Client> Client::CreateAndHandshake(
163     base::UnixSocketRaw sock,
164     UnhookedAllocator<Client> unhooked_allocator) {
165   if (!sock) {
166     PERFETTO_DFATAL_OR_ELOG("Socket not connected.");
167     return nullptr;
168   }
169 
170   sock.DcheckIsBlocking(true);
171 
172   // We might be running in a process that is not dumpable (such as app
173   // processes on user builds), in which case the /proc/self/mem will be chown'd
174   // to root:root, and will not be accessible even to the process itself (see
175   // man 5 proc). In such situations, temporarily mark the process dumpable to
176   // be able to open the files, unsetting dumpability immediately afterwards.
177   int orig_dumpable = prctl(PR_GET_DUMPABLE);
178 
179   enum { kNop, kDoUnset };
180   base::ScopedResource<int, UnsetDumpable, kNop, false> unset_dumpable(kNop);
181   if (orig_dumpable == 0) {
182     unset_dumpable.reset(kDoUnset);
183     prctl(PR_SET_DUMPABLE, 1);
184   }
185 
186   base::ScopedFile maps(base::OpenFile("/proc/self/maps", O_RDONLY));
187   if (!maps) {
188     PERFETTO_DFATAL_OR_ELOG("Failed to open /proc/self/maps");
189     return nullptr;
190   }
191   base::ScopedFile mem(base::OpenFile("/proc/self/mem", O_RDONLY));
192   if (!mem) {
193     PERFETTO_DFATAL_OR_ELOG("Failed to open /proc/self/mem");
194     return nullptr;
195   }
196 
197   // Restore original dumpability value if we overrode it.
198   unset_dumpable.reset();
199 
200   int fds[kHandshakeSize];
201   fds[kHandshakeMaps] = *maps;
202   fds[kHandshakeMem] = *mem;
203 
204   // Send an empty record to transfer fds for /proc/self/maps and
205   // /proc/self/mem.
206   if (sock.Send(kSingleByte, sizeof(kSingleByte), fds, kHandshakeSize) !=
207       sizeof(kSingleByte)) {
208     PERFETTO_DFATAL_OR_ELOG("Failed to send file descriptors.");
209     return nullptr;
210   }
211 
212   ClientConfiguration client_config;
213   base::ScopedFile shmem_fd;
214   size_t recv = 0;
215   while (recv < sizeof(client_config)) {
216     size_t num_fds = 0;
217     base::ScopedFile* fd = nullptr;
218     if (!shmem_fd) {
219       num_fds = 1;
220       fd = &shmem_fd;
221     }
222     ssize_t rd = sock.Receive(reinterpret_cast<char*>(&client_config) + recv,
223                               sizeof(client_config) - recv, fd, num_fds);
224     if (rd == -1) {
225       PERFETTO_PLOG("Failed to receive ClientConfiguration.");
226       return nullptr;
227     }
228     if (rd == 0) {
229       PERFETTO_LOG("Server disconnected while sending ClientConfiguration.");
230       return nullptr;
231     }
232     recv += static_cast<size_t>(rd);
233   }
234 
235   if (!shmem_fd) {
236     PERFETTO_DFATAL_OR_ELOG("Did not receive shmem fd.");
237     return nullptr;
238   }
239 
240   auto shmem = SharedRingBuffer::Attach(std::move(shmem_fd));
241   if (!shmem || !shmem->is_valid()) {
242     PERFETTO_DFATAL_OR_ELOG("Failed to attach to shmem.");
243     return nullptr;
244   }
245 
246   sock.SetBlocking(false);
247   // note: the shared_ptr will retain a copy of the unhooked_allocator
248   return std::allocate_shared<Client>(unhooked_allocator, std::move(sock),
249                                       client_config, std::move(shmem.value()),
250                                       getpid(), GetMainThreadStackRange());
251 }
252 
Client(base::UnixSocketRaw sock,ClientConfiguration client_config,SharedRingBuffer shmem,pid_t pid_at_creation,StackRange main_thread_stack_range)253 Client::Client(base::UnixSocketRaw sock,
254                ClientConfiguration client_config,
255                SharedRingBuffer shmem,
256                pid_t pid_at_creation,
257                StackRange main_thread_stack_range)
258     : client_config_(client_config),
259       max_shmem_tries_(GetMaxTries(client_config_)),
260       sock_(std::move(sock)),
261       main_thread_stack_range_(main_thread_stack_range),
262       shmem_(std::move(shmem)),
263       pid_at_creation_(pid_at_creation) {}
264 
~Client()265 Client::~Client() {
266   // This is work-around for code like the following:
267   // https://android.googlesource.com/platform/libcore/+/4ecb71f94378716f88703b9f7548b5d24839262f/ojluni/src/main/native/UNIXProcess_md.c#427
268   // They fork, close all fds by iterating over /proc/self/fd using opendir.
269   // Unfortunately closedir calls free, which detects the fork, and then tries
270   // to destruct this Client.
271   //
272   // ScopedResource crashes on failure to close, so we explicitly ignore
273   // failures here.
274   int fd = sock_.ReleaseFd().release();
275   if (fd != -1)
276     close(fd);
277 }
278 
GetStackEnd(const char * stackptr)279 const char* Client::GetStackEnd(const char* stackptr) {
280   StackRange thread_stack_range;
281   bool is_main_thread = IsMainThread();
282   if (is_main_thread) {
283     thread_stack_range = main_thread_stack_range_;
284   } else {
285     thread_stack_range = GetThreadStackRange();
286   }
287   if (Contained(thread_stack_range, stackptr)) {
288     return thread_stack_range.end;
289   }
290   StackRange sigalt_stack_range = GetSigAltStackRange();
291   if (Contained(sigalt_stack_range, stackptr)) {
292     return sigalt_stack_range.end;
293   }
294   // The main thread might have expanded since we read its bounds. We now know
295   // it is not the sigaltstack, so it has to be the main stack.
296   // TODO(fmayer): We should reparse maps here, because now we will keep
297   //               hitting the slow-path that calls the sigaltstack syscall.
298   if (is_main_thread && stackptr < thread_stack_range.end) {
299     return thread_stack_range.end;
300   }
301   return nullptr;
302 }
303 
304 // Best-effort detection of whether we're continuing work in a forked child of
305 // the profiled process, in which case we want to stop. Note that due to
306 // malloc_hooks.cc's atfork handler, the proper fork calls should leak the child
307 // before reaching this point. Therefore this logic exists primarily to handle
308 // clone and vfork.
309 // TODO(rsavitski): rename/delete |disable_fork_teardown| config option if this
310 // logic sticks, as the option becomes more clone-specific, and quite narrow.
IsPostFork()311 bool Client::IsPostFork() {
312   if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
313     // Only print the message once, even if we do not shut down the client.
314     if (!detected_fork_) {
315       detected_fork_ = true;
316       const char* vfork_detected = "";
317 
318       // We use the fact that vfork does not update Bionic's TID cache, so
319       // we will have a mismatch between the actual TID (from the syscall)
320       // and the cached one.
321       //
322       // What we really want to check is if we are sharing virtual memory space
323       // with the original process. This would be
324       // syscall(__NR_kcmp, syscall(__NR_getpid), pid_at_creation_,
325       //         KCMP_VM, 0, 0),
326       //  but that is not compiled into our kernels and disallowed by seccomp.
327       if (!client_config_.disable_vfork_detection &&
328           syscall(__NR_gettid) != base::GetThreadId()) {
329         postfork_return_value_ = true;
330         vfork_detected = " (vfork detected)";
331       } else {
332         postfork_return_value_ = client_config_.disable_fork_teardown;
333       }
334       const char* action =
335           postfork_return_value_ ? "Not shutting down" : "Shutting down";
336       const char* force =
337           postfork_return_value_ ? " (fork teardown disabled)" : "";
338       PERFETTO_LOG(
339           "Detected post-fork child situation. Not profiling the child. "
340           "%s client%s%s",
341           action, force, vfork_detected);
342     }
343     return true;
344   }
345   return false;
346 }
347 
348 #if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_RISCV) && \
349     !PERFETTO_HAS_BUILTIN_STACK_ADDRESS()
GetStackRegister(unwindstack::ArchEnum arch)350 ssize_t Client::GetStackRegister(unwindstack::ArchEnum arch) {
351   ssize_t reg_sp, reg_size;
352   switch (arch) {
353     case unwindstack::ARCH_X86:
354       reg_sp = unwindstack::X86_REG_SP;
355       reg_size = sizeof(uint32_t);
356       break;
357     case unwindstack::ARCH_X86_64:
358       reg_sp = unwindstack::X86_64_REG_SP;
359       reg_size = sizeof(uint64_t);
360       break;
361     case unwindstack::ARCH_ARM:
362       reg_sp = unwindstack::ARM_REG_SP;
363       reg_size = sizeof(uint32_t);
364       break;
365     case unwindstack::ARCH_ARM64:
366       reg_sp = unwindstack::ARM64_REG_SP;
367       reg_size = sizeof(uint64_t);
368       break;
369     case unwindstack::ARCH_RISCV64:
370       reg_sp = unwindstack::RISCV64_REG_SP;
371       reg_size = sizeof(uint64_t);
372       break;
373     case unwindstack::ARCH_UNKNOWN:
374       return -1;
375   }
376   return reg_sp * reg_size;
377 }
378 
GetStackAddress(char * reg_data,unwindstack::ArchEnum arch)379 uintptr_t Client::GetStackAddress(char* reg_data, unwindstack::ArchEnum arch) {
380   ssize_t reg = GetStackRegister(arch);
381   if (reg < 0)
382     return reinterpret_cast<uintptr_t>(nullptr);
383   return *reinterpret_cast<uintptr_t*>(&reg_data[reg]);
384 }
385 #endif /* PERFETTO_ARCH_CPU_RISCV && !PERFETTO_HAS_BUILTIN_STACK_ADDRESS() */
386 
387 // The stack grows towards numerically smaller addresses, so the stack layout
388 // of main calling malloc is as follows.
389 //
390 //               +------------+
391 //               |SendWireMsg |
392 // stackptr +--> +------------+ 0x1000
393 //               |RecordMalloc|    +
394 //               +------------+    |
395 //               | malloc     |    |
396 //               +------------+    |
397 //               |  main      |    v
398 // stackend  +-> +------------+ 0xffff
RecordMalloc(uint32_t heap_id,uint64_t sample_size,uint64_t alloc_size,uint64_t alloc_address)399 bool Client::RecordMalloc(uint32_t heap_id,
400                           uint64_t sample_size,
401                           uint64_t alloc_size,
402                           uint64_t alloc_address) {
403   if (PERFETTO_UNLIKELY(IsPostFork())) {
404     return postfork_return_value_;
405   }
406 
407   AllocMetadata metadata;
408   // By the difference between calling conventions, the frame pointer might
409   // include the current frame or not. So, using __builtin_frame_address()
410   // on specific architectures such as riscv can make stack unwinding failed.
411   // Thus, using __builtin_stack_address() or reading the stack pointer in
412   // register data directly instead of using __builtin_frame_address() on riscv.
413 #if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_RISCV)
414 #if PERFETTO_HAS_BUILTIN_STACK_ADDRESS()
415   const char* stackptr = reinterpret_cast<char*>(__builtin_stack_address());
416   unwindstack::AsmGetRegs(metadata.register_data);
417 #else
418   char* register_data = metadata.register_data;
419   unwindstack::AsmGetRegs(register_data);
420   const char* stackptr = reinterpret_cast<char*>(
421       GetStackAddress(register_data, unwindstack::Regs::CurrentArch()));
422   if (!stackptr) {
423     PERFETTO_ELOG("Failed to get stack address.");
424     shmem_.SetErrorState(SharedRingBuffer::kInvalidStackBounds);
425     return false;
426   }
427 #endif /* PERFETTO_HAS_BUILTIN_STACK_ADDRESS() */
428 #else
429   const char* stackptr = reinterpret_cast<char*>(__builtin_frame_address(0));
430   unwindstack::AsmGetRegs(metadata.register_data);
431 #endif /* PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_RISCV) */
432   const char* stackend = GetStackEnd(stackptr);
433   if (!stackend) {
434     PERFETTO_ELOG("Failed to find stackend.");
435     shmem_.SetErrorState(SharedRingBuffer::kInvalidStackBounds);
436     return false;
437   }
438   uint64_t stack_size = static_cast<uint64_t>(stackend - stackptr);
439   metadata.sample_size = sample_size;
440   metadata.alloc_size = alloc_size;
441   metadata.alloc_address = alloc_address;
442   metadata.stack_pointer = reinterpret_cast<uint64_t>(stackptr);
443   metadata.arch = unwindstack::Regs::CurrentArch();
444   metadata.sequence_number =
445       1 + sequence_number_[heap_id].fetch_add(1, std::memory_order_acq_rel);
446   metadata.heap_id = heap_id;
447 
448   struct timespec ts;
449   if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
450     metadata.clock_monotonic_coarse_timestamp =
451         static_cast<uint64_t>(base::FromPosixTimespec(ts).count());
452   } else {
453     metadata.clock_monotonic_coarse_timestamp = 0;
454   }
455 
456   WireMessage msg{};
457   msg.record_type = RecordType::Malloc;
458   msg.alloc_header = &metadata;
459   msg.payload = const_cast<char*>(stackptr);
460   msg.payload_size = static_cast<size_t>(stack_size);
461 
462   if (SendWireMessageWithRetriesIfBlocking(msg) == -1)
463     return false;
464 
465   if (!shmem_.GetAndResetReaderPaused())
466     return true;
467   return SendControlSocketByte();
468 }
469 
SendWireMessageWithRetriesIfBlocking(const WireMessage & msg)470 int64_t Client::SendWireMessageWithRetriesIfBlocking(const WireMessage& msg) {
471   for (uint64_t i = 0;
472        max_shmem_tries_ == kInfiniteTries || i < max_shmem_tries_; ++i) {
473     if (shmem_.shutting_down())
474       return -1;
475     int64_t res = SendWireMessage(&shmem_, msg);
476     if (PERFETTO_LIKELY(res >= 0))
477       return res;
478     // retry if in blocking mode and still connected
479     if (client_config_.block_client && base::IsAgain(errno) && IsConnected()) {
480       usleep(kResendBackoffUs);
481     } else {
482       break;
483     }
484   }
485   if (IsConnected())
486     shmem_.SetErrorState(SharedRingBuffer::kHitTimeout);
487   PERFETTO_PLOG("Failed to write to shared ring buffer. Disconnecting.");
488   return -1;
489 }
490 
RecordFree(uint32_t heap_id,const uint64_t alloc_address)491 bool Client::RecordFree(uint32_t heap_id, const uint64_t alloc_address) {
492   if (PERFETTO_UNLIKELY(IsPostFork())) {
493     return postfork_return_value_;
494   }
495 
496   FreeEntry current_entry;
497   current_entry.sequence_number =
498       1 + sequence_number_[heap_id].fetch_add(1, std::memory_order_acq_rel);
499   current_entry.addr = alloc_address;
500   current_entry.heap_id = heap_id;
501   WireMessage msg = {};
502   msg.record_type = RecordType::Free;
503   msg.free_header = &current_entry;
504   // Do not send control socket byte, as frees are very cheap to handle, so we
505   // just delay to the next alloc. Sending the control socket byte is ~10x the
506   // rest of the client overhead.
507   int64_t bytes_free = SendWireMessageWithRetriesIfBlocking(msg);
508   if (bytes_free == -1)
509     return false;
510   // Seems like we are filling up the shmem with frees. Flush.
511   if (static_cast<uint64_t>(bytes_free) < shmem_.size() / 2 &&
512       shmem_.GetAndResetReaderPaused()) {
513     return SendControlSocketByte();
514   }
515   return true;
516 }
517 
RecordHeapInfo(uint32_t heap_id,const char * heap_name,uint64_t interval)518 bool Client::RecordHeapInfo(uint32_t heap_id,
519                             const char* heap_name,
520                             uint64_t interval) {
521   if (PERFETTO_UNLIKELY(IsPostFork())) {
522     return postfork_return_value_;
523   }
524 
525   HeapName hnr;
526   hnr.heap_id = heap_id;
527   base::StringCopy(&hnr.heap_name[0], heap_name, sizeof(hnr.heap_name));
528   hnr.sample_interval = interval;
529 
530   WireMessage msg = {};
531   msg.record_type = RecordType::HeapName;
532   msg.heap_name_header = &hnr;
533   return SendWireMessageWithRetriesIfBlocking(msg);
534 }
535 
IsConnected()536 bool Client::IsConnected() {
537   sock_.DcheckIsBlocking(false);
538   char buf[1];
539   ssize_t recv_bytes = sock_.Receive(buf, sizeof(buf), nullptr, 0);
540   if (recv_bytes == 0)
541     return false;
542   // This is not supposed to happen because currently heapprofd does not send
543   // data to the client. Here for generality's sake.
544   if (recv_bytes > 0)
545     return true;
546   return base::IsAgain(errno);
547 }
548 
SendControlSocketByte()549 bool Client::SendControlSocketByte() {
550   // If base::IsAgain(errno), the socket buffer is full, so the service will
551   // pick up the notification even without adding another byte.
552   // In other error cases (usually EPIPE) we want to disconnect, because that
553   // is how the service signals the tracing session was torn down.
554   if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1 &&
555       !base::IsAgain(errno)) {
556     if (shmem_.shutting_down()) {
557       PERFETTO_LOG("Profiling session ended.");
558     } else {
559       PERFETTO_PLOG("Failed to send control socket byte.");
560     }
561     return false;
562   }
563   return true;
564 }
565 
566 }  // namespace profiling
567 }  // namespace perfetto
568