Stack unwinding unit tests.

Bug: 17436734
Change-Id: I1e98da7eaeab646b448fb3f2b683973dddc319b0
diff --git a/tests/Android.mk b/tests/Android.mk
index d5fb5cf..890e203 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -88,7 +88,6 @@
     search_test.cpp \
     signal_test.cpp \
     stack_protector_test.cpp \
-    stack_unwinding_test.cpp \
     stdatomic_test.cpp \
     stdint_test.cpp \
     stdio_test.cpp \
@@ -137,9 +136,6 @@
 libBionicStandardTests_ldlibs_host := \
     -lrt \
 
-libBionicStandardTests_whole_static_libraries := \
-    libBionicUnwindTest \
-
 module := libBionicStandardTests
 module_tag := optional
 build_type := target
@@ -149,25 +145,6 @@
 include $(LOCAL_PATH)/Android.build.mk
 
 # -----------------------------------------------------------------------------
-# Special stack unwinding test library compiled with special flags.
-# -----------------------------------------------------------------------------
-libBionicUnwindTest_cflags := \
-    $(test_cflags) \
-    -fexceptions \
-    -fnon-call-exceptions \
-
-libBionicUnwindTest_src_files := \
-    stack_unwinding_test_impl.c \
-
-module := libBionicUnwindTest
-module_tag := optional
-build_type := target
-build_target := STATIC_TEST_LIBRARY
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-# -----------------------------------------------------------------------------
 # Fortify tests.
 # -----------------------------------------------------------------------------
 $(foreach compiler,gcc clang, \
@@ -255,8 +232,15 @@
     atexit_test.cpp \
     dlext_test.cpp \
     dlfcn_test.cpp \
+    stack_unwinding_test.cpp \
+    stack_unwinding_test_impl.c \
 
 bionic-unit-tests_cflags := $(test_cflags)
+
+bionic-unit-tests_conlyflags := \
+    -fexceptions \
+    -fnon-call-exceptions \
+
 bionic-unit-tests_cppflags := $(test_cppflags)
 
 bionic-unit-tests_ldflags := \
diff --git a/tests/stack_unwinding_test.cpp b/tests/stack_unwinding_test.cpp
index 1024f28..017a5f2 100644
--- a/tests/stack_unwinding_test.cpp
+++ b/tests/stack_unwinding_test.cpp
@@ -20,18 +20,83 @@
 
 #include <gtest/gtest.h>
 
-extern "C" {
-  void do_test();
+#include <dlfcn.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <unwind.h>
+
+#include "ScopedSignalHandler.h"
+
+#define noinline __attribute__((noinline))
+
+static _Unwind_Reason_Code FrameCounter(_Unwind_Context* ctx __unused, void* arg) {
+  int* count_ptr = reinterpret_cast<int*>(arg);
+
+#if SHOW_FRAME_LOCATIONS
+  void* ip = reinterpret_cast<void*>(_Unwind_GetIP(ctx));
+
+  const char* symbol = "<unknown>";
+  int offset = 0;
+
+  Dl_info info;
+  memset(&info, 0, sizeof(info));
+  if (dladdr(ip, &info) != 0) {
+    symbol = info.dli_sname;
+    if (info.dli_saddr != nullptr) {
+      offset = static_cast<int>(reinterpret_cast<char*>(ip) - reinterpret_cast<char*>(info.dli_saddr));
+    }
+  }
+
+  fprintf(stderr, " #%02d %p %s%+d (%s)\n", *count_ptr, ip, symbol, offset, info.dli_fname ? info.dli_fname : "??");
+  fflush(stderr);
+#endif
+
+  ++*count_ptr;
+  return _URC_NO_REASON;
 }
 
+static int noinline unwind_one_frame_deeper() {
+  int count = 0;
+  _Unwind_Backtrace(FrameCounter, &count);
+  return count;
+}
+
+TEST(stack_unwinding, easy) {
+  int count = 0;
+  _Unwind_Backtrace(FrameCounter, &count);
+  int deeper_count = unwind_one_frame_deeper();
+  ASSERT_EQ(count + 1, deeper_count);
+}
+
+static int killer_count = 0;
+static int handler_count = 0;
+static int handler_one_deeper_count = 0;
+
+static void noinline UnwindSignalHandler(int) {
+  _Unwind_Backtrace(FrameCounter, &handler_count);
+  ASSERT_GT(handler_count, killer_count);
+
+  handler_one_deeper_count = unwind_one_frame_deeper();
+  ASSERT_EQ(handler_count + 1, handler_one_deeper_count);
+}
+
+TEST(stack_unwinding, unwind_through_signal_frame) {
+  ScopedSignalHandler ssh(SIGUSR1, UnwindSignalHandler);
+
+  _Unwind_Backtrace(FrameCounter, &killer_count);
+
+  ASSERT_EQ(0, kill(getpid(), SIGUSR1));
+}
+
+extern "C" void unwind_through_frame_with_cleanup_function();
+
 // We have to say "DeathTest" here so gtest knows to run this test (which exits)
 // in its own process.
-TEST(stack_unwinding_DeathTest, unwinding_through_signal_frame) {
-// Only our x86 unwinding is good enough. Switch to libunwind?
-#if defined(__BIONIC__) && defined(__i386__)
+TEST(stack_unwinding_DeathTest, unwind_through_frame_with_cleanup_function) {
   ::testing::FLAGS_gtest_death_test_style = "threadsafe";
-  ASSERT_EXIT(do_test(), ::testing::ExitedWithCode(42), "");
-#else // __i386__
-  GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif // __i386__
+  ASSERT_EXIT(unwind_through_frame_with_cleanup_function(), ::testing::ExitedWithCode(42), "");
 }
diff --git a/tests/stack_unwinding_test_impl.c b/tests/stack_unwinding_test_impl.c
index 7518a2c..2e03938 100644
--- a/tests/stack_unwinding_test_impl.c
+++ b/tests/stack_unwinding_test_impl.c
@@ -18,52 +18,47 @@
  * Contributed by: Intel Corporation
  */
 
-#include <stdio.h>
+#include <dlfcn.h>
 #include <signal.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <unwind.h>
 
 #define noinline __attribute__((__noinline__))
-#define unused __attribute__((__unused__))
 
-static noinline _Unwind_Reason_Code stop_fn(int a unused,
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+static noinline _Unwind_Reason_Code cleanup_unwind_fn(int a __unused,
     _Unwind_Action action,
-    _Unwind_Exception_Class b unused, struct _Unwind_Exception* c unused,
-    struct _Unwind_Context* d unused, void* e unused) {
+    _Unwind_Exception_Class b __unused,
+    struct _Unwind_Exception* c __unused,
+    struct _Unwind_Context* ctx __unused,
+    void* e __unused) {
   if ((action & _UA_END_OF_STACK) != 0) {
-    // We reached the end of the stack without executing foo_cleanup. Test failed.
-    abort();
+    abort(); // We reached the end of the stack without executing foo_cleanup (which would have exited). Test failed.
   }
   return _URC_NO_REASON;
 }
 
-static void noinline foo_cleanup(char* param unused) {
+static void noinline foo_cleanup(char* param __unused) {
   exit(42);
 }
 
-static void noinline do_crash() {
-  char* ptr = NULL;
-  *ptr = 0; // Deliberately cause a SIGSEGV.
+static void noinline function_with_cleanup_function() {
+  char c __attribute__((cleanup(foo_cleanup))) __unused;
+  *((int*) 1) = 0;
 }
 
-static void noinline foo() {
-  char c1 __attribute__((cleanup(foo_cleanup))) unused;
-  do_crash();
+static void noinline cleanup_sigsegv_handler(int param __unused) {
+  struct _Unwind_Exception* exception = (struct _Unwind_Exception*) calloc(1, sizeof(*exception));
+  _Unwind_ForcedUnwind(exception, cleanup_unwind_fn, 0);
 }
 
-// It's SEGSEGV handler. We start forced stack unwinding here.
-// If libgcc don't find dso for signal frame stack unwinding will be finished.
-// libgcc pass to stop_fn _UA_END_OF_STACK flag.
-// Test pass condition: stack unwinding through signal frame and foo1_handler execution.
-static void noinline sigsegv_handler(int param unused) {
-  struct _Unwind_Exception* exception = (struct _Unwind_Exception*) malloc(sizeof(*exception));
-  memset(&exception->exception_class, 0, sizeof(exception->exception_class));
-  exception->exception_cleanup = 0;
-  _Unwind_ForcedUnwind(exception, stop_fn, 0);
-}
-
-void do_test() {
-  signal(SIGSEGV, &sigsegv_handler);
-  foo();
+void unwind_through_frame_with_cleanup_function() {
+  signal(SIGSEGV, &cleanup_sigsegv_handler);
+  function_with_cleanup_function();
 }