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 | |
Danil Chapovalov | 5740f3e | 2019-10-10 11:12:15 +0200 | [diff] [blame] | 29 | #include "absl/base/attributes.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 30 | #include "rtc_base/logging.h" |
| 31 | #include "rtc_base/strings/string_builder.h" |
Markus Handell | 0dd35d3 | 2020-07-15 11:53:44 +0200 | [diff] [blame] | 32 | #include "rtc_base/synchronization/mutex.h" |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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: |
| 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. |
| 85 | struct SignalHandlerOutputState { |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 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 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 100 | // This function is called iteratively for each stack trace element and stores |
Artem Titov | d7ac581 | 2021-07-27 12:23:39 +0200 | [diff] [blame] | 101 | // the element in the array from `unwind_output_state`. |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 102 | _Unwind_Reason_Code SignalHandlerOutputState::UnwindBacktrace( |
| 103 | struct _Unwind_Context* unwind_context, |
| 104 | void* unwind_output_state) { |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 105 | SignalHandlerOutputState* const output_state = |
| 106 | static_cast<SignalHandlerOutputState*>(unwind_output_state); |
| 107 | |
Magnus Jedvert | 3d687a1 | 2020-04-15 15:16:19 +0200 | [diff] [blame] | 108 | // Abort if output state is corrupt. |
| 109 | if (output_state == nullptr) |
| 110 | return _URC_END_OF_STACK; |
| 111 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 | |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 126 | class GlobalStackUnwinder { |
| 127 | public: |
| 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: |
| 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 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 153 | // This signal handler is exectued on the interrupted thread. |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 154 | void GlobalStackUnwinder::SignalHandler(int signum, |
| 155 | siginfo_t* info, |
| 156 | void* ptr) { |
Magnus Jedvert | 3d687a1 | 2020-04-15 15:16:19 +0200 | [diff] [blame] | 157 | // This should have been set by the thread requesting the stack trace. |
| 158 | SignalHandlerOutputState* signal_handler_output_state = |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 159 | current_output_state_.load(); |
Magnus Jedvert | 3d687a1 | 2020-04-15 15:16:19 +0200 | [diff] [blame] | 160 | if (signal_handler_output_state != nullptr) { |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 161 | _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace, |
| 162 | signal_handler_output_state); |
Magnus Jedvert | 3d687a1 | 2020-04-15 15:16:19 +0200 | [diff] [blame] | 163 | signal_handler_output_state->signal_handler_finish_event.Signal(); |
| 164 | } |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 |
Artem Titov | d7ac581 | 2021-07-27 12:23:39 +0200 | [diff] [blame] | 169 | // thread stack trace has been stored in `params`. The return value is an error |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 170 | // string on failure and null on success. |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 171 | const char* GlobalStackUnwinder::CaptureRawStacktrace( |
| 172 | int pid, |
| 173 | int tid, |
| 174 | SignalHandlerOutputState* params) { |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 | |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 187 | MutexLock loch(&mutex_); |
| 188 | current_output_state_.store(params); |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 208 | // Translate addresses into symbolic information using dladdr(). |
| 209 | std::vector<StackTraceElement> FormatStackTrace( |
| 210 | const SignalHandlerOutputState& params) { |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 | |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 235 | } // namespace |
| 236 | |
| 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 |
Artem Titov | d7ac581 | 2021-07-27 12:23:39 +0200 | [diff] [blame] | 240 | // will be recorded to `params` through the use of the global pointer |
| 241 | // `g_signal_handler_param`. |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 242 | SignalHandlerOutputState params; |
| 243 | |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 244 | const char* error_string = |
| 245 | GlobalStackUnwinder::Get().CaptureRawStacktrace(getpid(), tid, ¶ms); |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 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 | |
| 257 | std::vector<StackTraceElement> GetStackTrace() { |
| 258 | SignalHandlerOutputState params; |
Niels Möller | 00e9324 | 2022-04-05 17:12:48 +0200 | [diff] [blame^] | 259 | _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace, ¶ms); |
Karl Wiberg | c130d42 | 2019-02-28 17:06:54 +0100 | [diff] [blame] | 260 | if (params.stack_size_counter >= kMaxStackSize) { |
| 261 | RTC_LOG(LS_WARNING) << "Stack trace was truncated"; |
| 262 | } |
| 263 | return FormatStackTrace(params); |
| 264 | } |
| 265 | |
Magnus Jedvert | 7510e4a | 2019-01-20 11:46:32 +0100 | [diff] [blame] | 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 |