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(¶ms);
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