diff --git a/CMakeLists.txt b/CMakeLists.txt index d3e5d09..175bb10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,10 @@ else() check_support(HAS_STACKWALK has_stackwalk.cpp "" "dbghelp" "") endif() +if(NOT WIN32 OR MINGW) + check_support(HAS_CXX_EXCEPTION_TYPE has_cxx_exception_type.cpp "" "" "") +endif() + # =============================================== Autoconfig unwinding =============================================== # Unwind back-ends if( @@ -248,6 +252,10 @@ target_compile_options( # =============================================== Apply options to build =============================================== +if(HAS_CXX_EXCEPTION_TYPE) + target_compile_definitions(cpptrace PUBLIC CPPTRACE_HAS_CXX_EXCEPTION_TYPE) +endif() + function(check_backtrace_error) if(NOT HAS_BACKTRACE) if(NOT "${CPPTRACE_BACKTRACE_PATH}" STREQUAL "") diff --git a/README.md b/README.md index aa8ddcc..27dd819 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ and Windows including MinGW and Cygwin environments. The goal: Make stack traces - [Raw Traces](#raw-traces) - [Utilities](#utilities) - [Traced Exceptions](#traced-exceptions) + - [Exception handling with cpptrace](#exception-handling-with-cpptrace) - [Notable Library Configurations](#notable-library-configurations) - [Notes About the Library and Future Work](#notes-about-the-library-and-future-work) - [FAQ: What about C++23 ``?](#faq-what-about-c23-stacktrace) @@ -226,6 +227,9 @@ performed. `cpptrace::isatty` and the fileno definitions are useful for deciding whether to use color when printing stack taces. +`cpptrace::register_terminate_handler()` is a helper function to set a custom `std::terminate` handler that prints a +stack trace from a cpptrace exception (more info below) and otherwise behaves like the normal terminate handler. + ```cpp namespace cpptrace { std::string demangle(const std::string& name); @@ -236,6 +240,8 @@ namespace cpptrace { extern const int stderr_fileno; extern const int stdout_fileno; + void register_terminate_handler(); + enum class cache_mode { // Only minimal lookup tables prioritize_memory, @@ -300,6 +306,29 @@ namespace cpptrace { } ``` +## Exception handling with cpptrace + +To register a custom handler for `std::terminate` that prints a stack trace from a cpptrace exception and otherwise +behaves like the normal terminate handler. + +```cpp +cpptrace::register_terminate_handler(); +``` + +Working with cpptrace exceptions in your code: +```cpp +try { + foo(); +} catch(cpptrace::exception& e) { + // Prints the exception info and stack trace, conditionally enabling color codes depending on + // whether stderr is a terminal + std::cerr << "Error: " << e.get_raw_what() << '\n'; + e.get_trace().print(std::cerr, cpptrace::isatty(cpptrace::stderr_fileno)); +} catch(std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; +} +``` + ## Notable Library Configurations - `CPPTRACE_STATIC=On/Off`: Create cpptrace as a static library. diff --git a/cmake/has_cxx_exception_type.cpp b/cmake/has_cxx_exception_type.cpp new file mode 100644 index 0000000..a5ea526 --- /dev/null +++ b/cmake/has_cxx_exception_type.cpp @@ -0,0 +1,6 @@ +#include + +int main() { + std::type_info* t = abi::__cxa_current_exception_type(); + (void*) t; +} diff --git a/include/cpptrace/cpptrace.hpp b/include/cpptrace/cpptrace.hpp index c63707c..9d75d74 100644 --- a/include/cpptrace/cpptrace.hpp +++ b/include/cpptrace/cpptrace.hpp @@ -133,6 +133,8 @@ namespace cpptrace { CPPTRACE_API extern const int stderr_fileno; CPPTRACE_API extern const int stdout_fileno; + CPPTRACE_API void register_terminate_handler(); + enum class cache_mode { // Only minimal lookup tables prioritize_memory, diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index beb4010..dbcac95 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -14,8 +14,9 @@ #include "unwind/unwind.hpp" #include "demangle/demangle.hpp" #include "platform/common.hpp" -#include "platform/utils.hpp" +#include "platform/exception_type.hpp" #include "platform/object.hpp" +#include "platform/utils.hpp" #define ESC "\033[" #define RESET ESC "0m" @@ -328,6 +329,34 @@ namespace cpptrace { CPPTRACE_API extern const int stdout_fileno = detail::fileno(stdout); CPPTRACE_API extern const int stderr_fileno = detail::fileno(stderr); + CPPTRACE_API [[noreturn]] void terminate_handler() { + try { + std::rethrow_exception(std::current_exception()); + } catch(cpptrace::exception& e) { + std::cerr << "Terminate called after throwing an instance of " + << cpptrace::demangle(typeid(e).name()) + << ": " + << e.get_raw_what() + << '\n'; + e.get_trace().print(std::cerr, cpptrace::isatty(cpptrace::stderr_fileno)); + } catch(std::exception& e) { + std::cerr << "Terminate called after throwing an instance of " + << cpptrace::demangle(typeid(e).name()) + << ": " + << e.what() + << '\n'; + } catch(...) { + std::cerr << "Terminate called after throwing an instance of " + << detail::exception_type_name() + << "\n"; + } + abort(); + } + + CPPTRACE_API void register_terminate_handler() { + std::set_terminate(terminate_handler); + } + namespace detail { std::atomic_bool absorb_trace_exceptions(true); // NOSONAR std::atomic cache_mode(cache_mode::prioritize_speed); // NOSONAR diff --git a/src/platform/exception_type.hpp b/src/platform/exception_type.hpp new file mode 100644 index 0000000..6d87545 --- /dev/null +++ b/src/platform/exception_type.hpp @@ -0,0 +1,26 @@ +#ifndef EXCEPTION_TYPE_HPP +#define EXCEPTION_TYPE_HPP + +#include + +// libstdc++ and libc++ +#if CPPTRACE_HAS_CXX_EXCEPTION_TYPE && defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_LIBCPP_VERSION) + #include +#endif + +#include "../demangle/demangle.hpp" + +namespace cpptrace { +namespace detail { + inline std::string exception_type_name() { + #if CPPTRACE_HAS_CXX_EXCEPTION_TYPE && defined(__GLIBCXX__) || defined(__GLIBCPP__) || defined(_LIBCPP_VERSION) + std::type_info* t = abi::__cxa_current_exception_type(); + return t ? detail::demangle(t->name()) : ""; + #else + return ""; + #endif + } +} +} + +#endif diff --git a/test/demo.cpp b/test/demo.cpp index c2a3111..228bc1e 100644 --- a/test/demo.cpp +++ b/test/demo.cpp @@ -7,7 +7,7 @@ void trace() { cpptrace::generate_trace().print(); - throw cpptrace::exception_with_message("foobar"); + throw cpptrace::logic_error("foobar"); } void foo(int n) { @@ -31,12 +31,8 @@ void function_one(int) { function_two(0, 0); } -int main() try { +int main() { cpptrace::absorb_trace_exceptions(false); + cpptrace::register_terminate_handler(); function_one(0); -} catch(cpptrace::exception& e) { - std::cerr << "Error: " - << e.get_raw_what() - << '\n'; - e.get_trace().print(std::cerr, cpptrace::isatty(cpptrace::stderr_fileno)); }