• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "absl/base/attributes.h"
30 #include "rtc_base/logging.h"
31 #include "rtc_base/strings/string_builder.h"
32 #include "rtc_base/synchronization/mutex.h"
33 
34 namespace webrtc {
35 
36 namespace {
37 
38 // Maximum stack trace depth we allow before aborting.
39 constexpr 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.
43 constexpr 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.
51 class AsyncSafeWaitableEvent {
52  public:
AsyncSafeWaitableEvent()53   AsyncSafeWaitableEvent() {
54     std::atomic_store_explicit(&futex_, 0, std::memory_order_release);
55   }
56 
~AsyncSafeWaitableEvent()57   ~AsyncSafeWaitableEvent() {}
58 
59   // Returns false in the event of an error and errno is set to indicate the
60   // cause of the error.
Wait()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 
Signal()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.
85 struct SignalHandlerOutputState {
86   // This function is called iteratively for each stack trace element and stores
87   // the element in the array from `unwind_output_state`.
88   static _Unwind_Reason_Code UnwindBacktrace(
89       struct _Unwind_Context* unwind_context,
90       void* unwind_output_state);
91 
92   // This event is signalled when signal handler is done executing.
93   AsyncSafeWaitableEvent signal_handler_finish_event;
94   // Running counter of array index below.
95   size_t stack_size_counter = 0;
96   // Array storing the stack trace.
97   uintptr_t addresses[kMaxStackSize];
98 };
99 
100 // This function is called iteratively for each stack trace element and stores
101 // the element in the array from `unwind_output_state`.
UnwindBacktrace(struct _Unwind_Context * unwind_context,void * unwind_output_state)102 _Unwind_Reason_Code SignalHandlerOutputState::UnwindBacktrace(
103     struct _Unwind_Context* unwind_context,
104     void* unwind_output_state) {
105   SignalHandlerOutputState* const output_state =
106       static_cast<SignalHandlerOutputState*>(unwind_output_state);
107 
108   // Abort if output state is corrupt.
109   if (output_state == nullptr)
110     return _URC_END_OF_STACK;
111 
112   // 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 class GlobalStackUnwinder {
127  public:
Get()128   static GlobalStackUnwinder& Get() {
129     static GlobalStackUnwinder* const instance = new GlobalStackUnwinder();
130     return *instance;
131   }
132   const char* CaptureRawStacktrace(int pid,
133                                    int tid,
134                                    SignalHandlerOutputState* params);
135 
136  private:
GlobalStackUnwinder()137   GlobalStackUnwinder() { current_output_state_.store(nullptr); }
138 
139   // Temporarily installed signal handler.
140   static void SignalHandler(int signum, siginfo_t* info, void* ptr);
141 
142   Mutex mutex_;
143 
144   // Accessed by signal handler.
145   static std::atomic<SignalHandlerOutputState*> current_output_state_;
146   // A signal handler mustn't use locks.
147   static_assert(std::atomic<SignalHandlerOutputState*>::is_always_lock_free);
148 };
149 
150 std::atomic<SignalHandlerOutputState*>
151     GlobalStackUnwinder::current_output_state_;
152 
153 // This signal handler is exectued on the interrupted thread.
SignalHandler(int signum,siginfo_t * info,void * ptr)154 void GlobalStackUnwinder::SignalHandler(int signum,
155                                         siginfo_t* info,
156                                         void* ptr) {
157   // This should have been set by the thread requesting the stack trace.
158   SignalHandlerOutputState* signal_handler_output_state =
159       current_output_state_.load();
160   if (signal_handler_output_state != nullptr) {
161     _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace,
162                       signal_handler_output_state);
163     signal_handler_output_state->signal_handler_finish_event.Signal();
164   }
165 }
166 
167 // Temporarily change the signal handler to a function that records a raw stack
168 // trace and interrupt the given tid. This function will block until the output
169 // thread stack trace has been stored in `params`. The return value is an error
170 // string on failure and null on success.
CaptureRawStacktrace(int pid,int tid,SignalHandlerOutputState * params)171 const char* GlobalStackUnwinder::CaptureRawStacktrace(
172     int pid,
173     int tid,
174     SignalHandlerOutputState* params) {
175   // This function is under a global lock since we are changing the signal
176   // handler and using global state for the output. The lock is to ensure only
177   // one thread at a time gets captured. The lock also means we need to be very
178   // careful with what statements we put in this function, and we should even
179   // avoid logging here.
180   struct sigaction act;
181   struct sigaction old_act;
182   memset(&act, 0, sizeof(act));
183   act.sa_sigaction = &SignalHandler;
184   act.sa_flags = SA_RESTART | SA_SIGINFO;
185   sigemptyset(&act.sa_mask);
186 
187   MutexLock loch(&mutex_);
188   current_output_state_.store(params);
189 
190   if (sigaction(kSignal, &act, &old_act) != 0)
191     return "Failed to change signal action";
192 
193   // Interrupt the thread which will execute SignalHandler() on the given
194   // thread.
195   if (tgkill(pid, tid, kSignal) != 0)
196     return "Failed to interrupt thread";
197 
198   // Wait until the thread is done recording its stack trace.
199   if (!params->signal_handler_finish_event.Wait())
200     return "Failed to wait for thread to finish stack trace";
201 
202   // Restore previous signal handler.
203   sigaction(kSignal, &old_act, /* old_act= */ nullptr);
204 
205   return nullptr;
206 }
207 
208 // Translate addresses into symbolic information using dladdr().
FormatStackTrace(const SignalHandlerOutputState & params)209 std::vector<StackTraceElement> FormatStackTrace(
210     const SignalHandlerOutputState& params) {
211   std::vector<StackTraceElement> stack_trace;
212   for (size_t i = 0; i < params.stack_size_counter; ++i) {
213     const uintptr_t address = params.addresses[i];
214 
215     Dl_info dl_info = {};
216     if (!dladdr(reinterpret_cast<void*>(address), &dl_info)) {
217       RTC_LOG(LS_WARNING)
218           << "Could not translate address to symbolic information for address "
219           << address << " at stack depth " << i;
220       continue;
221     }
222 
223     StackTraceElement stack_trace_element;
224     stack_trace_element.shared_object_path = dl_info.dli_fname;
225     stack_trace_element.relative_address = static_cast<uint32_t>(
226         address - reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
227     stack_trace_element.symbol_name = dl_info.dli_sname;
228 
229     stack_trace.push_back(stack_trace_element);
230   }
231 
232   return stack_trace;
233 }
234 
235 }  // namespace
236 
GetStackTrace(int tid)237 std::vector<StackTraceElement> GetStackTrace(int tid) {
238   // Only a thread itself can unwind its stack, so we will interrupt the given
239   // tid with a custom signal handler in order to unwind its stack. The stack
240   // will be recorded to `params` through the use of the global pointer
241   // `g_signal_handler_param`.
242   SignalHandlerOutputState params;
243 
244   const char* error_string =
245       GlobalStackUnwinder::Get().CaptureRawStacktrace(getpid(), tid, &params);
246   if (error_string != nullptr) {
247     RTC_LOG(LS_ERROR) << error_string << ". tid: " << tid
248                       << ". errno: " << errno;
249     return {};
250   }
251   if (params.stack_size_counter >= kMaxStackSize) {
252     RTC_LOG(LS_WARNING) << "Stack trace for thread " << tid << " was truncated";
253   }
254   return FormatStackTrace(params);
255 }
256 
GetStackTrace()257 std::vector<StackTraceElement> GetStackTrace() {
258   SignalHandlerOutputState params;
259   _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace, &params);
260   if (params.stack_size_counter >= kMaxStackSize) {
261     RTC_LOG(LS_WARNING) << "Stack trace was truncated";
262   }
263   return FormatStackTrace(params);
264 }
265 
StackTraceToString(const std::vector<StackTraceElement> & stack_trace)266 std::string StackTraceToString(
267     const std::vector<StackTraceElement>& stack_trace) {
268   rtc::StringBuilder string_builder;
269 
270   for (size_t i = 0; i < stack_trace.size(); ++i) {
271     const StackTraceElement& stack_trace_element = stack_trace[i];
272     string_builder.AppendFormat(
273         "#%02zu pc %08x %s", i,
274         static_cast<uint32_t>(stack_trace_element.relative_address),
275         stack_trace_element.shared_object_path);
276     // The symbol name is only available for unstripped .so files.
277     if (stack_trace_element.symbol_name != nullptr)
278       string_builder.AppendFormat(" %s", stack_trace_element.symbol_name);
279 
280     string_builder.AppendFormat("\n");
281   }
282 
283   return string_builder.Release();
284 }
285 
286 }  // namespace webrtc
287