summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/sdk/android/native_api/stacktrace
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/sdk/android/native_api/stacktrace
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/sdk/android/native_api/stacktrace')
-rw-r--r--third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.cc286
-rw-r--r--third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.h45
2 files changed, 331 insertions, 0 deletions
diff --git a/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.cc b/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.cc
new file mode 100644
index 0000000000..96e03e0af1
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.cc
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "sdk/android/native_api/stacktrace/stacktrace.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <linux/futex.h>
+#include <sys/ptrace.h>
+#include <sys/ucontext.h>
+#include <syscall.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <unwind.h>
+#include <atomic>
+
+// ptrace.h is polluting the namespace. Clean up to avoid conflicts with rtc.
+#if defined(DS)
+#undef DS
+#endif
+
+#include "absl/base/attributes.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/strings/string_builder.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace webrtc {
+
+namespace {
+
+// Maximum stack trace depth we allow before aborting.
+constexpr size_t kMaxStackSize = 100;
+// Signal that will be used to interrupt threads. SIGURG ("Urgent condition on
+// socket") is chosen because Android does not set up a specific handler for
+// this signal.
+constexpr int kSignal = SIGURG;
+
+// Note: This class is only meant for use within this file, and for the
+// simplified use case of a single Wait() and a single Signal(), followed by
+// discarding the object (never reused).
+// This is a replacement of rtc::Event that is async-safe and doesn't use
+// pthread api. This is necessary since signal handlers cannot allocate memory
+// or use pthread api. This class is ported from Chromium.
+class AsyncSafeWaitableEvent {
+ public:
+ AsyncSafeWaitableEvent() {
+ std::atomic_store_explicit(&futex_, 0, std::memory_order_release);
+ }
+
+ ~AsyncSafeWaitableEvent() {}
+
+ // Returns false in the event of an error and errno is set to indicate the
+ // cause of the error.
+ bool Wait() {
+ // futex() can wake up spuriously if this memory address was previously used
+ // for a pthread mutex. So, also check the condition.
+ while (true) {
+ int res = syscall(SYS_futex, &futex_, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0,
+ nullptr, nullptr, 0);
+ if (std::atomic_load_explicit(&futex_, std::memory_order_acquire) != 0)
+ return true;
+ if (res != 0)
+ return false;
+ }
+ }
+
+ void Signal() {
+ std::atomic_store_explicit(&futex_, 1, std::memory_order_release);
+ syscall(SYS_futex, &futex_, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, nullptr,
+ nullptr, 0);
+ }
+
+ private:
+ std::atomic<int> futex_;
+};
+
+// Struct to store the arguments to the signal handler.
+struct SignalHandlerOutputState {
+ // This function is called iteratively for each stack trace element and stores
+ // the element in the array from `unwind_output_state`.
+ static _Unwind_Reason_Code UnwindBacktrace(
+ struct _Unwind_Context* unwind_context,
+ void* unwind_output_state);
+
+ // This event is signalled when signal handler is done executing.
+ AsyncSafeWaitableEvent signal_handler_finish_event;
+ // Running counter of array index below.
+ size_t stack_size_counter = 0;
+ // Array storing the stack trace.
+ uintptr_t addresses[kMaxStackSize];
+};
+
+// This function is called iteratively for each stack trace element and stores
+// the element in the array from `unwind_output_state`.
+_Unwind_Reason_Code SignalHandlerOutputState::UnwindBacktrace(
+ struct _Unwind_Context* unwind_context,
+ void* unwind_output_state) {
+ SignalHandlerOutputState* const output_state =
+ static_cast<SignalHandlerOutputState*>(unwind_output_state);
+
+ // Abort if output state is corrupt.
+ if (output_state == nullptr)
+ return _URC_END_OF_STACK;
+
+ // Avoid overflowing the stack trace array.
+ if (output_state->stack_size_counter >= kMaxStackSize)
+ return _URC_END_OF_STACK;
+
+ // Store the instruction pointer in the array. Subtract 2 since we want to get
+ // the call instruction pointer, not the return address which is the
+ // instruction after.
+ output_state->addresses[output_state->stack_size_counter] =
+ _Unwind_GetIP(unwind_context) - 2;
+ ++output_state->stack_size_counter;
+
+ return _URC_NO_REASON;
+}
+
+class GlobalStackUnwinder {
+ public:
+ static GlobalStackUnwinder& Get() {
+ static GlobalStackUnwinder* const instance = new GlobalStackUnwinder();
+ return *instance;
+ }
+ const char* CaptureRawStacktrace(int pid,
+ int tid,
+ SignalHandlerOutputState* params);
+
+ private:
+ GlobalStackUnwinder() { current_output_state_.store(nullptr); }
+
+ // Temporarily installed signal handler.
+ static void SignalHandler(int signum, siginfo_t* info, void* ptr);
+
+ Mutex mutex_;
+
+ // Accessed by signal handler.
+ static std::atomic<SignalHandlerOutputState*> current_output_state_;
+ // A signal handler mustn't use locks.
+ static_assert(std::atomic<SignalHandlerOutputState*>::is_always_lock_free);
+};
+
+std::atomic<SignalHandlerOutputState*>
+ GlobalStackUnwinder::current_output_state_;
+
+// This signal handler is exectued on the interrupted thread.
+void GlobalStackUnwinder::SignalHandler(int signum,
+ siginfo_t* info,
+ void* ptr) {
+ // This should have been set by the thread requesting the stack trace.
+ SignalHandlerOutputState* signal_handler_output_state =
+ current_output_state_.load();
+ if (signal_handler_output_state != nullptr) {
+ _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace,
+ signal_handler_output_state);
+ signal_handler_output_state->signal_handler_finish_event.Signal();
+ }
+}
+
+// Temporarily change the signal handler to a function that records a raw stack
+// trace and interrupt the given tid. This function will block until the output
+// thread stack trace has been stored in `params`. The return value is an error
+// string on failure and null on success.
+const char* GlobalStackUnwinder::CaptureRawStacktrace(
+ int pid,
+ int tid,
+ SignalHandlerOutputState* params) {
+ // This function is under a global lock since we are changing the signal
+ // handler and using global state for the output. The lock is to ensure only
+ // one thread at a time gets captured. The lock also means we need to be very
+ // careful with what statements we put in this function, and we should even
+ // avoid logging here.
+ struct sigaction act;
+ struct sigaction old_act;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = &SignalHandler;
+ act.sa_flags = SA_RESTART | SA_SIGINFO;
+ sigemptyset(&act.sa_mask);
+
+ MutexLock loch(&mutex_);
+ current_output_state_.store(params);
+
+ if (sigaction(kSignal, &act, &old_act) != 0)
+ return "Failed to change signal action";
+
+ // Interrupt the thread which will execute SignalHandler() on the given
+ // thread.
+ if (tgkill(pid, tid, kSignal) != 0)
+ return "Failed to interrupt thread";
+
+ // Wait until the thread is done recording its stack trace.
+ if (!params->signal_handler_finish_event.Wait())
+ return "Failed to wait for thread to finish stack trace";
+
+ // Restore previous signal handler.
+ sigaction(kSignal, &old_act, /* old_act= */ nullptr);
+
+ return nullptr;
+}
+
+// Translate addresses into symbolic information using dladdr().
+std::vector<StackTraceElement> FormatStackTrace(
+ const SignalHandlerOutputState& params) {
+ std::vector<StackTraceElement> stack_trace;
+ for (size_t i = 0; i < params.stack_size_counter; ++i) {
+ const uintptr_t address = params.addresses[i];
+
+ Dl_info dl_info = {};
+ if (!dladdr(reinterpret_cast<void*>(address), &dl_info)) {
+ RTC_LOG(LS_WARNING)
+ << "Could not translate address to symbolic information for address "
+ << address << " at stack depth " << i;
+ continue;
+ }
+
+ StackTraceElement stack_trace_element;
+ stack_trace_element.shared_object_path = dl_info.dli_fname;
+ stack_trace_element.relative_address = static_cast<uint32_t>(
+ address - reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
+ stack_trace_element.symbol_name = dl_info.dli_sname;
+
+ stack_trace.push_back(stack_trace_element);
+ }
+
+ return stack_trace;
+}
+
+} // namespace
+
+std::vector<StackTraceElement> GetStackTrace(int tid) {
+ // Only a thread itself can unwind its stack, so we will interrupt the given
+ // tid with a custom signal handler in order to unwind its stack. The stack
+ // will be recorded to `params` through the use of the global pointer
+ // `g_signal_handler_param`.
+ SignalHandlerOutputState params;
+
+ const char* error_string =
+ GlobalStackUnwinder::Get().CaptureRawStacktrace(getpid(), tid, &params);
+ if (error_string != nullptr) {
+ RTC_LOG(LS_ERROR) << error_string << ". tid: " << tid
+ << ". errno: " << errno;
+ return {};
+ }
+ if (params.stack_size_counter >= kMaxStackSize) {
+ RTC_LOG(LS_WARNING) << "Stack trace for thread " << tid << " was truncated";
+ }
+ return FormatStackTrace(params);
+}
+
+std::vector<StackTraceElement> GetStackTrace() {
+ SignalHandlerOutputState params;
+ _Unwind_Backtrace(&SignalHandlerOutputState::UnwindBacktrace, &params);
+ if (params.stack_size_counter >= kMaxStackSize) {
+ RTC_LOG(LS_WARNING) << "Stack trace was truncated";
+ }
+ return FormatStackTrace(params);
+}
+
+std::string StackTraceToString(
+ const std::vector<StackTraceElement>& stack_trace) {
+ rtc::StringBuilder string_builder;
+
+ for (size_t i = 0; i < stack_trace.size(); ++i) {
+ const StackTraceElement& stack_trace_element = stack_trace[i];
+ string_builder.AppendFormat(
+ "#%02zu pc %08x %s", i,
+ static_cast<uint32_t>(stack_trace_element.relative_address),
+ stack_trace_element.shared_object_path);
+ // The symbol name is only available for unstripped .so files.
+ if (stack_trace_element.symbol_name != nullptr)
+ string_builder.AppendFormat(" %s", stack_trace_element.symbol_name);
+
+ string_builder.AppendFormat("\n");
+ }
+
+ return string_builder.Release();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.h b/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.h
new file mode 100644
index 0000000000..4cae1a58dd
--- /dev/null
+++ b/third_party/libwebrtc/sdk/android/native_api/stacktrace/stacktrace.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef SDK_ANDROID_NATIVE_API_STACKTRACE_STACKTRACE_H_
+#define SDK_ANDROID_NATIVE_API_STACKTRACE_STACKTRACE_H_
+
+#include <string>
+#include <vector>
+
+namespace webrtc {
+
+struct StackTraceElement {
+ // Pathname of shared object (.so file) that contains address.
+ const char* shared_object_path;
+ // Execution address relative to the .so base address. This matches the
+ // addresses you get with "nm", "objdump", and "ndk-stack", as long as the
+ // code is compiled with position-independent code. Android requires
+ // position-independent code since Lollipop.
+ uint32_t relative_address;
+ // Name of symbol whose definition overlaps the address. This value is null
+ // when symbol names are stripped.
+ const char* symbol_name;
+};
+
+// Utility to unwind stack for a given thread on Android ARM devices. This works
+// on top of unwind.h and unwinds native (C++) stack traces only.
+std::vector<StackTraceElement> GetStackTrace(int tid);
+
+// Unwind the stack of the current thread.
+std::vector<StackTraceElement> GetStackTrace();
+
+// Get a string representation of the stack trace in a format ndk-stack accepts.
+std::string StackTraceToString(
+ const std::vector<StackTraceElement>& stack_trace);
+
+} // namespace webrtc
+
+#endif // SDK_ANDROID_NATIVE_API_STACKTRACE_STACKTRACE_H_