Update how stacktraces are printed, add color, add a demo program, add a screenshot to the readme, and two small fixes to backends

This commit is contained in:
Jeremy 2023-07-22 21:31:31 -04:00
parent 927f5ea21e
commit 00d6f1c9d1
No known key found for this signature in database
GPG Key ID: 19AA8270105E8EB4
8 changed files with 149 additions and 11 deletions

View File

@ -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 "$<$<CONFIG:Debug>:-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)

View File

@ -14,6 +14,8 @@ Some day C++23's `<stacktrace>` 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`

BIN
res/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -4,6 +4,7 @@
#include <cstdint>
#include <string>
#include <vector>
#include <iomanip>
#include <iostream>
#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):"<<std::endl;
std::size_t counter = 0;
const auto trace = generate_trace(skip + 1);
// +1 to skip one frame
for(auto it = trace.begin() + 1; it != trace.end(); it++) {
const auto& frame = *it;
if(trace.empty()) {
std::cerr<<"<empty trace>"<<std::endl;
return;
}
const auto frame_number_width = n_digits(static_cast<int>(trace.size()) - 1);
for(const auto& frame : trace) {
std::cerr
<< '#'
<< std::setw(static_cast<int>(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;
}
}

View File

@ -28,6 +28,9 @@ namespace cpptrace {
trace_data& data = *reinterpret_cast<trace_data*>(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,

View File

@ -57,6 +57,10 @@
#error "Unsupported compiler"
#endif
#if IS_WINDOWS
#include <windows.h>
#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<T, sizeof(T)>{}(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

View File

@ -291,6 +291,9 @@ namespace cpptrace {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) {
// TODO: Refactor better
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
for(size_t i = 0; i < frames.size(); i++) {
trace[i].address = reinterpret_cast<uintptr_t>(frames[i]);
}
if(has_addr2line()) {
const std::vector<dlframe> dlframes = backtrace_frames(frames);
const auto entries = get_addr2line_targets(dlframes, trace);

50
test/demo.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <cpptrace/cpptrace.hpp>
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
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<typename... Args>
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;
}