New C api (#80)

Updated C API following the requested scheme. May implement "exceptions"
later...

Closes #38

---------

Co-authored-by: Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com>
This commit is contained in:
eightfold 2024-01-23 01:16:20 -05:00 committed by GitHub
parent cfdd311e15
commit 218957dfb0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 634 additions and 43 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ build*/
repro*/
__pycache__
scratch
.vscode

View File

@ -189,6 +189,7 @@ add_library(cpptrace::cpptrace ALIAS ${target_name})
target_sources(
${target_name} PRIVATE
include/cpptrace/cpptrace.hpp
include/ctrace/ctrace.h
)
# add /src files to target
@ -196,6 +197,7 @@ target_sources(
${target_name} PRIVATE
# src
src/cpptrace.cpp
src/ctrace.cpp
src/demangle/demangle_with_cxxabi.cpp
src/demangle/demangle_with_winapi.cpp
src/demangle/demangle_with_nothing.cpp
@ -519,44 +521,35 @@ endif()
# =============================================== Demo/test ===============================================
macro(add_test_dependencies exec_name)
target_compile_features(${exec_name} PRIVATE cxx_std_11)
target_link_libraries(${exec_name} PRIVATE ${target_name})
# 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(${exec_name} PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
endif()
# TODO: add debug info for mingw clang?
if(CPPTRACE_BUILD_TEST_RDYNAMIC)
set_property(TARGET ${exec_name} PROPERTY ENABLE_EXPORTS ON)
endif()
if(APPLE) # TODO: Temporary
add_custom_command(
TARGET ${exec_name}
POST_BUILD
COMMAND dsymutil $<TARGET_FILE:${exec_name}>
)
endif()
endmacro()
if(CPPTRACE_BUILD_TESTING)
add_executable(test test/test.cpp)
target_compile_features(test PRIVATE cxx_std_11)
target_link_libraries(test PRIVATE ${target_name})
# 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(test PRIVATE "$<$<CONFIG:Debug>:-gdwarf-4>")
endif()
if(CPPTRACE_BUILD_TEST_RDYNAMIC)
set_property(TARGET test PROPERTY ENABLE_EXPORTS ON)
endif()
if(APPLE) # TODO: Temporary
add_custom_command(
TARGET test
POST_BUILD
COMMAND dsymutil $<TARGET_FILE:test>
)
endif()
add_executable(demo test/demo.cpp)
target_compile_features(demo PRIVATE cxx_std_11)
target_link_libraries(demo PRIVATE ${target_name})
# 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()
if(APPLE) # TODO: Temporary
add_custom_command(
TARGET demo
POST_BUILD
COMMAND dsymutil $<TARGET_FILE:demo>
)
endif()
add_executable(c_demo test/ctrace_demo.cpp)
add_test_dependencies(test)
add_test_dependencies(demo)
add_test_dependencies(c_demo)
if(UNIX)
add_executable(signal_demo test/signal_demo.cpp)

View File

@ -431,8 +431,8 @@ namespace cpptrace {
}
```
**Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported
`safe_generate_raw_trace` will just produce an empty trace and if object information can't be resolved in a signal-safe
**Note:** Not all back-ends and platforms support these interfaces. If signal-safe unwinding isn't supported,
`safe_generate_raw_trace` will just produce an empty trace, and if object information can't be resolved in a signal-safe
way then `get_safe_object_frame` will not populate fields beyond the `raw_address`.
**Another big note:** Calls to shared objects can be lazy-loaded where the first call to the shared object invokes

View File

@ -9,7 +9,7 @@
#include <type_traits>
#include <utility>
#include <vector>
// Generated by the build system.
#include <cpptrace/cpptrace_export.hpp>
#if __cplusplus >= 202002L
@ -224,11 +224,11 @@ namespace cpptrace {
enum class cache_mode {
// Only minimal lookup tables
prioritize_memory,
prioritize_memory = 0,
// Build lookup tables but don't keep them around between trace calls
hybrid,
hybrid = 1,
// Build lookup tables as needed
prioritize_speed
prioritize_speed = 2
};
namespace experimental {

141
include/ctrace/ctrace.h Normal file
View File

@ -0,0 +1,141 @@
#ifndef CTRACE_H
#define CTRACE_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#if defined(__cplusplus)
#define CTRACE_BEGIN_DEFINITIONS extern "C" {
#define CTRACE_END_DEFINITIONS }
#else
#define CTRACE_BEGIN_DEFINITIONS
#define CTRACE_END_DEFINITIONS
#endif
#ifdef _MSC_VER
#define CTRACE_FORCE_NO_INLINE __declspec(noinline)
#else
#define CTRACE_FORCE_NO_INLINE __attribute__((noinline))
#endif
#ifdef _MSC_VER
#define CTRACE_FORCE_INLINE __forceinline
#elif defined(__clang__) || defined(__GNUC__)
#define CTRACE_FORCE_INLINE __attribute__((always_inline)) inline
#else
#define CTRACE_FORCE_INLINE inline
#endif
// See `CPPTRACE_PATH_MAX` for more info.
#define CTRACE_PATH_MAX 4096
// TODO: Add exports
CTRACE_BEGIN_DEFINITIONS
typedef struct raw_trace ctrace_raw_trace;
typedef struct object_trace ctrace_object_trace;
typedef struct stacktrace ctrace_stacktrace;
// Represents a boolean value, ensures a consistent ABI.
typedef int8_t ctrace_bool;
// A type that can represent a pointer, alias for `uintptr_t`.
typedef uintptr_t ctrace_frame_ptr;
typedef struct object_frame ctrace_object_frame;
typedef struct stacktrace_frame ctrace_stacktrace_frame;
typedef struct safe_object_frame ctrace_safe_object_frame;
// Type-safe null-terminated string wrapper
typedef struct {
const char* data;
} ctrace_owning_string;
struct object_frame {
ctrace_frame_ptr raw_address;
ctrace_frame_ptr obj_address;
const char* obj_path;
// const char* symbol;
};
struct stacktrace_frame {
ctrace_frame_ptr address;
uint32_t line;
uint32_t column;
const char* filename;
const char* symbol;
ctrace_bool is_inline;
};
struct safe_object_frame {
ctrace_frame_ptr raw_address;
ctrace_frame_ptr relative_obj_address;
char object_path[CTRACE_PATH_MAX + 1];
};
struct raw_trace {
ctrace_frame_ptr* frames;
size_t count;
};
struct object_trace {
ctrace_object_frame* frames;
size_t count;
};
struct stacktrace {
ctrace_stacktrace_frame* frames;
size_t count;
};
typedef enum {
// Only minimal lookup tables
ctrace_prioritize_memory = 0,
// Build lookup tables but don't keep them around between trace calls
ctrace_hybrid = 1,
// Build lookup tables as needed
ctrace_prioritize_speed = 2
} ctrace_cache_mode;
// ctrace::string:
ctrace_owning_string ctrace_generate_owning_string(const char* raw_string);
void ctrace_free_owning_string(ctrace_owning_string* string);
// ctrace::generation:
ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth);
ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth);
ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth);
// ctrace::freeing:
void ctrace_free_raw_trace(ctrace_raw_trace* trace);
void ctrace_free_object_trace(ctrace_object_trace* trace);
void ctrace_free_stacktrace(ctrace_stacktrace* trace);
// ctrace::resolve:
ctrace_stacktrace ctrace_raw_trace_resolve(const ctrace_raw_trace* trace);
ctrace_object_trace ctrace_raw_trace_resolve_object_trace(const ctrace_raw_trace* trace);
ctrace_stacktrace ctrace_object_trace_resolve(const ctrace_object_trace* trace);
// ctrace::safe:
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth);
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out);
// ctrace::io:
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color);
void ctrace_stacktrace_print(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color);
// utility::demangle:
ctrace_owning_string ctrace_demangle(const char* mangled);
// utility::io:
int ctrace_stdin_fileno(void);
int ctrace_stderr_fileno(void);
int ctrace_stdout_fileno(void);
ctrace_bool ctrace_isatty(int fd);
// utility::cache:
void ctrace_set_cache_mode(ctrace_cache_mode mode);
ctrace_cache_mode ctrace_get_cache_mode(void);
CTRACE_END_DEFINITIONS
#endif

398
src/ctrace.cpp Normal file
View File

@ -0,0 +1,398 @@
#include <ctrace/ctrace.h>
#include <cpptrace/cpptrace.hpp>
#include <algorithm>
#include "symbols/symbols.hpp"
#include "unwind/unwind.hpp"
#include "demangle/demangle.hpp"
#include "utils/exception_type.hpp"
#include "utils/common.hpp"
#include "utils/utils.hpp"
#include "binary/object.hpp"
#include "binary/safe_dl.hpp"
#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"
#if defined(__GNUC__) && ((__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))
# define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__)))
#elif defined(__clang__)
// Probably requires llvm >3.5? Not exactly sure.
# define CTRACE_GNU_FORMAT(...) __attribute__((format(__VA_ARGS__)))
#else
# define CTRACE_GNU_FORMAT(...)
#endif
#if defined(__clang__)
# define CTRACE_FORMAT_PROLOGUE \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wformat-security\"")
# define CTRACE_FORMAT_EPILOGUE \
_Pragma("clang diagnostic pop")
#elif defined(__GNUC_MINOR__)
# define CTRACE_FORMAT_PROLOGUE \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wformat-security\"")
# define CTRACE_FORMAT_EPILOGUE \
_Pragma("GCC diagnostic pop")
#else
# define CTRACE_FORMAT_PROLOGUE
# define CTRACE_FORMAT_EPILOGUE
#endif
namespace ctrace {
static constexpr std::uint32_t invalid_pos = ~0U;
CTRACE_FORMAT_PROLOGUE
template <typename...Args>
CTRACE_GNU_FORMAT(printf, 2, 0)
static void ffprintf(std::FILE* f, const char fmt[], Args&&...args) {
(void)std::fprintf(f, fmt, args...);
(void)fflush(f);
}
CTRACE_FORMAT_EPILOGUE
static bool is_empty(std::uint32_t pos) noexcept {
return pos == invalid_pos;
}
static bool is_empty(const char* str) noexcept {
return !str || std::char_traits<char>::length(str) == 0;
}
static ctrace_owning_string generate_owning_string(const char* raw_string) noexcept {
// Returns length to the null terminator.
std::size_t count = std::char_traits<char>::length(raw_string);
char* new_string = new char[count + 1];
std::char_traits<char>::copy(new_string, raw_string, count);
new_string[count] = '\0';
return { new_string };
}
static ctrace_owning_string generate_owning_string(const std::string& std_string) {
return generate_owning_string(std_string.c_str());
}
static void free_owning_string(const char* owned_string) noexcept {
if(!owned_string) return; // Not necessary but eh
delete[] owned_string;
}
static void free_owning_string(ctrace_owning_string& owned_string) noexcept {
free_owning_string(owned_string.data);
}
static ctrace_object_trace c_convert(const std::vector<cpptrace::object_frame>& trace) {
std::size_t count = trace.size();
auto* frames = new ctrace_object_frame[count];
std::transform(
trace.begin(),
trace.end(),
frames,
[] (const cpptrace::object_frame& frame) -> ctrace_object_frame {
const char* new_path = generate_owning_string(frame.object_path).data;
return { frame.raw_address, frame.object_address, new_path };
}
);
return { frames, count };
}
static ctrace_stacktrace c_convert(const std::vector<cpptrace::stacktrace_frame>& trace) {
std::size_t count = trace.size();
auto* frames = new ctrace_stacktrace_frame[count];
std::transform(
trace.begin(),
trace.end(),
frames,
[] (const cpptrace::stacktrace_frame& frame) -> ctrace_stacktrace_frame {
ctrace_stacktrace_frame new_frame;
new_frame.address = frame.address;
new_frame.line = frame.line.value_or(invalid_pos);
new_frame.column = frame.column.value_or(invalid_pos);
new_frame.filename = generate_owning_string(frame.filename).data;
new_frame.symbol = generate_owning_string(cpptrace::detail::demangle(frame.symbol)).data;
new_frame.is_inline = ctrace_bool(frame.is_inline);
return new_frame;
}
);
return { frames, count };
}
static cpptrace::stacktrace cpp_convert(const ctrace_stacktrace* ptrace) {
if(!ptrace || !ptrace->frames) return { };
std::vector<cpptrace::stacktrace_frame> new_frames;
new_frames.reserve(ptrace->count);
for(std::size_t i = 0; i < ptrace->count; ++i) {
using nullable_type = cpptrace::nullable<std::uint32_t>;
static constexpr auto null_v = nullable_type::null().raw_value;
const ctrace_stacktrace_frame& old_frame = ptrace->frames[i];
cpptrace::stacktrace_frame new_frame;
new_frame.address = old_frame.address;
new_frame.line = nullable_type{is_empty(old_frame.line) ? null_v : old_frame.line};
new_frame.column = nullable_type{is_empty(old_frame.column) ? null_v : old_frame.column};
new_frame.filename = old_frame.filename;
new_frame.symbol = old_frame.symbol;
new_frame.is_inline = bool(old_frame.is_inline);
new_frames.push_back(std::move(new_frame));
}
return cpptrace::stacktrace{std::move(new_frames)};
}
}
extern "C" {
// ctrace::string
ctrace_owning_string ctrace_generate_owning_string(const char* raw_string) {
return ctrace::generate_owning_string(raw_string);
}
void ctrace_free_owning_string(ctrace_owning_string* string) {
if(!string) return;
ctrace::free_owning_string(*string);
string->data = nullptr;
}
// ctrace::generation:
CTRACE_FORCE_NO_INLINE
ctrace_raw_trace ctrace_generate_raw_trace(size_t skip, size_t max_depth) {
try {
std::vector<cpptrace::frame_ptr> trace = cpptrace::detail::capture_frames(skip + 1, max_depth);
std::size_t count = trace.size();
auto* frames = new ctrace_frame_ptr[count];
std::copy(trace.data(), trace.data() + count, frames);
return { frames, count };
} catch(...) {
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
CTRACE_FORCE_NO_INLINE
ctrace_object_trace ctrace_generate_object_trace(size_t skip, size_t max_depth) {
try {
std::vector<cpptrace::object_frame> trace = cpptrace::detail::get_frames_object_info(
cpptrace::detail::capture_frames(skip + 1, max_depth)
);
return ctrace::c_convert(trace);
} catch(...) { // NOSONAR
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
CTRACE_FORCE_NO_INLINE
ctrace_stacktrace ctrace_generate_trace(size_t skip, size_t max_depth) {
try {
std::vector<cpptrace::frame_ptr> frames = cpptrace::detail::capture_frames(skip + 1, max_depth);
std::vector<cpptrace::stacktrace_frame> trace = cpptrace::detail::resolve_frames(frames);
return ctrace::c_convert(trace);
} catch(...) { // NOSONAR
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
// ctrace::freeing:
void ctrace_free_raw_trace(ctrace_raw_trace* trace) {
if(!trace) return;
ctrace_frame_ptr* frames = trace->frames;
delete[] frames;
trace->frames = nullptr;
trace->count = 0;
}
void ctrace_free_object_trace(ctrace_object_trace* trace) {
if(!trace || !trace->frames) return;
ctrace_object_frame* frames = trace->frames;
for(std::size_t i = 0; i < trace->count; ++i) {
const char* path = frames[i].obj_path;
ctrace::free_owning_string(path);
}
delete[] frames;
trace->frames = nullptr;
trace->count = 0;
}
void ctrace_free_stacktrace(ctrace_stacktrace* trace) {
if(!trace || !trace->frames) return;
ctrace_stacktrace_frame* frames = trace->frames;
for(std::size_t i = 0; i < trace->count; ++i) {
ctrace::free_owning_string(frames[i].filename);
ctrace::free_owning_string(frames[i].symbol);
}
delete[] frames;
trace->frames = nullptr;
trace->count = 0;
}
// ctrace::resolve:
ctrace_stacktrace ctrace_raw_trace_resolve(const ctrace_raw_trace* trace) {
if(!trace || !trace->frames) return { nullptr, 0 };
try {
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
std::copy(trace->frames, trace->frames + trace->count, frames.begin());
std::vector<cpptrace::stacktrace_frame> resolved = cpptrace::detail::resolve_frames(frames);
return ctrace::c_convert(resolved);
} catch(...) { // NOSONAR
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
ctrace_object_trace ctrace_raw_trace_resolve_object_trace(const ctrace_raw_trace* trace) {
if(!trace || !trace->frames) return { nullptr, 0 };
try {
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
std::copy(trace->frames, trace->frames + trace->count, frames.begin());
std::vector<cpptrace::object_frame> obj = cpptrace::detail::get_frames_object_info(frames);
return ctrace::c_convert(obj);
} catch(...) { // NOSONAR
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
ctrace_stacktrace ctrace_object_trace_resolve(const ctrace_object_trace* trace) {
if(!trace || !trace->frames) return { nullptr, 0 };
try {
std::vector<cpptrace::frame_ptr> frames(trace->count, 0);
std::transform(
trace->frames,
trace->frames + trace->count,
frames.begin(),
[] (const ctrace_object_frame& frame) -> cpptrace::frame_ptr {
return frame.raw_address;
}
);
std::vector<cpptrace::stacktrace_frame> resolved = cpptrace::detail::resolve_frames(frames);
return ctrace::c_convert(resolved);
} catch(...) { // NOSONAR
// Don't check rethrow condition, it's risky.
return { nullptr, 0 };
}
}
// ctrace::safe:
size_t ctrace_safe_generate_raw_trace(ctrace_frame_ptr* buffer, size_t size, size_t skip, size_t max_depth) {
return cpptrace::safe_generate_raw_trace(buffer, size, skip, max_depth);
}
void ctrace_get_safe_object_frame(ctrace_frame_ptr address, ctrace_safe_object_frame* out) {
// TODO: change this?
static_assert(sizeof(cpptrace::safe_object_frame) == sizeof(ctrace_safe_object_frame), "");
cpptrace::get_safe_object_frame(address, reinterpret_cast<cpptrace::safe_object_frame*>(out));
}
// ctrace::io:
ctrace_owning_string ctrace_stacktrace_to_string(const ctrace_stacktrace* trace, ctrace_bool use_color) {
if(!trace || !trace->frames) return ctrace::generate_owning_string("<empty trace>");
auto cpp_trace = ctrace::cpp_convert(trace);
std::string trace_string = cpp_trace.to_string(bool(use_color));
return ctrace::generate_owning_string(trace_string);
}
void ctrace_stacktrace_print(const ctrace_stacktrace* trace, FILE* to, ctrace_bool use_color) {
if(use_color) cpptrace::detail::enable_virtual_terminal_processing_if_needed();
ctrace::ffprintf(to, "Stack trace (most recent call first):\n");
if(trace->count == 0 || !trace->frames) {
ctrace::ffprintf(to, "<empty trace>\n");
return;
}
const auto reset = use_color ? ESC "0m" : "";
const auto green = use_color ? ESC "32m" : "";
const auto yellow = use_color ? ESC "33m" : "";
const auto blue = use_color ? ESC "34m" : "";
const auto frame_number_width = cpptrace::detail::n_digits(unsigned(trace->count - 1));
ctrace_stacktrace_frame* frames = trace->frames;
for(std::size_t i = 0; i < trace->count; ++i) {
static constexpr auto ptr_len = 2 * sizeof(cpptrace::frame_ptr);
ctrace::ffprintf(to, "#%-*llu ", int(frame_number_width), i);
if(frames[i].is_inline) {
(void)std::fprintf(to, "%*s",
int(ptr_len + 2),
"(inlined)");
} else {
(void)std::fprintf(to, "%s0x%0*llx%s",
blue,
int(ptr_len),
cpptrace::detail::to_ull(frames[i].address),
reset);
}
if(!ctrace::is_empty(frames[i].symbol)) {
(void)std::fprintf(to, " in %s%s%s",
yellow,
frames[i].symbol,
reset);
}
if(!ctrace::is_empty(frames[i].filename)) {
(void)std::fprintf(to, " at %s%s%s",
green,
frames[i].filename,
reset);
if(ctrace::is_empty(frames[i].line)) {
ctrace::ffprintf(to, "\n");
continue;
}
(void)std::fprintf(to, ":%s%llu%s",
blue,
cpptrace::detail::to_ull(frames[i].line),
reset);
if(ctrace::is_empty(frames[i].column)) {
ctrace::ffprintf(to, "\n");
continue;
}
(void)std::fprintf(to, ":%s%llu%s",
blue,
cpptrace::detail::to_ull(frames[i].column),
reset);
}
// always print newline at end :M
ctrace::ffprintf(to, "\n");
}
}
// utility::demangle:
ctrace_owning_string ctrace_demangle(const char* mangled) {
if(!mangled) return ctrace::generate_owning_string("");
std::string demangled = cpptrace::demangle(mangled);
return ctrace::generate_owning_string(demangled);
}
// utility::io
int ctrace_stdin_fileno(void) {
return cpptrace::stdin_fileno;
}
int ctrace_stderr_fileno(void) {
return cpptrace::stderr_fileno;
}
int ctrace_stdout_fileno(void) {
return cpptrace::stdout_fileno;
}
ctrace_bool ctrace_isatty(int fd) {
return cpptrace::isatty(fd);
}
// utility::cache:
void ctrace_set_cache_mode(ctrace_cache_mode mode) {
static constexpr auto cache_max = cpptrace::cache_mode::prioritize_speed;
if(mode > unsigned(cache_max)) return;
auto cache_mode = static_cast<cpptrace::cache_mode>(mode);
cpptrace::experimental::set_cache_mode(cache_mode);
}
ctrace_cache_mode ctrace_get_cache_mode(void) {
auto cache_mode = cpptrace::detail::get_cache_mode();
return static_cast<ctrace_cache_mode>(cache_mode);
}
}

View File

@ -345,7 +345,7 @@ namespace dbghelp {
std::fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
return {
addr,
static_cast<std::uint32_t>(line.LineNumber),
{ static_cast<std::uint32_t>(line.LineNumber) },
nullable<std::uint32_t>::null(),
line.FileName,
symbol->Name,
@ -377,7 +377,7 @@ namespace dbghelp {
signature = std::regex_replace(signature, comma_re, ", ");
return {
addr,
static_cast<std::uint32_t>(line.LineNumber),
{ static_cast<std::uint32_t>(line.LineNumber) },
nullable<std::uint32_t>::null(),
line.FileName,
signature,

58
test/ctrace_demo.cpp Normal file
View File

@ -0,0 +1,58 @@
#include <ctrace/ctrace.h>
#include <cassert>
#include <climits>
#include <cstdio>
void test_linker() {
/* Owning String */ {
auto str = ctrace_generate_owning_string("Hello C!");
std::printf("%s\n", str.data);
ctrace_free_owning_string(&str);
assert(str.data == nullptr);
} /* Trace */ {
ctrace_stacktrace trace = ctrace_generate_trace(0, INT_MAX);
ctrace_owning_string str = ctrace_stacktrace_to_string(&trace, 0);
ctrace_free_stacktrace(&trace);
assert(trace.count == 0);
std::printf("%s\n", str.data);
ctrace_free_owning_string(&str);
}
}
void trace() {
ctrace_raw_trace raw_trace = ctrace_generate_raw_trace(1, INT_MAX);
ctrace_object_trace obj_trace = ctrace_raw_trace_resolve_object_trace(&raw_trace);
ctrace_stacktrace trace = ctrace_object_trace_resolve(&obj_trace);
ctrace_stacktrace_print(&trace, stdout, 1);
ctrace_free_stacktrace(&trace);
ctrace_free_object_trace(&obj_trace);
ctrace_free_raw_trace(&raw_trace);
assert(raw_trace.frames == nullptr && obj_trace.count == 0);
}
void foo(int n) {
if(n == 0) {
trace();
} else {
foo(n - 1);
}
}
template<typename... Args>
void foo(int x, Args... args) {
foo(args...);
}
void function_two(int, float) {
foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
}
CTRACE_FORCE_INLINE
void function_one(int) {
function_two(0, 0);
}
int main() {
test_linker();
function_one(0);
}