summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2014-09-17 17:21:20 -0700
committerElliott Hughes <enh@google.com>2014-10-27 16:42:37 -0700
commit190dce9e56c750be6b8d113ffdd32a9c20c19e3d (patch)
tree7bdaed9e582a5fc3948163fef82ed1fbd97deaab /tests
parente5e61a0a920a8e6560735b2e565c3bd7a1e35ba5 (diff)
downloadbionic-190dce9e56c750be6b8d113ffdd32a9c20c19e3d.zip
bionic-190dce9e56c750be6b8d113ffdd32a9c20c19e3d.tar.gz
bionic-190dce9e56c750be6b8d113ffdd32a9c20c19e3d.tar.bz2
Stack unwinding unit tests.
Bug: 17436734 (cherry picked from commit bee1993a14b47bc7acda544242f405ae45e42566) Change-Id: I7205a862ba2c3b474e287f5e9c8982cef4610af9
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk30
-rw-r--r--tests/stack_unwinding_test.cpp83
-rw-r--r--tests/stack_unwinding_test_impl.c51
3 files changed, 104 insertions, 60 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index dd43049..23a2cb3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -88,7 +88,6 @@ libBionicStandardTests_src_files := \
search_test.cpp \
signal_test.cpp \
stack_protector_test.cpp \
- stack_unwinding_test.cpp \
stdatomic_test.cpp \
stdint_test.cpp \
stdio_test.cpp \
@@ -134,9 +133,6 @@ libBionicStandardTests_c_includes := \
libBionicStandardTests_ldlibs_host := \
-lrt \
-libBionicStandardTests_whole_static_libraries := \
- libBionicUnwindTest \
-
module := libBionicStandardTests
module_tag := optional
build_type := target
@@ -146,25 +142,6 @@ build_type := host
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, \
@@ -247,8 +224,15 @@ bionic-unit-tests_src_files := \
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 foo() {
- char c1 __attribute__((cleanup(foo_cleanup))) unused;
- do_crash();
+static void noinline function_with_cleanup_function() {
+ char c __attribute__((cleanup(foo_cleanup))) __unused;
+ *((int*) 1) = 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);
+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);
}
-void do_test() {
- signal(SIGSEGV, &sigsegv_handler);
- foo();
+void unwind_through_frame_with_cleanup_function() {
+ signal(SIGSEGV, &cleanup_sigsegv_handler);
+ function_with_cleanup_function();
}