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:
parent
927f5ea21e
commit
00d6f1c9d1
@ -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)
|
||||
|
||||
@ -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. 🏗️
|
||||
|
||||

|
||||
|
||||
## 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
BIN
res/screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
50
test/demo.cpp
Normal 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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user