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