blob: 8268ff549e7b6945274661a62601af3d9eb2b189 [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
29#include "rtc_base/critical_section.h"
30#include "rtc_base/logging.h"
31#include "rtc_base/strings/string_builder.h"
32
33namespace webrtc {
34
35namespace {
36
37// Maximum stack trace depth we allow before aborting.
38constexpr size_t kMaxStackSize = 100;
39// Signal that will be used to interrupt threads. SIGURG ("Urgent condition on
40// socket") is chosen because Android does not set up a specific handler for
41// this signal.
42constexpr int kSignal = SIGURG;
43
44// Note: This class is only meant for use within this file, and for the
45// simplified use case of a single Wait() and a single Signal(), followed by
46// discarding the object (never reused).
47// This is a replacement of rtc::Event that is async-safe and doesn't use
48// pthread api. This is necessary since signal handlers cannot allocate memory
49// or use pthread api. This class is ported from Chromium.
50class AsyncSafeWaitableEvent {
51 public:
52 AsyncSafeWaitableEvent() {
53 std::atomic_store_explicit(&futex_, 0, std::memory_order_release);
54 }
55
56 ~AsyncSafeWaitableEvent() {}
57
58 // Returns false in the event of an error and errno is set to indicate the
59 // cause of the error.
60 bool Wait() {
61 // futex() can wake up spuriously if this memory address was previously used
62 // for a pthread mutex. So, also check the condition.
63 while (true) {
64 int res = syscall(SYS_futex, &futex_, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0,
65 nullptr, nullptr, 0);
66 if (std::atomic_load_explicit(&futex_, std::memory_order_acquire) != 0)
67 return true;
68 if (res != 0)
69 return false;
70 }
71 }
72
73 void Signal() {
74 std::atomic_store_explicit(&futex_, 1, std::memory_order_release);
75 syscall(SYS_futex, &futex_, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, nullptr,
76 nullptr, 0);
77 }
78
79 private:
80 std::atomic<int> futex_;
81};
82
83// Struct to store the arguments to the signal handler.
84struct SignalHandlerOutputState {
85 // This event is signalled when signal handler is done executing.
86 AsyncSafeWaitableEvent signal_handler_finish_event;
87 // Running counter of array index below.
88 size_t stack_size_counter = 0;
89 // Array storing the stack trace.
90 uintptr_t addresses[kMaxStackSize];
91};
92
93// Global lock to ensure only one thread gets interrupted at a time.
94rtc::GlobalLockPod g_signal_handler_lock;
95// Argument passed to the ThreadSignalHandler() from the sampling thread to the
96// sampled (stopped) thread. This value is set just before sending signal to the
97// thread and reset when handler is done.
98SignalHandlerOutputState* volatile g_signal_handler_output_state;
99
100// This function is called iteratively for each stack trace element and stores
101// the element in the array from |unwind_output_state|.
102_Unwind_Reason_Code UnwindBacktrace(struct _Unwind_Context* unwind_context,
103 void* unwind_output_state) {
104 SignalHandlerOutputState* const output_state =
105 static_cast<SignalHandlerOutputState*>(unwind_output_state);
106
107 // Avoid overflowing the stack trace array.
108 if (output_state->stack_size_counter >= kMaxStackSize)
109 return _URC_END_OF_STACK;
110
111 // Store the instruction pointer in the array. Subtract 2 since we want to get
112 // the call instruction pointer, not the return address which is the
113 // instruction after.
114 output_state->addresses[output_state->stack_size_counter] =
115 _Unwind_GetIP(unwind_context) - 2;
116 ++output_state->stack_size_counter;
117
118 return _URC_NO_REASON;
119}
120
121// This signal handler is exectued on the interrupted thread.
122void SignalHandler(int signum, siginfo_t* info, void* ptr) {
123 _Unwind_Backtrace(&UnwindBacktrace, g_signal_handler_output_state);
124 g_signal_handler_output_state->signal_handler_finish_event.Signal();
125}
126
127// Temporarily change the signal handler to a function that records a raw stack
128// trace and interrupt the given tid. This function will block until the output
129// thread stack trace has been stored in |params|. The return value is an error
130// string on failure and null on success.
131const char* CaptureRawStacktrace(int pid,
132 int tid,
133 SignalHandlerOutputState* params) {
134 // This function is under a global lock since we are changing the signal
135 // handler and using global state for the output. The lock is to ensure only
136 // one thread at a time gets captured. The lock also means we need to be very
137 // careful with what statements we put in this function, and we should even
138 // avoid logging here.
139 struct sigaction act;
140 struct sigaction old_act;
141 memset(&act, 0, sizeof(act));
142 act.sa_sigaction = &SignalHandler;
143 act.sa_flags = SA_RESTART | SA_SIGINFO;
144 sigemptyset(&act.sa_mask);
145
146 rtc::GlobalLockScope ls(&g_signal_handler_lock);
147 g_signal_handler_output_state = params;
148
149 if (sigaction(kSignal, &act, &old_act) != 0)
150 return "Failed to change signal action";
151
152 // Interrupt the thread which will execute SignalHandler() on the given
153 // thread.
154 if (tgkill(pid, tid, kSignal) != 0)
155 return "Failed to interrupt thread";
156
157 // Wait until the thread is done recording its stack trace.
158 if (!params->signal_handler_finish_event.Wait())
159 return "Failed to wait for thread to finish stack trace";
160
161 // Restore previous signal handler.
162 sigaction(kSignal, &old_act, /* old_act= */ nullptr);
163
164 return nullptr;
165}
166
167} // namespace
168
169std::vector<StackTraceElement> GetStackTrace(int tid) {
170 // Only a thread itself can unwind its stack, so we will interrupt the given
171 // tid with a custom signal handler in order to unwind its stack. The stack
172 // will be recorded to |params| through the use of the global pointer
173 // |g_signal_handler_param|.
174 SignalHandlerOutputState params;
175
176 const char* error_string = CaptureRawStacktrace(getpid(), tid, &params);
177 if (error_string != nullptr) {
178 RTC_LOG(LS_ERROR) << error_string << ". tid: " << tid
179 << ". errno: " << errno;
180 return {};
181 }
182
183 if (params.stack_size_counter >= kMaxStackSize)
184 RTC_LOG(LS_WARNING) << "Stack trace for thread " << tid << " was truncated";
185
186 // Translate addresses into symbolic information using dladdr().
187 std::vector<StackTraceElement> stack_trace;
188 for (size_t i = 0; i < params.stack_size_counter; ++i) {
189 const uintptr_t address = params.addresses[i];
190
191 Dl_info dl_info = {};
192 if (!dladdr(reinterpret_cast<void*>(address), &dl_info)) {
193 RTC_LOG(LS_WARNING)
194 << "Could not translate address to symbolic information for address "
195 << address << " at stack depth " << i;
196 continue;
197 }
198
199 StackTraceElement stack_trace_element;
200 stack_trace_element.shared_object_path = dl_info.dli_fname;
201 stack_trace_element.relative_address = static_cast<uint32_t>(
202 address - reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
203 stack_trace_element.symbol_name = dl_info.dli_sname;
204
205 stack_trace.push_back(stack_trace_element);
206 }
207
208 return stack_trace;
209}
210
211std::string StackTraceToString(
212 const std::vector<StackTraceElement>& stack_trace) {
213 rtc::StringBuilder string_builder;
214
215 for (size_t i = 0; i < stack_trace.size(); ++i) {
216 const StackTraceElement& stack_trace_element = stack_trace[i];
217 string_builder.AppendFormat(
218 "#%02zu pc %08x %s", i,
219 static_cast<uint32_t>(stack_trace_element.relative_address),
220 stack_trace_element.shared_object_path);
221 // The symbol name is only available for unstripped .so files.
222 if (stack_trace_element.symbol_name != nullptr)
223 string_builder.AppendFormat(" %s", stack_trace_element.symbol_name);
224
225 string_builder.AppendFormat("\n");
226 }
227
228 return string_builder.Release();
229}
230
231} // namespace webrtc