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