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