From 854d3dcaf84c900482e8b94d2bb475b923fb84d2 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Wed, 17 Jul 2024 13:50:13 +0200 Subject: [PATCH] Portable backtrace mechanism --- CMakeLists.txt | 5 +++- include/reactor-cpp/assert.hh | 47 +++++++++++++++++++------------- include/reactor-cpp/config.hh.in | 3 ++ lib/CMakeLists.txt | 5 ++++ lib/assert.cc | 4 +-- 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b908131..24e5bbed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,10 @@ if (NOT DEFINED LF_REACTOR_CPP_SUFFIX) endif() endif() -find_package (Threads) +find_package(Threads) + +find_package(Backtrace) +set(REACTOR_CPP_USE_BACKTRACE ${Backtrace_FOUND}) set(DEFAULT_BUILD_TYPE "Release") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) diff --git a/include/reactor-cpp/assert.hh b/include/reactor-cpp/assert.hh index ffffff31..8483e8de 100644 --- a/include/reactor-cpp/assert.hh +++ b/include/reactor-cpp/assert.hh @@ -17,11 +17,6 @@ #include #include -#ifdef __linux__ -#include -#include -#endif - #ifdef REACTOR_CPP_VALIDATE constexpr bool runtime_validation = true; #else @@ -37,6 +32,33 @@ constexpr bool runtime_assertion = true; // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define reactor_assert(x) assert(x) +#ifdef REACTOR_CPP_USE_BACKTRACE + +// NOLINTNEXTLINE +#include REACTOR_CPP_BACKTRACE_HEADER +#include +#include + +namespace reactor { + +constexpr std::size_t MAX_TRACE_SIZE{16}; + +inline void print_backtrace() { + std::array trace{nullptr}; + int size = backtrace(trace.data(), MAX_TRACE_SIZE); + char** messages = backtrace_symbols(trace.data(), size); + for (int i{0}; i < size; i++) { + std::cerr << "[backtrace] " << messages[i] << '\n'; // NOLINT + } +} + +} // namespace reactor +#else +namespace reactor { +inline void print_backtrace() {} +} // namespace reactor +#endif // REACTOR_CPP_BACKTRACE_SUPPORT + namespace reactor { class ValidationError : public std::runtime_error { @@ -48,23 +70,10 @@ public: : std::runtime_error(build_message(msg)) {} }; -#ifdef __linux__ -constexpr std::size_t MAX_STACK_SIZE{10}; - -inline void print_debug_backtrace() { - void* array[10]; // NOLINT - // get void*'s for all entries on the stack - int size = backtrace((void**)array, MAX_STACK_SIZE); - backtrace_symbols_fd((void**)array, size, STDERR_FILENO); -} -#endif - constexpr inline void validate([[maybe_unused]] bool condition, [[maybe_unused]] const std::string_view message) { if constexpr (runtime_validation) { // NOLINT if (!condition) { -#ifdef __linux__ - print_debug_backtrace(); -#endif + print_backtrace(); throw ValidationError(message); } } diff --git a/include/reactor-cpp/config.hh.in b/include/reactor-cpp/config.hh.in index 94b9d2ea..472eff23 100644 --- a/include/reactor-cpp/config.hh.in +++ b/include/reactor-cpp/config.hh.in @@ -10,4 +10,7 @@ // NOLINTNEXTLINE #cmakedefine REACTOR_CPP_LOG_LEVEL @REACTOR_CPP_LOG_LEVEL@ +#cmakedefine REACTOR_CPP_USE_BACKTRACE +#define REACTOR_CPP_BACKTRACE_HEADER <@Backtrace_HEADER@> + #endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index fd47bcfb..43265b9f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -36,6 +36,11 @@ else() target_compile_options(${LIB_TARGET} PRIVATE -Wall -Wextra -pedantic -Werror) endif() +if(${Backtrace_FOUND}) + target_include_directories(${LIB_TARGET} PRIVATE ${Backtrace_INCLUDE_DIRS}) + target_link_libraries(${LIB_TARGET} ${Backtrace_LIBRARY}) +endif() + target_link_libraries(${LIB_TARGET} ${CMAKE_THREAD_LIBS_INIT}) if(REACTOR_CPP_TRACE) target_link_libraries(${LIB_TARGET} LTTng::UST) diff --git a/lib/assert.cc b/lib/assert.cc index 5e6e9584..22403ae2 100644 --- a/lib/assert.cc +++ b/lib/assert.cc @@ -33,9 +33,7 @@ void assert_phase([[maybe_unused]] const ReactorElement* ptr, [[maybe_unused]] P } return "Unknown Phase: Value: " + std::to_string(extract_value(phase)); }; -#ifdef __linux__ - print_debug_backtrace(); -#endif + print_backtrace(); // C++20 std::format throw ValidationError("Expected Phase: " + enum_value_to_name(phase) +