blob: 48894374acd9728cf9f3337bf7127e15432af058 [file] [log] [blame]
Magnus Jedvert7510e4a2019-01-20 11:46:32 +01001/*
2 * Copyright 2019 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "sdk/android/native_api/stacktrace/stacktrace.h"
12
13#include <dlfcn.h>
14#include <errno.h>
15#include <linux/futex.h>
16#include <sys/ptrace.h>
17#include <sys/ucontext.h>
18#include <syscall.h>
19#include <ucontext.h>
20#include <unistd.h>
21#include <unwind.h>
22#include <atomic>
23
24// ptrace.h is polluting the namespace. Clean up to avoid conflicts with rtc.
25#if defined(DS)
26#undef DS
27#endif
28
Danil Chapovalov5740f3e2019-10-10 11:12:15 +020029#include "absl/base/attributes.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010030#include "rtc_base/logging.h"
31#include "rtc_base/strings/string_builder.h"
Markus Handell0dd35d32020-07-15 11:53:44 +020032#include "rtc_base/synchronization/mutex.h"
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010033
34namespace webrtc {
35
36namespace {
37
38// Maximum stack trace depth we allow before aborting.
39constexpr size_t kMaxStackSize = 100;
40// Signal that will be used to interrupt threads. SIGURG ("Urgent condition on
41// socket") is chosen because Android does not set up a specific handler for
42// this signal.
43constexpr int kSignal = SIGURG;
44
45// Note: This class is only meant for use within this file, and for the
46// simplified use case of a single Wait() and a single Signal(), followed by
47// discarding the object (never reused).
48// This is a replacement of rtc::Event that is async-safe and doesn't use
49// pthread api. This is necessary since signal handlers cannot allocate memory
50// or use pthread api. This class is ported from Chromium.
51class AsyncSafeWaitableEvent {
52 public:
53 AsyncSafeWaitableEvent() {
54 std::atomic_store_explicit(&futex_, 0, std::memory_order_release);
55 }
56
57 ~AsyncSafeWaitableEvent() {}
58
59 // Returns false in the event of an error and errno is set to indicate the
60 // cause of the error.
61 bool Wait() {
62 // futex() can wake up spuriously if this memory address was previously used
63 // for a pthread mutex. So, also check the condition.
64 while (true) {
65 int res = syscall(SYS_futex, &futex_, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0,
66 nullptr, nullptr, 0);
67 if (std::atomic_load_explicit(&futex_, std::memory_order_acquire) != 0)
68 return true;
69 if (res != 0)
70 return false;
71 }
72 }
73
74 void Signal() {
75 std::atomic_store_explicit(&futex_, 1, std::memory_order_release);
76 syscall(SYS_futex, &futex_, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, nullptr,
77 nullptr, 0);
78 }
79
80 private:
81 std::atomic<int> futex_;
82};
83
84// Struct to store the arguments to the signal handler.
85struct SignalHandlerOutputState {
86 // This event is signalled when signal handler is done executing.
87 AsyncSafeWaitableEvent signal_handler_finish_event;
88 // Running counter of array index below.
89 size_t stack_size_counter = 0;
90 // Array storing the stack trace.
91 uintptr_t addresses[kMaxStackSize];
92};
93
94// Global lock to ensure only one thread gets interrupted at a time.
Markus Handell0dd35d32020-07-15 11:53:44 +020095ABSL_CONST_INIT GlobalMutex g_signal_handler_lock(absl::kConstInit);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +010096// Argument passed to the ThreadSignalHandler() from the sampling thread to the
97// sampled (stopped) thread. This value is set just before sending signal to the
98// thread and reset when handler is done.
99SignalHandlerOutputState* volatile g_signal_handler_output_state;
100
101// This function is called iteratively for each stack trace element and stores
Artem Titovd7ac5812021-07-27 12:23:39 +0200102// the element in the array from `unwind_output_state`.
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100103_Unwind_Reason_Code UnwindBacktrace(struct _Unwind_Context* unwind_context,
104 void* unwind_output_state) {
105 SignalHandlerOutputState* const output_state =
106 static_cast<SignalHandlerOutputState*>(unwind_output_state);
107
Magnus Jedvert3d687a12020-04-15 15:16:19 +0200108 // Abort if output state is corrupt.
109 if (output_state == nullptr)
110 return _URC_END_OF_STACK;
111
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100112 // Avoid overflowing the stack trace array.
113 if (output_state->stack_size_counter >= kMaxStackSize)
114 return _URC_END_OF_STACK;
115
116 // Store the instruction pointer in the array. Subtract 2 since we want to get
117 // the call instruction pointer, not the return address which is the
118 // instruction after.
119 output_state->addresses[output_state->stack_size_counter] =
120 _Unwind_GetIP(unwind_context) - 2;
121 ++output_state->stack_size_counter;
122
123 return _URC_NO_REASON;
124}
125
126// This signal handler is exectued on the interrupted thread.
127void SignalHandler(int signum, siginfo_t* info, void* ptr) {
Magnus Jedvert3d687a12020-04-15 15:16:19 +0200128 // This should have been set by the thread requesting the stack trace.
129 SignalHandlerOutputState* signal_handler_output_state =
130 g_signal_handler_output_state;
131 if (signal_handler_output_state != nullptr) {
132 _Unwind_Backtrace(&UnwindBacktrace, signal_handler_output_state);
133 signal_handler_output_state->signal_handler_finish_event.Signal();
134 }
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100135}
136
137// Temporarily change the signal handler to a function that records a raw stack
138// trace and interrupt the given tid. This function will block until the output
Artem Titovd7ac5812021-07-27 12:23:39 +0200139// thread stack trace has been stored in `params`. The return value is an error
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100140// string on failure and null on success.
141const char* CaptureRawStacktrace(int pid,
142 int tid,
143 SignalHandlerOutputState* params) {
144 // This function is under a global lock since we are changing the signal
145 // handler and using global state for the output. The lock is to ensure only
146 // one thread at a time gets captured. The lock also means we need to be very
147 // careful with what statements we put in this function, and we should even
148 // avoid logging here.
149 struct sigaction act;
150 struct sigaction old_act;
151 memset(&act, 0, sizeof(act));
152 act.sa_sigaction = &SignalHandler;
153 act.sa_flags = SA_RESTART | SA_SIGINFO;
154 sigemptyset(&act.sa_mask);
155
Markus Handell0dd35d32020-07-15 11:53:44 +0200156 GlobalMutexLock ls(&g_signal_handler_lock);
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100157 g_signal_handler_output_state = params;
158
159 if (sigaction(kSignal, &act, &old_act) != 0)
160 return "Failed to change signal action";
161
162 // Interrupt the thread which will execute SignalHandler() on the given
163 // thread.
164 if (tgkill(pid, tid, kSignal) != 0)
165 return "Failed to interrupt thread";
166
167 // Wait until the thread is done recording its stack trace.
168 if (!params->signal_handler_finish_event.Wait())
169 return "Failed to wait for thread to finish stack trace";
170
171 // Restore previous signal handler.
172 sigaction(kSignal, &old_act, /* old_act= */ nullptr);
173
174 return nullptr;
175}
176
Karl Wibergc130d422019-02-28 17:06:54 +0100177// Translate addresses into symbolic information using dladdr().
178std::vector<StackTraceElement> FormatStackTrace(
179 const SignalHandlerOutputState& params) {
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100180 std::vector<StackTraceElement> stack_trace;
181 for (size_t i = 0; i < params.stack_size_counter; ++i) {
182 const uintptr_t address = params.addresses[i];
183
184 Dl_info dl_info = {};
185 if (!dladdr(reinterpret_cast<void*>(address), &dl_info)) {
186 RTC_LOG(LS_WARNING)
187 << "Could not translate address to symbolic information for address "
188 << address << " at stack depth " << i;
189 continue;
190 }
191
192 StackTraceElement stack_trace_element;
193 stack_trace_element.shared_object_path = dl_info.dli_fname;
194 stack_trace_element.relative_address = static_cast<uint32_t>(
195 address - reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
196 stack_trace_element.symbol_name = dl_info.dli_sname;
197
198 stack_trace.push_back(stack_trace_element);
199 }
200
201 return stack_trace;
202}
203
Karl Wibergc130d422019-02-28 17:06:54 +0100204} // namespace
205
206std::vector<StackTraceElement> GetStackTrace(int tid) {
207 // Only a thread itself can unwind its stack, so we will interrupt the given
208 // tid with a custom signal handler in order to unwind its stack. The stack
Artem Titovd7ac5812021-07-27 12:23:39 +0200209 // will be recorded to `params` through the use of the global pointer
210 // `g_signal_handler_param`.
Karl Wibergc130d422019-02-28 17:06:54 +0100211 SignalHandlerOutputState params;
212
213 const char* error_string = CaptureRawStacktrace(getpid(), tid, &params);
214 if (error_string != nullptr) {
215 RTC_LOG(LS_ERROR) << error_string << ". tid: " << tid
216 << ". errno: " << errno;
217 return {};
218 }
219 if (params.stack_size_counter >= kMaxStackSize) {
220 RTC_LOG(LS_WARNING) << "Stack trace for thread " << tid << " was truncated";
221 }
222 return FormatStackTrace(params);
223}
224
225std::vector<StackTraceElement> GetStackTrace() {
226 SignalHandlerOutputState params;
227 _Unwind_Backtrace(&UnwindBacktrace, &params);
228 if (params.stack_size_counter >= kMaxStackSize) {
229 RTC_LOG(LS_WARNING) << "Stack trace was truncated";
230 }
231 return FormatStackTrace(params);
232}
233
Magnus Jedvert7510e4a2019-01-20 11:46:32 +0100234std::string StackTraceToString(
235 const std::vector<StackTraceElement>& stack_trace) {
236 rtc::StringBuilder string_builder;
237
238 for (size_t i = 0; i < stack_trace.size(); ++i) {
239 const StackTraceElement& stack_trace_element = stack_trace[i];
240 string_builder.AppendFormat(
241 "#%02zu pc %08x %s", i,
242 static_cast<uint32_t>(stack_trace_element.relative_address),
243 stack_trace_element.shared_object_path);
244 // The symbol name is only available for unstripped .so files.
245 if (stack_trace_element.symbol_name != nullptr)
246 string_builder.AppendFormat(" %s", stack_trace_element.symbol_name);
247
248 string_builder.AppendFormat("\n");
249 }
250
251 return string_builder.Release();
252}
253
254} // namespace webrtc