xref: /aosp_15_r20/external/cronet/base/profiler/stack_copier_signal.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/profiler/stack_copier_signal.h"
6 
7 #include <errno.h>
8 #include <linux/futex.h>
9 #include <signal.h>
10 #include <stdint.h>
11 #include <sys/ucontext.h>
12 #include <syscall.h>
13 
14 #include <atomic>
15 #include <cstring>
16 #include <optional>
17 
18 #include "base/memory/raw_ptr.h"
19 #include "base/memory/raw_ptr_exclusion.h"
20 #include "base/notreached.h"
21 #include "base/profiler/register_context.h"
22 #include "base/profiler/stack_buffer.h"
23 #include "base/profiler/suspendable_thread_delegate.h"
24 #include "base/time/time_override.h"
25 #include "base/trace_event/base_tracing.h"
26 #include "build/build_config.h"
27 
28 namespace base {
29 
30 namespace {
31 
32 // Waitable event implementation with futex and without DCHECK(s), since signal
33 // handlers cannot allocate memory or use pthread api.
34 class AsyncSafeWaitableEvent {
35  public:
AsyncSafeWaitableEvent()36   AsyncSafeWaitableEvent() {
37     futex_.store(kNotSignaled, std::memory_order_release);
38   }
39   ~AsyncSafeWaitableEvent() = default;
40   AsyncSafeWaitableEvent(const AsyncSafeWaitableEvent&) = delete;
41   AsyncSafeWaitableEvent& operator=(const AsyncSafeWaitableEvent&) = delete;
42 
Wait()43   bool Wait() {
44     // futex() can wake up spuriously if this memory address was previously used
45     // for a pthread mutex or we get a signal. So, also check the condition.
46     while (true) {
47       long res =
48           syscall(SYS_futex, futex_ptr(), FUTEX_WAIT | FUTEX_PRIVATE_FLAG,
49                   kNotSignaled, nullptr, nullptr, 0);
50       int futex_errno = errno;
51       if (futex_.load(std::memory_order_acquire) != kNotSignaled) {
52         return true;
53       }
54       if (res != 0) {
55         // EINTR indicates the wait was interrupted by a signal; retry the wait.
56         // EAGAIN happens if this thread sees the FUTEX_WAKE before it sees the
57         // atomic_int store in Signal. This can't happen in an unoptimized
58         // single total modification order threading model; however, since we
59         // using release-acquire semantics on the atomic_uint32_t, it might be.
60         // (The futex docs aren't clear what memory/threading model they are
61         // using.)
62         if (futex_errno != EINTR && futex_errno != EAGAIN) {
63           return false;
64         }
65       }
66     }
67   }
68 
Signal()69   void Signal() {
70     futex_.store(kSignaled, std::memory_order_release);
71     syscall(SYS_futex, futex_ptr(), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, nullptr,
72             nullptr, 0);
73   }
74 
75  private:
76   // The possible values in the futex / atomic_int.
77   static constexpr uint32_t kNotSignaled = 0;
78   static constexpr uint32_t kSignaled = 1;
79 
80   // Provides a pointer to the atomic's storage. std::atomic_uint32_t has
81   // standard layout so its address can be used for the pointer as long as it
82   // only contains the uint32_t.
futex_ptr()83   uint32_t* futex_ptr() {
84     // futex documents state the futex is 32 bits regardless of the platform
85     // size.
86     static_assert(sizeof(futex_) == sizeof(uint32_t),
87                   "Expected std::atomic_uint32_t to be the same size as "
88                   "uint32_t");
89     return reinterpret_cast<uint32_t*>(&futex_);
90   }
91 
92   std::atomic_uint32_t futex_{kNotSignaled};
93 };
94 
95 // Scoped signal event that calls Signal on the AsyncSafeWaitableEvent at
96 // destructor.
97 class ScopedEventSignaller {
98  public:
ScopedEventSignaller(AsyncSafeWaitableEvent * event)99   ScopedEventSignaller(AsyncSafeWaitableEvent* event) : event_(event) {}
~ScopedEventSignaller()100   ~ScopedEventSignaller() { event_->Signal(); }
101 
102  private:
103   // RAW_PTR_EXCLUSION: raw_ptr<> is not safe within a signal handler.
104   RAW_PTR_EXCLUSION AsyncSafeWaitableEvent* event_;
105 };
106 
107 // Struct to store the arguments to the signal handler.
108 struct HandlerParams {
109   uintptr_t stack_base_address;
110 
111   // RAW_PTR_EXCLUSION: raw_ptr<> is not safe within a signal handler,
112   // as the target thread could be in the middle of an allocation and
113   // PartitionAlloc's external invariants might be violated. So all
114   // the pointers below are C pointers.
115 
116   // The event is signalled when signal handler is done executing.
117   RAW_PTR_EXCLUSION AsyncSafeWaitableEvent* event;
118 
119   // Return values:
120 
121   // Successfully copied the stack segment.
122   RAW_PTR_EXCLUSION bool* success;
123 
124   // The thread context of the leaf function.
125   RAW_PTR_EXCLUSION mcontext_t* context;
126 
127   // Buffer to copy the stack segment.
128   RAW_PTR_EXCLUSION StackBuffer* stack_buffer;
129   RAW_PTR_EXCLUSION const uint8_t** stack_copy_bottom;
130 
131   // The timestamp when the stack was copied.
132   RAW_PTR_EXCLUSION std::optional<TimeTicks>* maybe_timestamp;
133 
134   // The delegate provided to the StackCopier.
135   RAW_PTR_EXCLUSION StackCopier::Delegate* stack_copier_delegate;
136 };
137 
138 // Pointer to the parameters to be "passed" to the CopyStackSignalHandler() from
139 // the sampling thread to the sampled (stopped) thread. This value is set just
140 // before sending the signal to the thread and reset when the handler is done.
141 std::atomic<HandlerParams*> g_handler_params;
142 
143 // CopyStackSignalHandler is invoked on the stopped thread and records the
144 // thread's stack and register context at the time the signal was received. This
145 // function may only call reentrant code.
CopyStackSignalHandler(int n,siginfo_t * siginfo,void * sigcontext)146 void CopyStackSignalHandler(int n, siginfo_t* siginfo, void* sigcontext) {
147   HandlerParams* params = g_handler_params.load(std::memory_order_acquire);
148 
149   // MaybeTimeTicksNowIgnoringOverride() is implemented in terms of
150   // clock_gettime on Linux, which is signal safe per the signal-safety(7) man
151   // page, but is not garanteed to succeed, in which case std::nullopt is
152   // returned. TimeTicks::Now() can't be used because it expects clock_gettime
153   // to always succeed and is thus not signal-safe.
154   *params->maybe_timestamp = subtle::MaybeTimeTicksNowIgnoringOverride();
155 
156   ScopedEventSignaller e(params->event);
157   *params->success = false;
158 
159   const ucontext_t* ucontext = static_cast<ucontext_t*>(sigcontext);
160   std::memcpy(params->context, &ucontext->uc_mcontext, sizeof(mcontext_t));
161 
162   const uintptr_t bottom = RegisterContextStackPointer(params->context);
163   const uintptr_t top = params->stack_base_address;
164   if ((top - bottom) > params->stack_buffer->size()) {
165     // The stack exceeds the size of the allocated buffer. The buffer is sized
166     // such that this shouldn't happen under typical execution so we can safely
167     // punt in this situation.
168     return;
169   }
170 
171   params->stack_copier_delegate->OnStackCopy();
172 
173   *params->stack_copy_bottom =
174       StackCopierSignal::CopyStackContentsAndRewritePointers(
175           reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
176           StackBuffer::kPlatformStackAlignment, params->stack_buffer->buffer());
177 
178   *params->success = true;
179 }
180 
181 // Sets the global handler params for the signal handler function.
182 class ScopedSetSignalHandlerParams {
183  public:
ScopedSetSignalHandlerParams(HandlerParams * params)184   ScopedSetSignalHandlerParams(HandlerParams* params) {
185     g_handler_params.store(params, std::memory_order_release);
186   }
187 
~ScopedSetSignalHandlerParams()188   ~ScopedSetSignalHandlerParams() {
189     g_handler_params.store(nullptr, std::memory_order_release);
190   }
191 };
192 
193 class ScopedSigaction {
194  public:
ScopedSigaction(int signal,struct sigaction * action,struct sigaction * original_action)195   ScopedSigaction(int signal,
196                   struct sigaction* action,
197                   struct sigaction* original_action)
198       : signal_(signal),
199         action_(action),
200         original_action_(original_action),
201         succeeded_(sigaction(signal, action, original_action) == 0) {}
202 
succeeded() const203   bool succeeded() const { return succeeded_; }
204 
~ScopedSigaction()205   ~ScopedSigaction() {
206     if (!succeeded_)
207       return;
208 
209     bool reset_succeeded = sigaction(signal_, original_action_, action_) == 0;
210     DCHECK(reset_succeeded);
211   }
212 
213  private:
214   const int signal_;
215   const raw_ptr<struct sigaction> action_;
216   const raw_ptr<struct sigaction> original_action_;
217   const bool succeeded_;
218 };
219 
220 }  // namespace
221 
StackCopierSignal(std::unique_ptr<ThreadDelegate> thread_delegate)222 StackCopierSignal::StackCopierSignal(
223     std::unique_ptr<ThreadDelegate> thread_delegate)
224     : thread_delegate_(std::move(thread_delegate)) {}
225 
226 StackCopierSignal::~StackCopierSignal() = default;
227 
CopyStack(StackBuffer * stack_buffer,uintptr_t * stack_top,TimeTicks * timestamp,RegisterContext * thread_context,Delegate * delegate)228 bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
229                                   uintptr_t* stack_top,
230                                   TimeTicks* timestamp,
231                                   RegisterContext* thread_context,
232                                   Delegate* delegate) {
233   AsyncSafeWaitableEvent wait_event;
234   bool copied = false;
235   const uint8_t* stack_copy_bottom = nullptr;
236   const uintptr_t stack_base_address = thread_delegate_->GetStackBaseAddress();
237   std::optional<TimeTicks> maybe_timestamp;
238   HandlerParams params = {stack_base_address, &wait_event,  &copied,
239                           thread_context,     stack_buffer, &stack_copy_bottom,
240                           &maybe_timestamp,   delegate};
241   {
242     ScopedSetSignalHandlerParams scoped_handler_params(&params);
243 
244     // Set the signal handler for the thread to the stack copy function.
245     struct sigaction action;
246     struct sigaction original_action;
247     memset(&action, 0, sizeof(action));
248     action.sa_sigaction = CopyStackSignalHandler;
249     action.sa_flags = SA_RESTART | SA_SIGINFO;
250     sigemptyset(&action.sa_mask);
251     TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
252                        "StackCopierSignal copy stack");
253     // SIGURG is chosen here because we observe no crashes with this signal and
254     // neither Chrome or the AOSP sets up a special handler for this signal.
255     ScopedSigaction scoped_sigaction(SIGURG, &action, &original_action);
256     if (!scoped_sigaction.succeeded())
257       return false;
258 
259     if (syscall(SYS_tgkill, getpid(), thread_delegate_->GetThreadId(),
260                 SIGURG) != 0) {
261       NOTREACHED();
262       return false;
263     }
264     bool finished_waiting = wait_event.Wait();
265     TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
266                      "StackCopierSignal copy stack");
267     if (!finished_waiting) {
268       NOTREACHED();
269       return false;
270     }
271     // Ideally, an accurate timestamp is captured while the sampled thread is
272     // paused. In rare cases, this may fail, in which case we resort to
273     // capturing an delayed timestamp here instead.
274     if (maybe_timestamp.has_value())
275       *timestamp = maybe_timestamp.value();
276     else {
277       TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
278                    "Fallback on TimeTicks::Now()");
279       *timestamp = TimeTicks::Now();
280     }
281   }
282 
283   const uintptr_t bottom = RegisterContextStackPointer(params.context);
284   for (uintptr_t* reg :
285        thread_delegate_->GetRegistersToRewrite(thread_context)) {
286     *reg = StackCopierSignal::RewritePointerIfInOriginalStack(
287         reinterpret_cast<uint8_t*>(bottom),
288         reinterpret_cast<uintptr_t*>(stack_base_address), stack_copy_bottom,
289         *reg);
290   }
291 
292   *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) +
293                (stack_base_address - bottom);
294 
295   return copied;
296 }
297 
298 }  // namespace base
299