diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index 534865ec11..60b55179f1 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -68,7 +68,6 @@ if (is_android) { ":native_api_codecs", ":native_api_jni", ":native_api_peerconnection", - ":native_api_stacktrace", ":native_api_video", ] } @@ -981,19 +980,6 @@ if (is_android) { ] } - # API for capturing and printing native stacktraces. - rtc_static_library("native_api_stacktrace") { - visibility = [ "*" ] - sources = [ - "native_api/stacktrace/stacktrace.cc", - "native_api/stacktrace/stacktrace.h", - ] - - deps = [ - "../../rtc_base", - ] - } - # API for creating C++ wrapper implementations of api/mediastreaminterface.h # video interfaces from their Java equivalents. rtc_static_library("native_api_video") { @@ -1526,7 +1512,6 @@ if (is_android) { "native_unittests/codecs/wrapper_unittest.cc", "native_unittests/java_types_unittest.cc", "native_unittests/peerconnection/peer_connection_factory_unittest.cc", - "native_unittests/stacktrace/stacktrace_unittest.cc", "native_unittests/test_jni_onload.cc", "native_unittests/video/video_source_unittest.cc", ] @@ -1552,7 +1537,6 @@ if (is_android) { ":native_api_codecs", ":native_api_jni", ":native_api_peerconnection", - ":native_api_stacktrace", ":native_api_video", ":opensles_audio_device_module", ":video_jni", diff --git a/sdk/android/native_api/stacktrace/stacktrace.cc b/sdk/android/native_api/stacktrace/stacktrace.cc deleted file mode 100644 index 8268ff549e..0000000000 --- a/sdk/android/native_api/stacktrace/stacktrace.cc +++ /dev/null @@ -1,231 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// ptrace.h is polluting the namespace. Clean up to avoid conflicts with rtc. -#if defined(DS) -#undef DS -#endif - -#include "rtc_base/critical_section.h" -#include "rtc_base/logging.h" -#include "rtc_base/strings/string_builder.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 futex_; -}; - -// Struct to store the arguments to the signal handler. -struct SignalHandlerOutputState { - // 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]; -}; - -// Global lock to ensure only one thread gets interrupted at a time. -rtc::GlobalLockPod g_signal_handler_lock; -// Argument passed to the ThreadSignalHandler() from the sampling thread to the -// sampled (stopped) thread. This value is set just before sending signal to the -// thread and reset when handler is done. -SignalHandlerOutputState* volatile g_signal_handler_output_state; - -// This function is called iteratively for each stack trace element and stores -// the element in the array from |unwind_output_state|. -_Unwind_Reason_Code UnwindBacktrace(struct _Unwind_Context* unwind_context, - void* unwind_output_state) { - SignalHandlerOutputState* const output_state = - static_cast(unwind_output_state); - - // 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; -} - -// This signal handler is exectued on the interrupted thread. -void SignalHandler(int signum, siginfo_t* info, void* ptr) { - _Unwind_Backtrace(&UnwindBacktrace, g_signal_handler_output_state); - g_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* 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); - - rtc::GlobalLockScope ls(&g_signal_handler_lock); - g_signal_handler_output_state = 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; -} - -} // namespace - -std::vector 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 = CaptureRawStacktrace(getpid(), tid, ¶ms); - 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"; - - // Translate addresses into symbolic information using dladdr(). - std::vector 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(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( - address - reinterpret_cast(dl_info.dli_fbase)); - stack_trace_element.symbol_name = dl_info.dli_sname; - - stack_trace.push_back(stack_trace_element); - } - - return stack_trace; -} - -std::string StackTraceToString( - const std::vector& 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(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/sdk/android/native_api/stacktrace/stacktrace.h b/sdk/android/native_api/stacktrace/stacktrace.h deleted file mode 100644 index 1fbf21b347..0000000000 --- a/sdk/android/native_api/stacktrace/stacktrace.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 -#include - -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 GetStackTrace(int tid); - -// Get a string representation of the stack trace in a format ndk-stack accepts. -std::string StackTraceToString( - const std::vector& stack_trace); - -} // namespace webrtc - -#endif // SDK_ANDROID_NATIVE_API_STACKTRACE_STACKTRACE_H_ diff --git a/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc b/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc deleted file mode 100644 index 4268fdaf79..0000000000 --- a/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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 -#include -#include "absl/memory/memory.h" -#include "rtc_base/criticalsection.h" -#include "rtc_base/event.h" -#include "rtc_base/logging.h" -#include "rtc_base/platform_thread.h" -#include "rtc_base/strings/string_builder.h" -#include "test/gtest.h" - -namespace webrtc { -namespace test { - -// Returns the execution address relative to the .so base address. This matches -// the addresses we get from GetStacktrace(). -uint32_t GetCurrentRelativeExecutionAddress() { - void* pc = __builtin_return_address(0); - Dl_info dl_info = {}; - const bool success = dladdr(pc, &dl_info); - EXPECT_TRUE(success); - return static_cast(reinterpret_cast(pc) - - reinterpret_cast(dl_info.dli_fbase)); -} - -// Returns true if any of the stack trace element is inside the specified -// region. -bool StackTraceContainsRange(const std::vector& stack_trace, - uintptr_t pc_low, - uintptr_t pc_high) { - for (const StackTraceElement& stack_trace_element : stack_trace) { - if (pc_low <= stack_trace_element.relative_address && - pc_high >= stack_trace_element.relative_address) { - return true; - } - } - return false; -} - -class DeadlockInterface { - public: - virtual ~DeadlockInterface() {} - - // This function should deadlock until Release() is called. - virtual void Deadlock() = 0; - - // This function should release the thread stuck in Deadlock(). - virtual void Release() = 0; -}; - -struct ThreadParams { - volatile int tid; - // Signaled when the deadlock region is entered. - rtc::Event deadlock_start_event; - DeadlockInterface* volatile deadlock_impl; - // Defines an address range within the deadlock will occur. - volatile uint32_t deadlock_region_start_address; - volatile uint32_t deadlock_region_end_address; - // Signaled when the deadlock is done. - rtc::Event deadlock_done_event; -}; - -class RtcEventDeadlock : public DeadlockInterface { - private: - void Deadlock() override { event.Wait(rtc::Event::kForever); } - void Release() override { event.Set(); } - - rtc::Event event; -}; - -class RtcCriticalSectionDeadlock : public DeadlockInterface { - public: - RtcCriticalSectionDeadlock() - : critscope_(absl::make_unique(&crit_)) {} - - private: - void Deadlock() override { rtc::CritScope lock(&crit_); } - - void Release() override { critscope_.reset(); } - - rtc::CriticalSection crit_; - std::unique_ptr critscope_; -}; - -class SpinDeadlock : public DeadlockInterface { - public: - SpinDeadlock() : is_deadlocked_(true) {} - - private: - void Deadlock() override { - while (is_deadlocked_) { - } - } - - void Release() override { is_deadlocked_ = false; } - - std::atomic is_deadlocked_; -}; - -class SleepDeadlock : public DeadlockInterface { - private: - void Deadlock() override { sleep(1000000); } - - void Release() override { - // The interrupt itself will break free from the sleep. - } -}; - -// This is the function that is exectued by the thread that will deadlock and -// have its stacktrace captured. -void ThreadFunction(void* void_params) { - ThreadParams* params = static_cast(void_params); - params->tid = gettid(); - - params->deadlock_region_start_address = GetCurrentRelativeExecutionAddress(); - params->deadlock_start_event.Set(); - params->deadlock_impl->Deadlock(); - params->deadlock_region_end_address = GetCurrentRelativeExecutionAddress(); - - params->deadlock_done_event.Set(); -} - -void TestStacktrace(std::unique_ptr deadlock_impl) { - // Set params that will be sent to other thread. - ThreadParams params; - params.deadlock_impl = deadlock_impl.get(); - - // Spawn thread. - rtc::PlatformThread thread(&ThreadFunction, ¶ms, "StacktraceTest"); - thread.Start(); - - // Wait until the thread has entered the deadlock region. - params.deadlock_start_event.Wait(rtc::Event::kForever); - - // Acquire the stack trace of the thread which should now be deadlocking. - std::vector stack_trace = GetStackTrace(params.tid); - - // Release the deadlock so that the thread can continue. - deadlock_impl->Release(); - - // Wait until the thread has left the deadlock. - params.deadlock_done_event.Wait(rtc::Event::kForever); - - // Assert that the stack trace contains the deadlock region. - EXPECT_TRUE(StackTraceContainsRange(stack_trace, - params.deadlock_region_start_address, - params.deadlock_region_end_address)) - << "Deadlock region: [" - << rtc::ToHex(params.deadlock_region_start_address) << ", " - << rtc::ToHex(params.deadlock_region_end_address) - << "] not contained in: " << StackTraceToString(stack_trace); - - thread.Stop(); -} - -TEST(Stacktrace, TestSpinLock) { - TestStacktrace(absl::make_unique()); -} - -TEST(Stacktrace, TestSleep) { - TestStacktrace(absl::make_unique()); -} - -// Stack traces originating from kernel space does not include user space stack -// traces for ARM 32. -#ifdef WEBRTC_ARCH_ARM64 -TEST(Stacktrace, TestRtcEvent) { - TestStacktrace(absl::make_unique()); -} - -TEST(Stacktrace, TestRtcCriticalSection) { - TestStacktrace(absl::make_unique()); -} -#endif - -} // namespace test -} // namespace webrtc