diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dc280a..d69bac9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ option(CPPTRACE_DEMANGLE_WITH_CXXABI "" OFF) option(CPPTRACE_DEMANGLE_WITH_NOTHING "" OFF) option(CPPTRACE_BUILD_TEST "" OFF) +option(CPPTRACE_BUILD_DEMO "" OFF) option(CPPTRACE_BUILD_TEST_RDYNAMIC "" OFF) option(CPPTRACE_BUILD_SPEEDTEST "" OFF) @@ -408,6 +409,19 @@ if(CPPTRACE_BUILD_TEST) endif() endif() +if(CPPTRACE_BUILD_DEMO) + add_executable(demo test/demo.cpp) + target_link_libraries(demo PRIVATE cpptrace) + # Clang has been fast to adopt dwarf 5, other tools (e.g. addr2line from binutils) have not + check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4) + if(HAS_DWARF4) + target_compile_options(demo PRIVATE "$<$:-gdwarf-4>") + endif() + if(CPPTRACE_BUILD_TEST_RDYNAMIC) + set_property(TARGET demo PROPERTY ENABLE_EXPORTS ON) + endif() +endif() + if(CPPTRACE_BUILD_SPEEDTEST) if(CPPTRACE_BUILD_SPEEDTEST_DWARF4) check_cxx_compiler_flag("-gdwarf-4" HAS_DWARF4) diff --git a/README.md b/README.md index 7295284..62b3e1d 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Some day C++23's `` will be ubiquitous. And maybe one day the msvc i 🚧 WIP: This library is in beta. 🏗️ +![Screenshot](res/screenshot.png) + ## Table of contents - [Cpptrace](#cpptrace) @@ -187,9 +189,10 @@ General: `100`. Testing: -- `CPPTRACE_BUILD_TEST` -- `CPPTRACE_BUILD_TEST_RDYNAMIC` -- `CPPTRACE_BUILD_SPEEDTEST` +- `CPPTRACE_BUILD_TEST` Build a small test program +- `CPPTRACE_BUILD_DEMO` Build a small demo program +- `CPPTRACE_BUILD_TEST_RDYNAMIC` Use `-rdynamic` when compiling the test program +- `CPPTRACE_BUILD_SPEEDTEST` Build a small speed test program - `CPPTRACE_BUILD_SPEEDTEST_DWARF4` - `CPPTRACE_BUILD_SPEEDTEST_DWARF5` diff --git a/res/screenshot.png b/res/screenshot.png new file mode 100644 index 0000000..27ad81b Binary files /dev/null and b/res/screenshot.png differ diff --git a/src/cpptrace.cpp b/src/cpptrace.cpp index eee4776..dabc627 100644 --- a/src/cpptrace.cpp +++ b/src/cpptrace.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #if !(defined(CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE) || defined(CPPTRACE_FULL_TRACE_WITH_STACKTRACE)) @@ -46,23 +47,55 @@ namespace cpptrace { #endif +#define ESC "\033[" +#define RESET ESC "0m" +#define RED ESC "31m" +#define GREEN ESC "32m" +#define YELLOW ESC "33m" +#define BLUE ESC "34m" +#define MAGENTA ESC "35m" +#define CYAN ESC "36m" + namespace cpptrace { void print_trace(std::uint32_t skip) { + enable_virtual_terminal_processing_if_needed(); std::cerr<<"Stack trace (most recent call first):"<"<(trace.size()) - 1); + for(const auto& frame : trace) { std::cerr + << '#' + << std::setw(static_cast(frame_number_width)) + << std::left << counter++ << " " - << frame.filename - << ":" - << frame.line - << (frame.col > 0 ? ":" + std::to_string(frame.col) : "") - << " " + << std::hex + << BLUE + << "0x" + << std::setw(2 * sizeof(uintptr_t)) + << std::setfill('0') + << frame.address + << std::dec + << std::setfill(' ') + << RESET + << " in " + << YELLOW << frame.symbol + << RESET + << " at " + << GREEN + << frame.filename + << RESET + << ":" + << BLUE + << frame.line + << RESET + << (frame.col > 0 ? ":" BLUE + std::to_string(frame.col) + RESET : "") << std::endl; } } diff --git a/src/full/full_trace_with_libbacktrace.cpp b/src/full/full_trace_with_libbacktrace.cpp index c7f6791..2713a42 100644 --- a/src/full/full_trace_with_libbacktrace.cpp +++ b/src/full/full_trace_with_libbacktrace.cpp @@ -28,6 +28,9 @@ namespace cpptrace { trace_data& data = *reinterpret_cast(data_pointer); if(data.skip > 0) { data.skip--; + } else if(address == uintptr_t(-1)) { + // sentinel for libbacktrace, stop tracing + return 1; } else { data.frames.push_back({ address, diff --git a/src/platform/cpptrace_common.hpp b/src/platform/cpptrace_common.hpp index 750e4a1..a744db3 100644 --- a/src/platform/cpptrace_common.hpp +++ b/src/platform/cpptrace_common.hpp @@ -57,6 +57,10 @@ #error "Unsupported compiler" #endif +#if IS_WINDOWS + #include +#endif + // Lightweight std::source_location. struct source_location { // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) @@ -218,6 +222,34 @@ T byteswap(T value) { return byte_swapper{}(value); } +CPPTRACE_MAYBE_UNUSED +inline void enable_virtual_terminal_processing_if_needed() { + // enable colors / ansi processing if necessary + #if IS_WINDOWS + // https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing + #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING + constexpr DWORD ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4; + #endif + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + if(hOut == INVALID_HANDLE_VALUE) return; + if(!GetConsoleMode(hOut, &dwMode)) return; + if(dwMode != (dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if(!SetConsoleMode(hOut, dwMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return; + #endif +} + +CPPTRACE_MAYBE_UNUSED +// NOLINTNEXTLINE(misc-no-recursion) +inline constexpr unsigned n_digits(unsigned value) { + return value < 10 ? 1 : 1 + n_digits(value / 10); +} +static_assert(n_digits(1) == 1, "n_digits utility producing the wrong result"); +static_assert(n_digits(9) == 1, "n_digits utility producing the wrong result"); +static_assert(n_digits(10) == 2, "n_digits utility producing the wrong result"); +static_assert(n_digits(11) == 2, "n_digits utility producing the wrong result"); +static_assert(n_digits(1024) == 4, "n_digits utility producing the wrong result"); + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/src/symbols/symbols_with_addr2line.cpp b/src/symbols/symbols_with_addr2line.cpp index db2eca6..e0eac4f 100644 --- a/src/symbols/symbols_with_addr2line.cpp +++ b/src/symbols/symbols_with_addr2line.cpp @@ -291,6 +291,9 @@ namespace cpptrace { std::vector resolve_frames(const std::vector& frames) { // TODO: Refactor better std::vector trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); + for(size_t i = 0; i < frames.size(); i++) { + trace[i].address = reinterpret_cast(frames[i]); + } if(has_addr2line()) { const std::vector dlframes = backtrace_frames(frames); const auto entries = get_addr2line_targets(dlframes, trace); diff --git a/test/demo.cpp b/test/demo.cpp new file mode 100644 index 0000000..1d688ac --- /dev/null +++ b/test/demo.cpp @@ -0,0 +1,50 @@ +#include + +#include +#include +#include +#include + +void trace() { + cpptrace::print_trace(); +} + +int x; + +void foo(int n) { + if(n == 0) { + x = 0; + trace(); + x = 0; + } else { + x = 0; + foo(n - 1); + x = 0; + } +} + +template +void foo(int x, Args... args) { + x = 0; + foo(args...); + x = 0; +} + +void function_two(int, float) { + x = 0; + foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + x = 0; +} + +void function_one(int) { + x = 0; + function_two(0, 0); + x = 0; +} + +int main() { + x = 0; + function_one(0); + x = 0; +} +