Android: Add helper methods for printing native stack traces

This CL adds utility functions to unwind the stack for a given thread on
Android ARM devices. This works on top of unwind.h and unwinds native
(C++) stack traces only. Unwinding a thread from another thread is done
by overriding the signal handler with a custom function and then
interrupting the specific thread.

Bug: webrtc:10168
Change-Id: If5adffd3a6bb57bf502168743e09a7eefc292bf3
Reviewed-on: https://webrtc-review.googlesource.com/c/118141
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Tommi <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26328}
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
index 60b5517..534865e 100644
--- a/sdk/android/BUILD.gn
+++ b/sdk/android/BUILD.gn
@@ -68,6 +68,7 @@
       ":native_api_codecs",
       ":native_api_jni",
       ":native_api_peerconnection",
+      ":native_api_stacktrace",
       ":native_api_video",
     ]
   }
@@ -980,6 +981,19 @@
     ]
   }
 
+  # 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") {
@@ -1512,6 +1526,7 @@
       "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",
     ]
@@ -1537,6 +1552,7 @@
       ":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
new file mode 100644
index 0000000..8268ff5
--- /dev/null
+++ b/sdk/android/native_api/stacktrace/stacktrace.cc
@@ -0,0 +1,231 @@
+/*
+ *  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 "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<int> 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<SignalHandlerOutputState*>(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<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 = 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";
+
+  // Translate addresses into symbolic information using dladdr().
+  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;
+}
+
+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/sdk/android/native_api/stacktrace/stacktrace.h b/sdk/android/native_api/stacktrace/stacktrace.h
new file mode 100644
index 0000000..1fbf21b
--- /dev/null
+++ b/sdk/android/native_api/stacktrace/stacktrace.h
@@ -0,0 +1,42 @@
+/*
+ *  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);
+
+// 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_
diff --git a/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc b/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc
new file mode 100644
index 0000000..4268fda
--- /dev/null
+++ b/sdk/android/native_unittests/stacktrace/stacktrace_unittest.cc
@@ -0,0 +1,187 @@
+/*
+ *  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 <vector>
+#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<uint32_t>(reinterpret_cast<uintptr_t>(pc) -
+                               reinterpret_cast<uintptr_t>(dl_info.dli_fbase));
+}
+
+// Returns true if any of the stack trace element is inside the specified
+// region.
+bool StackTraceContainsRange(const std::vector<StackTraceElement>& 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<rtc::CritScope>(&crit_)) {}
+
+ private:
+  void Deadlock() override { rtc::CritScope lock(&crit_); }
+
+  void Release() override { critscope_.reset(); }
+
+  rtc::CriticalSection crit_;
+  std::unique_ptr<rtc::CritScope> 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<bool> 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<ThreadParams*>(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<DeadlockInterface> 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, &params, "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<StackTraceElement> 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<SpinDeadlock>());
+}
+
+TEST(Stacktrace, TestSleep) {
+  TestStacktrace(absl::make_unique<SleepDeadlock>());
+}
+
+// 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<RtcEventDeadlock>());
+}
+
+TEST(Stacktrace, TestRtcCriticalSection) {
+  TestStacktrace(absl::make_unique<RtcCriticalSectionDeadlock>());
+}
+#endif
+
+}  // namespace test
+}  // namespace webrtc