From 9bddcde5165adb60577a8817ac823bf7ac88ea0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Chor=C4=85=C5=BCewicz?= Date: Thu, 11 Aug 2022 13:51:56 +0200 Subject: [PATCH] Add support in test framework for signal handling --- CONTRIBUTING.md | 8 +++++ tests/common/test_backtrace.c | 24 +------------ tests/common/test_backtrace.h | 3 +- tests/common/test_sighandlers.h | 31 +++++++++++++++++ tests/common/test_sighandlers.hpp | 51 ++++++++++++++++++++++++++++ tests/common/thread_helpers.hpp | 3 ++ tests/common/thread_helpers_test.cpp | 13 ++++++- tests/common/unittest.h | 4 +-- tests/common/unittest.hpp | 1 + 9 files changed, 110 insertions(+), 28 deletions(-) create mode 100644 tests/common/test_sighandlers.h create mode 100644 tests/common/test_sighandlers.hpp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f11db1fa..28816496 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -168,3 +168,11 @@ It's simply possible to print this content by running C++ code like this (from a auto spans = span_runtimes_from_stream(stream); std::cout << spans << std::endl; ``` + +## Debugging segfaults and other signals + +Pmemstream test framework has support for handling signals and turning them into exceptions so that +applications do not crash. It is useful for Rapidcheck tests (it makes shrinking possible) and for ctest. + +To turn this feature on, set env variable PMEMSTREAM_HANDLE_SIGNAL_FOR_DEBUG=1 and add test_register_sighandlers() +where the exceptions should be thrown (e.g. at the beginning of lambda passed to rc::check()) diff --git a/tests/common/test_backtrace.c b/tests/common/test_backtrace.c index 915f3cf3..d8c45280 100644 --- a/tests/common/test_backtrace.c +++ b/tests/common/test_backtrace.c @@ -155,32 +155,10 @@ void test_dump_backtrace(void) /* * test_sighandler -- fatal signal handler */ -void test_sighandler(int sig) +void test_backtrace_sighandler(int sig) { -#ifndef PMEMSTREAM_USE_TSAN printf("\nSignal: %s, backtrace:\n", strsignal(sig)); test_dump_backtrace(); printf("\n"); exit(128 + sig); -#endif -} - -/* - * test_register_sighandlers -- register signal handlers for various fatal - * signals - */ -void test_register_sighandlers(void) -{ -#ifndef PMEMSTREAM_USE_TSAN - signal(SIGSEGV, test_sighandler); - signal(SIGABRT, test_sighandler); - signal(SIGILL, test_sighandler); - signal(SIGFPE, test_sighandler); - signal(SIGINT, test_sighandler); -#ifndef _WIN32 - signal(SIGALRM, test_sighandler); - signal(SIGQUIT, test_sighandler); - signal(SIGBUS, test_sighandler); -#endif -#endif } diff --git a/tests/common/test_backtrace.h b/tests/common/test_backtrace.h index 5df656ef..67bf0706 100644 --- a/tests/common/test_backtrace.h +++ b/tests/common/test_backtrace.h @@ -9,8 +9,7 @@ extern "C" { #endif void test_dump_backtrace(void); -void test_sighandler(int sig); -void test_register_sighandlers(void); +void test_backtrace_sighandler(int sig); #ifdef __cplusplus } diff --git a/tests/common/test_sighandlers.h b/tests/common/test_sighandlers.h new file mode 100644 index 00000000..86374efa --- /dev/null +++ b/tests/common/test_sighandlers.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2022, Intel Corporation */ + +#ifndef LIBPMEMSTREAM_TEST_SIGHANDLERS_H +#define LIBPMEMSTREAM_TEST_SIGHANDLERS_H + +#include + +#include "test_backtrace.h" + +/* + * test_register_sighandlers -- register signal handlers for various fatal + * signals + */ +static inline void test_register_sighandlers(void (*sighandler)(int)) +{ +#ifndef PMEMSTREAM_USE_TSAN + signal(SIGSEGV, sighandler); + signal(SIGABRT, sighandler); + signal(SIGILL, sighandler); + signal(SIGFPE, sighandler); + signal(SIGINT, sighandler); +#ifndef _WIN32 + signal(SIGALRM, sighandler); + signal(SIGQUIT, sighandler); + signal(SIGBUS, sighandler); +#endif +#endif +} + +#endif // LIBPMEMSTREAM_TEST_SIGHANDLERS_H diff --git a/tests/common/test_sighandlers.hpp b/tests/common/test_sighandlers.hpp new file mode 100644 index 00000000..68989e1c --- /dev/null +++ b/tests/common/test_sighandlers.hpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* Copyright 2022, Intel Corporation */ + +#ifndef LIBPMEMSTREAM_TEST_SIGHANDLERS_HPP +#define LIBPMEMSTREAM_TEST_SIGHANDLERS_HPP + +#include +#include +#include +#include + +#include "test_sighandlers.h" + +static inline sigjmp_buf &sigjmp_tls_buf() +{ + static thread_local sigjmp_buf sigjmp; + return sigjmp; +} + +static inline void test_handle_signal_for_debug(int sig) +{ + if (sig == SIGKILL) { + test_backtrace_sighandler(sig); + } else { + siglongjmp(sigjmp_tls_buf(), sig); + } +} + +static inline int test_register_signal_handler_for_debug() +{ + int sig = sigsetjmp(sigjmp_tls_buf(), 0); + if (sig != 0) { + test_register_sighandlers(test_backtrace_sighandler); + throw std::runtime_error("Signal: " + std::string(strsignal(sig)) + " received!"); + } else { + test_register_sighandlers(test_handle_signal_for_debug); + return 0; + } +} + +static inline int test_register_sighandlers() +{ + if (getenv("PMEMSTREAM_HANDLE_SIGNAL_FOR_DEBUG")) { + return test_register_signal_handler_for_debug(); + } else { + test_register_sighandlers(test_backtrace_sighandler); + return 0; + } +} + +#endif // LIBPMEMSTREAM_TEST_SIGHANDLERS_HPP diff --git a/tests/common/thread_helpers.hpp b/tests/common/thread_helpers.hpp index ac872014..8554f135 100644 --- a/tests/common/thread_helpers.hpp +++ b/tests/common/thread_helpers.hpp @@ -11,6 +11,8 @@ #include #include +#include "test_sighandlers.hpp" + static inline std::string get_msg_from_exception_ptr(std::exception_ptr ptr) { try { @@ -61,6 +63,7 @@ void parallel_exec(size_t threads_number, Function f) threads.emplace_back( [&](size_t id) { try { + test_register_sighandlers(); f(id); } catch (...) { exception_ptrs[id] = std::current_exception(); diff --git a/tests/common/thread_helpers_test.cpp b/tests/common/thread_helpers_test.cpp index 5b7930e9..61c6fcb6 100644 --- a/tests/common/thread_helpers_test.cpp +++ b/tests/common/thread_helpers_test.cpp @@ -16,7 +16,6 @@ static constexpr size_t concurrency = 128; int main() { struct test_config_type test_config; - return run_test(test_config, [] { std::atomic counter; counter = 0; @@ -36,5 +35,17 @@ int main() syncthreads(); UT_ASSERTeq(counter.load(), concurrency * 2); }); + + setenv("PMEMSTREAM_HANDLE_SIGNAL_FOR_DEBUG", "1", 1); + try { + parallel_exec(2, [&](size_t id) { + auto ptr = (char *)nullptr; + std::cout << *ptr; + UT_ASSERT_UNREACHABLE; + }); + } catch (std::runtime_error &e) { + } catch (...) { + UT_ASSERT_UNREACHABLE; + } }); } diff --git a/tests/common/unittest.h b/tests/common/unittest.h index df269e54..e6e86c26 100644 --- a/tests/common/unittest.h +++ b/tests/common/unittest.h @@ -5,7 +5,7 @@ #define LIBPMEMSTREAM_UNITTEST_H #include "libpmemstream.h" -#include "test_backtrace.h" +#include "test_sighandlers.h" #include #include @@ -50,7 +50,7 @@ extern "C" { #endif /* XXX: refactor to use __start (https://stackoverflow.com/questions/15919356/c-program-start) */ -#define START() test_register_sighandlers() +#define START() test_register_sighandlers(test_backtrace_sighandler) /* XXX: provide function to get the actual metadata overhead */ #define STREAM_METADATA_SIZE (16UL * 1024) diff --git a/tests/common/unittest.hpp b/tests/common/unittest.hpp index 614ded59..0d09db7e 100644 --- a/tests/common/unittest.hpp +++ b/tests/common/unittest.hpp @@ -18,6 +18,7 @@ #include #include "env_setter.hpp" +#include "test_sighandlers.hpp" #include "valgrind_internal.h" /* Execute only this many runs of rc_check tests under valgrind. It must be bigger than one because