Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 "rtc_base/critical_section.h" |
| 30 | #include "rtc_base/logging.h" |
| 31 | #include "rtc_base/strings/string_builder.h" |
| 32 | |
| 33 | namespace webrtc { |
| 34 | |
| 35 | namespace { |
| 36 | |
| 37 | // Maximum stack trace depth we allow before aborting. |
| 38 | constexpr 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. |
| 42 | constexpr 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. |
| 50 | class 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. |
| 84 | struct 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. |
| 94 | rtc::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. |
| 98 | SignalHandlerOutputState* 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. |
| 122 | void 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. |
| 131 | const 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 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 167 | // Translate addresses into symbolic information using dladdr(). |
| 168 | std::vector<StackTraceElement> FormatStackTrace( |
| 169 | const SignalHandlerOutputState& params) { |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 170 | std::vector<StackTraceElement> stack_trace; |
| 171 | for (size_t i = 0; i < params.stack_size_counter; ++i) { |
| 172 | const uintptr_t address = params.addresses[i]; |
| 173 | |
| 174 | Dl_info dl_info = {}; |
| 175 | if (!dladdr(reinterpret_cast<void*>(address), &dl_info)) { |
| 176 | RTC_LOG(LS_WARNING) |
| 177 | << "Could not translate address to symbolic information for address " |
| 178 | << address << " at stack depth " << i; |
| 179 | continue; |
| 180 | } |
| 181 | |
| 182 | StackTraceElement stack_trace_element; |
| 183 | stack_trace_element.shared_object_path = dl_info.dli_fname; |
| 184 | stack_trace_element.relative_address = static_cast<uint32_t>( |
| 185 | address - reinterpret_cast<uintptr_t>(dl_info.dli_fbase)); |
| 186 | stack_trace_element.symbol_name = dl_info.dli_sname; |
| 187 | |
| 188 | stack_trace.push_back(stack_trace_element); |
| 189 | } |
| 190 | |
| 191 | return stack_trace; |
| 192 | } |
| 193 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 194 | } // namespace |
| 195 | |
| 196 | std::vector<StackTraceElement> GetStackTrace(int tid) { |
| 197 | // Only a thread itself can unwind its stack, so we will interrupt the given |
| 198 | // tid with a custom signal handler in order to unwind its stack. The stack |
| 199 | // will be recorded to |params| through the use of the global pointer |
| 200 | // |g_signal_handler_param|. |
| 201 | SignalHandlerOutputState params; |
| 202 | |
| 203 | const char* error_string = CaptureRawStacktrace(getpid(), tid, ¶ms); |
| 204 | if (error_string != nullptr) { |
| 205 | RTC_LOG(LS_ERROR) << error_string << ". tid: " << tid |
| 206 | << ". errno: " << errno; |
| 207 | return {}; |
| 208 | } |
| 209 | if (params.stack_size_counter >= kMaxStackSize) { |
| 210 | RTC_LOG(LS_WARNING) << "Stack trace for thread " << tid << " was truncated"; |
| 211 | } |
| 212 | return FormatStackTrace(params); |
| 213 | } |
| 214 | |
| 215 | std::vector<StackTraceElement> GetStackTrace() { |
| 216 | SignalHandlerOutputState params; |
| 217 | _Unwind_Backtrace(&UnwindBacktrace, ¶ms); |
| 218 | if (params.stack_size_counter >= kMaxStackSize) { |
| 219 | RTC_LOG(LS_WARNING) << "Stack trace was truncated"; |
| 220 | } |
| 221 | return FormatStackTrace(params); |
| 222 | } |
| 223 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 224 | std::string StackTraceToString( |
| 225 | const std::vector<StackTraceElement>& stack_trace) { |
| 226 | rtc::StringBuilder string_builder; |
| 227 | |
| 228 | for (size_t i = 0; i < stack_trace.size(); ++i) { |
| 229 | const StackTraceElement& stack_trace_element = stack_trace[i]; |
| 230 | string_builder.AppendFormat( |
| 231 | "#%02zu pc %08x %s", i, |
| 232 | static_cast<uint32_t>(stack_trace_element.relative_address), |
| 233 | stack_trace_element.shared_object_path); |
| 234 | // The symbol name is only available for unstripped .so files. |
| 235 | if (stack_trace_element.symbol_name != nullptr) |
| 236 | string_builder.AppendFormat(" %s", stack_trace_element.symbol_name); |
| 237 | |
| 238 | string_builder.AppendFormat("\n"); |
| 239 | } |
| 240 | |
| 241 | return string_builder.Release(); |
| 242 | } |
| 243 | |
| 244 | } // namespace webrtc |