diff options
author | Elliott Hughes <enh@google.com> | 2014-09-17 17:21:20 -0700 |
---|---|---|
committer | Elliott Hughes <enh@google.com> | 2014-10-27 16:42:37 -0700 |
commit | 190dce9e56c750be6b8d113ffdd32a9c20c19e3d (patch) | |
tree | 7bdaed9e582a5fc3948163fef82ed1fbd97deaab /tests | |
parent | e5e61a0a920a8e6560735b2e565c3bd7a1e35ba5 (diff) | |
download | bionic-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.mk | 30 | ||||
-rw-r--r-- | tests/stack_unwinding_test.cpp | 83 | ||||
-rw-r--r-- | tests/stack_unwinding_test_impl.c | 51 |
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(); } |