From bee1993a14b47bc7acda544242f405ae45e42566 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 17 Sep 2014 17:21:20 -0700 Subject: [PATCH] Stack unwinding unit tests. Bug: 17436734 Change-Id: I1e98da7eaeab646b448fb3f2b683973dddc319b0 --- tests/Android.mk | 30 +++-------- tests/stack_unwinding_test.cpp | 83 +++++++++++++++++++++++++++---- 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 d5fb5cf2f..890e20360 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 \ @@ -137,9 +136,6 @@ libBionicStandardTests_c_includes := \ libBionicStandardTests_ldlibs_host := \ -lrt \ -libBionicStandardTests_whole_static_libraries := \ - libBionicUnwindTest \ - module := libBionicStandardTests module_tag := optional build_type := target @@ -148,25 +144,6 @@ include $(LOCAL_PATH)/Android.build.mk 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. # ----------------------------------------------------------------------------- @@ -255,8 +232,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 1024f28f1..017a5f2e8 100644 --- a/tests/stack_unwinding_test.cpp +++ b/tests/stack_unwinding_test.cpp @@ -20,18 +20,83 @@ #include -extern "C" { - void do_test(); +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ScopedSignalHandler.h" + +#define noinline __attribute__((noinline)) + +static _Unwind_Reason_Code FrameCounter(_Unwind_Context* ctx __unused, void* arg) { + int* count_ptr = reinterpret_cast(arg); + +#if SHOW_FRAME_LOCATIONS + void* ip = reinterpret_cast(_Unwind_GetIP(ctx)); + + const char* symbol = ""; + 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(reinterpret_cast(ip) - reinterpret_cast(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 7518a2cdd..2e0393847 100644 --- a/tests/stack_unwinding_test_impl.c +++ b/tests/stack_unwinding_test_impl.c @@ -18,52 +18,47 @@ * Contributed by: Intel Corporation */ -#include +#include #include +#include #include #include +#include #include #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(); }