1 // Copyright 2019 The Chromium Authors. All rights reserved.
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 #if defined(OS_LINUX)
8 #include <linux/futex.h>
9 #include <syscall.h>
10 #elif defined(OS_FREEBSD)
11 #include <sys/types.h>
12 #include <sys/thr.h>
13 #include <sys/umtx.h>
14 #endif
15 #include <signal.h>
16 #include <sys/ucontext.h>
17 #include <syscall.h>
18
19 #include <atomic>
20
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/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() { futex_.store(0, std::memory_order_release); }
~AsyncSafeWaitableEvent()37 ~AsyncSafeWaitableEvent() {}
38
Wait()39 bool Wait() {
40 // futex() can wake up spuriously if this memory address was previously used
41 // for a pthread mutex. So, also check the condition.
42 while (true) {
43 int res =
44 #if defined(OS_LINUX)
45 syscall(SYS_futex, futex_int_ptr(), FUTEX_WAIT | FUTEX_PRIVATE_FLAG,
46 0, nullptr, nullptr, 0);
47 #elif defined(OS_FREEBSD)
48 _umtx_op(futex_int_ptr(), UMTX_OP_WAIT_UINT_PRIVATE, 0, nullptr,
49 nullptr);
50 #endif
51 if (futex_.load(std::memory_order_acquire) != 0)
52 return true;
53 if (res != 0)
54 return false;
55 }
56 }
57
Signal()58 void Signal() {
59 futex_.store(1, std::memory_order_release);
60 #if defined(OS_LINUX)
61 syscall(SYS_futex, futex_int_ptr(), FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1,
62 nullptr, nullptr, 0);
63 #elif defined(OS_FREEBSD)
64 _umtx_op(futex_int_ptr(), UMTX_OP_WAKE_PRIVATE, 1, nullptr, nullptr);
65 #endif
66 }
67
68 private:
69 // Provides a pointer to the atomic's storage. std::atomic_int has standard
70 // layout so its address can be used for the pointer as long as it only
71 // contains the int.
futex_int_ptr()72 int* futex_int_ptr() {
73 static_assert(sizeof(futex_) == sizeof(int),
74 "Expected std::atomic_int to be the same size as int");
75 return reinterpret_cast<int*>(&futex_);
76 }
77
78 std::atomic_int futex_{0};
79 };
80
81 // Scoped signal event that calls Signal on the AsyncSafeWaitableEvent at
82 // destructor.
83 class ScopedEventSignaller {
84 public:
ScopedEventSignaller(AsyncSafeWaitableEvent * event)85 ScopedEventSignaller(AsyncSafeWaitableEvent* event) : event_(event) {}
~ScopedEventSignaller()86 ~ScopedEventSignaller() { event_->Signal(); }
87
88 private:
89 AsyncSafeWaitableEvent* event_;
90 };
91
92 // Struct to store the arguments to the signal handler.
93 struct HandlerParams {
94 uintptr_t stack_base_address;
95
96 // The event is signalled when signal handler is done executing.
97 AsyncSafeWaitableEvent* event;
98
99 // Return values:
100
101 // Successfully copied the stack segment.
102 bool* success;
103
104 // The thread context of the leaf function.
105 mcontext_t* context;
106
107 // Buffer to copy the stack segment.
108 StackBuffer* stack_buffer;
109 const uint8_t** stack_copy_bottom;
110
111 // The timestamp when the stack was copied.
112 TimeTicks* timestamp;
113
114 // The delegate provided to the StackCopier.
115 StackCopier::Delegate* stack_copier_delegate;
116 };
117
118 // Pointer to the parameters to be "passed" to the CopyStackSignalHandler() from
119 // the sampling thread to the sampled (stopped) thread. This value is set just
120 // before sending the signal to the thread and reset when the handler is done.
121 std::atomic<HandlerParams*> g_handler_params;
122
123 // CopyStackSignalHandler is invoked on the stopped thread and records the
124 // thread's stack and register context at the time the signal was received. This
125 // function may only call reentrant code.
CopyStackSignalHandler(int n,siginfo_t * siginfo,void * sigcontext)126 void CopyStackSignalHandler(int n, siginfo_t* siginfo, void* sigcontext) {
127 HandlerParams* params = g_handler_params.load(std::memory_order_acquire);
128
129 // TimeTicks::Now() is implemented in terms of clock_gettime on Linux, which
130 // is signal safe per the signal-safety(7) man page.
131 *params->timestamp = TimeTicks::Now();
132
133 ScopedEventSignaller e(params->event);
134 *params->success = false;
135
136 const ucontext_t* ucontext = static_cast<ucontext_t*>(sigcontext);
137 memcpy(params->context, &ucontext->uc_mcontext, sizeof(mcontext_t));
138
139 const uintptr_t bottom = RegisterContextStackPointer(params->context);
140 const uintptr_t top = params->stack_base_address;
141 if ((top - bottom) > params->stack_buffer->size()) {
142 // The stack exceeds the size of the allocated buffer. The buffer is sized
143 // such that this shouldn't happen under typical execution so we can safely
144 // punt in this situation.
145 return;
146 }
147
148 params->stack_copier_delegate->OnStackCopy();
149
150 *params->stack_copy_bottom =
151 StackCopierSignal::CopyStackContentsAndRewritePointers(
152 reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
153 StackBuffer::kPlatformStackAlignment, params->stack_buffer->buffer());
154
155 *params->success = true;
156 }
157
158 // Sets the global handler params for the signal handler function.
159 class ScopedSetSignalHandlerParams {
160 public:
ScopedSetSignalHandlerParams(HandlerParams * params)161 ScopedSetSignalHandlerParams(HandlerParams* params) {
162 g_handler_params.store(params, std::memory_order_release);
163 }
164
~ScopedSetSignalHandlerParams()165 ~ScopedSetSignalHandlerParams() {
166 g_handler_params.store(nullptr, std::memory_order_release);
167 }
168 };
169
170 class ScopedSigaction {
171 public:
ScopedSigaction(int signal,struct sigaction * action,struct sigaction * original_action)172 ScopedSigaction(int signal,
173 struct sigaction* action,
174 struct sigaction* original_action)
175 : signal_(signal),
176 action_(action),
177 original_action_(original_action),
178 succeeded_(sigaction(signal, action, original_action) == 0) {}
179
succeeded() const180 bool succeeded() const { return succeeded_; }
181
~ScopedSigaction()182 ~ScopedSigaction() {
183 if (!succeeded_)
184 return;
185
186 bool reset_succeeded = sigaction(signal_, original_action_, action_) == 0;
187 DCHECK(reset_succeeded);
188 }
189
190 private:
191 const int signal_;
192 struct sigaction* const action_;
193 struct sigaction* const original_action_;
194 const bool succeeded_;
195 };
196
197 } // namespace
198
StackCopierSignal(std::unique_ptr<ThreadDelegate> thread_delegate)199 StackCopierSignal::StackCopierSignal(
200 std::unique_ptr<ThreadDelegate> thread_delegate)
201 : thread_delegate_(std::move(thread_delegate)) {}
202
203 StackCopierSignal::~StackCopierSignal() = default;
204
CopyStack(StackBuffer * stack_buffer,uintptr_t * stack_top,TimeTicks * timestamp,RegisterContext * thread_context,Delegate * delegate)205 bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
206 uintptr_t* stack_top,
207 TimeTicks* timestamp,
208 RegisterContext* thread_context,
209 Delegate* delegate) {
210 AsyncSafeWaitableEvent wait_event;
211 bool copied = false;
212 const uint8_t* stack_copy_bottom = nullptr;
213 const uintptr_t stack_base_address = thread_delegate_->GetStackBaseAddress();
214 HandlerParams params = {stack_base_address, &wait_event, &copied,
215 thread_context, stack_buffer, &stack_copy_bottom,
216 timestamp, delegate};
217 {
218 ScopedSetSignalHandlerParams scoped_handler_params(¶ms);
219
220 // Set the signal handler for the thread to the stack copy function.
221 struct sigaction action;
222 struct sigaction original_action;
223 memset(&action, 0, sizeof(action));
224 action.sa_sigaction = CopyStackSignalHandler;
225 action.sa_flags = SA_RESTART | SA_SIGINFO;
226 sigemptyset(&action.sa_mask);
227 TRACE_EVENT_BEGIN0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
228 "StackCopierSignal copy stack");
229 // SIGURG is chosen here because we observe no crashes with this signal and
230 // neither Chrome or the AOSP sets up a special handler for this signal.
231 ScopedSigaction scoped_sigaction(SIGURG, &action, &original_action);
232 if (!scoped_sigaction.succeeded())
233 return false;
234
235 #if defined(OS_LINUX)
236 if (syscall(SYS_tgkill, getpid(), thread_delegate_->GetThreadId(),
237 SIGURG) != 0) {
238 NOTREACHED();
239 return false;
240 }
241 #elif defined(OS_FREEBSD)
242 if (thr_kill2(getpid(), thread_delegate_->GetThreadId(), SIGURG) != 0) {
243 NOTREACHED();
244 return false;
245 }
246 #endif
247 bool finished_waiting = wait_event.Wait();
248 TRACE_EVENT_END0(TRACE_DISABLED_BY_DEFAULT("cpu_profiler.debug"),
249 "StackCopierSignal copy stack");
250 if (!finished_waiting) {
251 NOTREACHED();
252 return false;
253 }
254 }
255
256 const uintptr_t bottom = RegisterContextStackPointer(params.context);
257 for (uintptr_t* reg :
258 thread_delegate_->GetRegistersToRewrite(thread_context)) {
259 *reg = StackCopierSignal::RewritePointerIfInOriginalStack(
260 reinterpret_cast<uint8_t*>(bottom),
261 reinterpret_cast<uintptr_t*>(stack_base_address), stack_copy_bottom,
262 *reg);
263 }
264
265 *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) +
266 (stack_base_address - bottom);
267
268 return copied;
269 }
270
271 } // namespace base
272