Expand cpptrace API (#37)

This commit is contained in:
Jeremy Rifkin 2023-09-18 20:33:46 -04:00 committed by GitHub
parent 43a50c734c
commit 0b32df64e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 513 additions and 126 deletions

View File

@ -1,5 +1,5 @@
--- ---
Checks: '-*,clang-diagnostic-*,clang-analyzer-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-cppcoreguidelines-macro-usage,-modernize-use-trailing-return-type,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-avoid-c-arrays,-modernize-avoid-c-arrays,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-else-after-return,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cert-dcl50-cpp,-cppcoreguidelines-init-variables,-readability-implicit-bool-conversion,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-type-member-init,-readability-isolate-declaration,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-cstyle-cast,-readability-named-parameter,-cppcoreguidelines-avoid-goto,-readability-uppercase-literal-suffix,-performance-avoid-endl,-bugprone-easily-swappable-parameters' Checks: '-*,clang-diagnostic-*,clang-analyzer-*,bugprone-*,cert-*,clang-analyzer-*,concurrency-*,cppcoreguidelines-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-cppcoreguidelines-macro-usage,-modernize-use-trailing-return-type,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-avoid-c-arrays,-modernize-avoid-c-arrays,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-else-after-return,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cert-dcl50-cpp,-cppcoreguidelines-init-variables,-readability-implicit-bool-conversion,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-type-member-init,-readability-isolate-declaration,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-cppcoreguidelines-pro-type-union-access,-cppcoreguidelines-pro-type-cstyle-cast,-readability-named-parameter,-cppcoreguidelines-avoid-goto,-readability-uppercase-literal-suffix,-performance-avoid-endl,-bugprone-easily-swappable-parameters,-cppcoreguidelines-non-private-member-variables-in-classes'
WarningsAsErrors: '*' WarningsAsErrors: '*'
HeaderFilterRegex: '' HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false AnalyzeTemporaryDtors: false

120
README.md
View File

@ -46,14 +46,40 @@ Generating traces is as easy as calling `cpptrace::print_trace`:
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
void trace() { void trace() {
cpptrace::print_trace(); cpptrace::generate_trace().print();
} }
// ... /* other stuff */
``` ```
![Screenshot](res/screenshot.png) ![Screenshot](res/screenshot.png)
Cpptrace provides access to resolved stack traces as well as lightweight raw traces (just addresses) that can be
resolved later:
```cpp
const auto raw_trace = cpptrace::generate_raw_trace();
// then later
raw_trace.resolve().print();
```
Cpptrace also provides exception types that store stack traces:
```cpp
#include <cpptrace/cpptrace.hpp>
void trace() {
throw cpptrace::exception();
}
/* other stuff */
// terminate called after throwing an instance of 'cpptrace::exception'
// what(): cpptrace::exception:
// Stack trace (most recent call first):
// #0 0x00005641c715a1b6 in trace() at demo.cpp:9
// #1 0x00005641c715a229 in foo(int) at demo.cpp:16
// #2 0x00005641c715a2ba in main at demo.cpp:34
```
## CMake FetchContent Usage ## CMake FetchContent Usage
```cmake ```cmake
@ -73,8 +99,9 @@ On windows and macos some extra work is required, see [below](#platform-logistic
## API ## API
`cpptrace::print_trace()` can be used to print a stacktrace at the current call site, `cpptrace::generate_trace()` can `cpptrace::generate_trace()` can used to generate a stacktrace object at the current call site. Resolved frames can be
be used to get raw frame information for custom use. accessed from this object with `.frames` and also the trace can be printed with `.print()`. Cpptrace also provides a
method to get lightweight raw traces, which are just vectors of program counters, which can be resolved at a later time.
**Note:** Debug info (`-g`/`/Z7`/`/Zi`/`/DEBUG`) is generally required for good trace information. **Note:** Debug info (`-g`/`/Z7`/`/Zi`/`/DEBUG`) is generally required for good trace information.
@ -83,15 +110,96 @@ for generating these is included above.
```cpp ```cpp
namespace cpptrace { namespace cpptrace {
/*
* Raw trace access
*/
struct raw_trace {
std::vector<uintptr_t> frames;
explicit raw_trace(std::vector<uintptr_t>&& frames);
object_trace resolve_object_trace() const;
stacktrace resolve() const;
void clear();
/* iterators exist for this object */
};
/*
* Object trace with object file information for each frame, any accessible symbol information, and the address in
* the object file for the frame's program counter.
*/
struct object_frame {
std::string obj_path;
std::string symbol;
uintptr_t raw_address = 0;
uintptr_t obj_address = 0;
};
struct object_trace {
std::vector<object_frame> frames;
explicit object_trace(std::vector<object_frame>&& frames);
stacktrace resolve() const;
void clear();
/* iterators exist for this object */
};
/*
* Resolved stacktrace object.
*/
struct stacktrace_frame { struct stacktrace_frame {
uintptr_t address; uintptr_t address;
std::uint_least32_t line; std::uint_least32_t line;
std::uint_least32_t col; std::uint_least32_t col;
std::string filename; std::string filename;
std::string symbol; std::string symbol;
bool operator==(const stacktrace_frame& other) const;
bool operator!=(const stacktrace_frame& other) const;
}; };
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
void print_trace(std::uint32_t skip = 0); struct stacktrace {
std::vector<stacktrace_frame> frames;
explicit stacktrace(std::vector<stacktrace_frame>&& frames);
void print() const;
void print(std::ostream& stream) const;
void print(std::ostream& stream, bool color) const;
std::string to_string() const;
void clear();
/* iterators exist for this object */
};
/*
* Trace generation
*/
raw_trace generate_raw_trace(std::uint32_t skip = 0);
object_trace generate_object_trace(std::uint32_t skip = 0);
stacktrace generate_trace(std::uint32_t skip = 0);
/*
* Utilities
*/
std::string demangle(const std::string& name);
// Traced exception class
class exception : public std::exception {
public:
explicit exception();
const char* what() const noexcept override;
};
class exception_with_message : public exception {
public:
explicit exception_with_message(std::string&& message_arg);
const char* what() const noexcept override;
};
// All stdexcept errors have analogs here. Same constructor as exception_with_message.
class logic_error : exception_with_message { ... };
class domain_error : exception_with_message { ... };
class invalid_argument : exception_with_message { ... };
class length_error : exception_with_message { ... };
class out_of_range : exception_with_message { ... };
class runtime_error : exception_with_message { ... };
class range_error : exception_with_message { ... };
class overflow_error : exception_with_message { ... };
class underflow_error : exception_with_message { ... };
} }
``` ```

View File

@ -2,6 +2,8 @@
#define CPPTRACE_HPP #define CPPTRACE_HPP
#include <cstdint> #include <cstdint>
#include <exception>
#include <ostream>
#include <string> #include <string>
#include <vector> #include <vector>
@ -12,15 +14,171 @@
#endif #endif
namespace cpptrace { namespace cpptrace {
struct object_trace;
struct stacktrace;
struct raw_trace {
std::vector<uintptr_t> frames;
explicit raw_trace(std::vector<uintptr_t>&& frames_) : frames(frames_) {}
CPPTRACE_API object_trace resolve_object_trace() const;
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
using iterator = std::vector<uintptr_t>::iterator;
using const_iterator = std::vector<uintptr_t>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct object_frame {
std::string obj_path;
std::string symbol;
uintptr_t raw_address = 0;
uintptr_t obj_address = 0;
};
struct object_trace {
std::vector<object_frame> frames;
explicit object_trace(std::vector<object_frame>&& frames_) : frames(frames_) {}
CPPTRACE_API stacktrace resolve() const;
CPPTRACE_API void clear();
using iterator = std::vector<object_frame>::iterator;
using const_iterator = std::vector<object_frame>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
};
struct stacktrace_frame { struct stacktrace_frame {
uintptr_t address; uintptr_t address;
std::uint_least32_t line; std::uint_least32_t line;
std::uint_least32_t col; std::uint_least32_t col;
std::string filename; std::string filename;
std::string symbol; std::string symbol;
bool operator==(const stacktrace_frame& other) const {
return address == other.address
&& line == other.line
&& col == other.col
&& filename == other.filename
&& symbol == other.symbol;
}
bool operator!=(const stacktrace_frame& other) const {
return !operator==(other);
}
};
struct stacktrace {
std::vector<stacktrace_frame> frames;
explicit stacktrace(std::vector<stacktrace_frame>&& frames_) : frames(frames_) {}
CPPTRACE_API void print() const;
CPPTRACE_API void print(std::ostream& stream) const;
CPPTRACE_API void print(std::ostream& stream, bool color) const;
CPPTRACE_API std::string to_string() const;
CPPTRACE_API void clear();
using iterator = std::vector<stacktrace_frame>::iterator;
using const_iterator = std::vector<stacktrace_frame>::const_iterator;
inline iterator begin() noexcept { return frames.begin(); }
inline const_iterator cbegin() const noexcept { return frames.cbegin(); }
inline iterator end() noexcept { return frames.end(); }
inline const_iterator cend() const noexcept { return frames.cend(); }
};
CPPTRACE_API raw_trace generate_raw_trace(std::uint32_t skip = 0);
CPPTRACE_API object_trace generate_object_trace(std::uint32_t skip = 0);
CPPTRACE_API stacktrace generate_trace(std::uint32_t skip = 0);
// utilities:
CPPTRACE_API std::string demangle(const std::string& name);
class exception : public std::exception {
protected:
mutable raw_trace trace;
mutable std::string resolved_message;
explicit exception(uint32_t skip) : trace(generate_raw_trace(skip + 1)) {}
virtual const std::string& get_resolved_message() const {
if(resolved_message.empty()) {
resolved_message = "cpptrace::exception:\n" + trace.resolve().to_string();
trace.clear();
}
return resolved_message;
}
public:
explicit exception() : exception(1) {}
const char* what() const noexcept override {
return get_resolved_message().c_str();
}
};
class exception_with_message : public exception {
protected:
mutable std::string message;
explicit exception_with_message(std::string&& message_arg, uint32_t skip)
: exception(skip + 1), message(std::move(message_arg)) {}
const std::string& get_resolved_message() const override {
if(resolved_message.empty()) {
resolved_message = message + "\n" + trace.resolve().to_string();
trace.clear();
message.clear();
}
return resolved_message;
}
public:
explicit exception_with_message(std::string&& message_arg)
: exception_with_message(std::move(message_arg), 1) {}
const char* what() const noexcept override {
return get_resolved_message().c_str();
}
};
class logic_error : public exception_with_message {
public:
explicit logic_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class domain_error : public exception_with_message {
public:
explicit domain_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class invalid_argument : public exception_with_message {
public:
explicit invalid_argument(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class length_error : public exception_with_message {
public:
explicit length_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class out_of_range : public exception_with_message {
public:
explicit out_of_range(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class runtime_error : public exception_with_message {
public:
explicit runtime_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class range_error : public exception_with_message {
public:
explicit range_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class overflow_error : public exception_with_message {
public:
explicit overflow_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
};
class underflow_error : public exception_with_message {
public:
explicit underflow_error(std::string&& message_arg) : exception_with_message(std::move(message_arg), 1) {}
}; };
CPPTRACE_API std::vector<stacktrace_frame> generate_trace(std::uint32_t skip = 0);
CPPTRACE_API void print_trace(std::uint32_t skip = 0);
} }
#endif #endif

View File

@ -2,16 +2,18 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <string>
#include <vector>
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "symbols/symbols.hpp" #include "symbols/symbols.hpp"
#include "unwind/unwind.hpp" #include "unwind/unwind.hpp"
#include "demangle/demangle.hpp" #include "demangle/demangle.hpp"
#include "platform/common.hpp" #include "platform/common.hpp"
#include "platform/utils.hpp" #include "platform/utils.hpp"
#include "platform/object.hpp"
#define ESC "\033[" #define ESC "\033["
#define RESET ESC "0m" #define RESET ESC "0m"
@ -23,29 +25,59 @@
#define CYAN ESC "36m" #define CYAN ESC "36m"
namespace cpptrace { namespace cpptrace {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API CPPTRACE_API
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) { object_trace raw_trace::resolve_object_trace() const {
std::vector<void*> frames = detail::capture_frames(skip + 1); return object_trace(detail::get_frames_object_info(frames));
}
CPPTRACE_API
stacktrace raw_trace::resolve() const {
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames); std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) { for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol); frame.symbol = detail::demangle(frame.symbol);
} }
return trace; return stacktrace(std::move(trace));
} }
CPPTRACE_API CPPTRACE_API
void print_trace(std::uint32_t skip) { void raw_trace::clear() {
detail::enable_virtual_terminal_processing_if_needed(); frames.clear();
std::cerr<<"Stack trace (most recent call first):"<<std::endl; }
CPPTRACE_API
stacktrace object_trace::resolve() const {
return stacktrace(detail::resolve_frames(frames));
}
CPPTRACE_API
void object_trace::clear() {
frames.clear();
}
CPPTRACE_API
void stacktrace::print() const {
print(std::cerr, true);
}
CPPTRACE_API
void stacktrace::print(std::ostream& stream) const {
print(stream, true);
}
CPPTRACE_API
void stacktrace::print(std::ostream& stream, bool color) const {
if(color) {
detail::enable_virtual_terminal_processing_if_needed();
}
stream<<"Stack trace (most recent call first):"<<std::endl;
std::size_t counter = 0; std::size_t counter = 0;
const auto trace = generate_trace(skip + 1); if(frames.empty()) {
if(trace.empty()) { stream<<"<empty trace>"<<std::endl;
std::cerr<<"<empty trace>"<<std::endl;
return; return;
} }
const auto frame_number_width = detail::n_digits(static_cast<int>(trace.size()) - 1); const auto frame_number_width = detail::n_digits(static_cast<int>(frames.size()) - 1);
for(const auto& frame : trace) { for(const auto& frame : frames) {
std::cerr stream
<< '#' << '#'
<< std::setw(static_cast<int>(frame_number_width)) << std::setw(static_cast<int>(frame_number_width))
<< std::left << std::left
@ -53,28 +85,65 @@ namespace cpptrace {
<< std::right << std::right
<< " " << " "
<< std::hex << std::hex
<< BLUE << (color ? BLUE : "")
<< "0x" << "0x"
<< std::setw(2 * sizeof(uintptr_t)) << std::setw(2 * sizeof(uintptr_t))
<< std::setfill('0') << std::setfill('0')
<< frame.address << frame.address
<< std::dec << std::dec
<< std::setfill(' ') << std::setfill(' ')
<< RESET << (color ? RESET : "")
<< " in " << " in "
<< YELLOW << (color ? YELLOW : "")
<< frame.symbol << frame.symbol
<< RESET << (color ? RESET : "")
<< " at " << " at "
<< GREEN << (color ? GREEN : "")
<< frame.filename << frame.filename
<< RESET << (color ? RESET : "")
<< ":" << ":"
<< BLUE << (color ? BLUE : "")
<< frame.line << frame.line
<< RESET << (color ? RESET : "")
<< (frame.col > 0 ? ":" BLUE + std::to_string(frame.col) + RESET : "") << (frame.col > 0 ? (color ? ":" BLUE : ":") + std::to_string(frame.col) + (color ? RESET : "") : "")
<< std::endl; << std::endl;
} }
} }
CPPTRACE_API
std::string stacktrace::to_string() const {
std::ostringstream oss;
print(oss, false);
return std::move(oss).str();
}
CPPTRACE_API
void stacktrace::clear() {
frames.clear();
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
raw_trace generate_raw_trace(std::uint32_t skip) {
return raw_trace(detail::capture_frames(skip + 1));
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
object_trace generate_object_trace(std::uint32_t skip) {
return object_trace(detail::get_frames_object_info(detail::capture_frames(skip + 1)));
}
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
stacktrace generate_trace(std::uint32_t skip) {
std::vector<uintptr_t> frames = detail::capture_frames(skip + 1);
std::vector<stacktrace_frame> trace = detail::resolve_frames(frames);
for(auto& frame : trace) {
frame.symbol = detail::demangle(frame.symbol);
}
return stacktrace(std::move(trace));
}
CPPTRACE_API
std::string demangle(const std::string& name) {
return detail::demangle(name);
}
} }

View File

@ -45,6 +45,8 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <cpptrace/cpptrace.hpp>
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
// Placed here instead of utils because it's used by error.hpp and utils.hpp // Placed here instead of utils because it's used by error.hpp and utils.hpp

View File

@ -24,13 +24,6 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
struct dlframe {
std::string obj_path;
std::string symbol;
uintptr_t raw_address = 0;
uintptr_t obj_address = 0;
};
#if IS_LINUX || IS_APPLE #if IS_LINUX || IS_APPLE
#if !IS_APPLE #if !IS_APPLE
inline uintptr_t get_module_image_base(const std::string& obj_path) { inline uintptr_t get_module_image_base(const std::string& obj_path) {
@ -69,19 +62,19 @@ namespace detail {
} }
#endif #endif
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
inline std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) { inline std::vector<object_frame> get_frames_object_info(const std::vector<uintptr_t>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<dlframe> frames; std::vector<object_frame> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
for(const void* addr : addrs) { for(const uintptr_t addr : addrs) {
Dl_info info; Dl_info info;
dlframe frame; object_frame frame;
frame.raw_address = reinterpret_cast<uintptr_t>(addr); frame.raw_address = addr;
if(dladdr(addr, &info)) { // thread safe if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread safe
// dli_sname and dli_saddr are only present with -rdynamic, sname will be included // dli_sname and dli_saddr are only present with -rdynamic, sname will be included
// but we don't really need dli_saddr // but we don't really need dli_saddr
frame.obj_path = info.dli_fname; frame.obj_path = info.dli_fname;
frame.obj_address = reinterpret_cast<uintptr_t>(addr) frame.obj_address = addr
- reinterpret_cast<uintptr_t>(info.dli_fbase) - reinterpret_cast<uintptr_t>(info.dli_fbase)
+ get_module_image_base(info.dli_fname); + get_module_image_base(info.dli_fname);
frame.symbol = info.dli_sname ?: ""; frame.symbol = info.dli_sname ?: "";
@ -129,22 +122,22 @@ namespace detail {
} }
// aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on // aladdr queries are needed to get pre-ASLR addresses and targets to run addr2line on
inline std::vector<dlframe> get_frames_object_info(const std::vector<void*>& addrs) { inline std::vector<object_frame> get_frames_object_info(const std::vector<uintptr_t>& addrs) {
// reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c // reference: https://github.com/bminor/glibc/blob/master/debug/backtracesyms.c
std::vector<dlframe> frames; std::vector<object_frame> frames;
frames.reserve(addrs.size()); frames.reserve(addrs.size());
for(const void* addr : addrs) { for(const uintptr_t addr : addrs) {
dlframe frame; object_frame frame;
frame.raw_address = reinterpret_cast<uintptr_t>(addr); frame.raw_address = addr;
HMODULE handle; HMODULE handle;
// Multithread safe as long as another thread doesn't come along and free the module // Multithread safe as long as another thread doesn't come along and free the module
if(GetModuleHandleExA( if(GetModuleHandleExA(
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
static_cast<const char*>(addr), reinterpret_cast<const char*>(addr),
&handle &handle
)) { )) {
frame.obj_path = get_module_name(handle); frame.obj_path = get_module_name(handle);
frame.obj_address = reinterpret_cast<uintptr_t>(addr) frame.obj_address = addr
- reinterpret_cast<uintptr_t>(handle) - reinterpret_cast<uintptr_t>(handle)
+ get_module_image_base(frame.obj_path); + get_module_image_base(frame.obj_path);
} else { } else {

View File

@ -31,7 +31,6 @@ namespace detail {
errno_t ret = fopen_s(&file, obj_path.c_str(), "rb"); errno_t ret = fopen_s(&file, obj_path.c_str(), "rb");
if(ret != 0 || file == nullptr) { if(ret != 0 || file == nullptr) {
throw file_error(); throw file_error();
return 0;
} }
auto magic = load_bytes<std::array<char, 2>>(file, 0); auto magic = load_bytes<std::array<char, 2>>(file, 0);
CPPTRACE_VERIFY(memcmp(magic.data(), "MZ", 2) == 0); CPPTRACE_VERIFY(memcmp(magic.data(), "MZ", 2) == 0);

View File

@ -11,46 +11,48 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
using collated_vec = std::vector< using collated_vec = std::vector<
std::pair<std::reference_wrapper<const dlframe>, std::reference_wrapper<stacktrace_frame>> std::pair<std::reference_wrapper<const object_frame>, std::reference_wrapper<stacktrace_frame>>
>; >;
std::unordered_map<std::string, collated_vec> collate_frames( std::unordered_map<std::string, collated_vec> collate_frames(
const std::vector<dlframe>& frames, const std::vector<object_frame>& frames,
std::vector<stacktrace_frame>& trace std::vector<stacktrace_frame>& trace
); );
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
namespace libbacktrace { namespace libbacktrace {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
namespace libdwarf { namespace libdwarf {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
namespace libdl { namespace libdl {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
namespace addr2line { namespace addr2line {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
namespace dbghelp { namespace dbghelp {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames);
} }
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
namespace nothing { namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames);
} }
#endif #endif
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames); std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames);
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames);
} }
} }

View File

@ -3,12 +3,13 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include "../platform/common.hpp"
#include "../platform/object.hpp" #include "../platform/object.hpp"
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::unordered_map<std::string, collated_vec> collate_frames( std::unordered_map<std::string, collated_vec> collate_frames(
const std::vector<dlframe>& frames, const std::vector<object_frame>& frames,
std::vector<stacktrace_frame>& trace std::vector<stacktrace_frame>& trace
) { ) {
std::unordered_map<std::string, collated_vec> entries; std::unordered_map<std::string, collated_vec> entries;
@ -49,25 +50,61 @@ namespace detail {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
std::vector<stacktrace_frame> trace(frames.size()); std::vector<stacktrace_frame> trace(frames.size());
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDL) \
|| defined(CPPTRACE_GET_SYMBOLS_WITH_DBGHELP) \
|| defined(CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE)
// actually need to go backwards to a void*
std::vector<uintptr_t> raw_frames(frames.size());
for(std::size_t i = 0; i < frames.size(); i++) {
raw_frames[i] = frames[i].raw_address;
}
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
apply_trace(trace, libdl::resolve_frames(frames)); apply_trace(trace, libdl::resolve_frames(raw_frames));
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
apply_trace(trace, libdwarf::resolve_frames(frames)); apply_trace(trace, libdwarf::resolve_frames(frames));
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP #ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
apply_trace(trace, dbghelp::resolve_frames(frames)); apply_trace(trace, dbghelp::resolve_frames(raw_frames));
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE #ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
apply_trace(trace, addr2line::resolve_frames(frames)); apply_trace(trace, addr2line::resolve_frames(frames));
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE #ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
apply_trace(trace, libbacktrace::resolve_frames(frames)); apply_trace(trace, libbacktrace::resolve_frames(raw_frames));
#endif #endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING #ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
apply_trace(trace, nothing::resolve_frames(frames)); apply_trace(trace, nothing::resolve_frames(frames));
#endif
return trace;
}
std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
#if defined(CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF) \
|| defined(CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE)
auto dlframes = get_frames_object_info(frames);
#endif
std::vector<stacktrace_frame> trace(frames.size());
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDL
apply_trace(trace, libdl::resolve_frames(frames));
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
apply_trace(trace, libdwarf::resolve_frames(dlframes));
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
apply_trace(trace, dbghelp::resolve_frames(frames));
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
apply_trace(trace, addr2line::resolve_frames(dlframes));
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
apply_trace(trace, libbacktrace::resolve_frames(frames));
#endif
#ifdef CPPTRACE_GET_SYMBOLS_WITH_NOTHING
apply_trace(trace, nothing::resolve_frames(frames));
#endif #endif
return trace; return trace;
} }

View File

@ -271,18 +271,17 @@ namespace addr2line {
} }
// NOLINTNEXTLINE(readability-convert-member-functions-to-static) // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
// TODO: Refactor better // TODO: Refactor better
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
const std::vector<dlframe> dlframes = get_frames_object_info(frames); for(size_t i = 0; i < frames.size(); i++) {
for(size_t i = 0; i < dlframes.size(); i++) { trace[i].address = frames[i].raw_address;
trace[i].address = dlframes[i].raw_address;
// Set what is known for now, and resolutions from addr2line should overwrite // Set what is known for now, and resolutions from addr2line should overwrite
trace[i].filename = dlframes[i].obj_path; trace[i].filename = frames[i].obj_path;
trace[i].symbol = dlframes[i].symbol; trace[i].symbol = frames[i].symbol;
} }
if(has_addr2line()) { if(has_addr2line()) {
const auto entries = collate_frames(dlframes, trace); const auto entries = collate_frames(frames, trace);
for(const auto& entry : entries) { for(const auto& entry : entries) {
const auto& object_name = entry.first; const auto& object_name = entry.first;
const auto& entries_vec = entry.second; const auto& entries_vec = entry.second;

View File

@ -324,7 +324,7 @@ namespace dbghelp {
std::mutex dbghelp_lock; std::mutex dbghelp_lock;
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(HANDLE proc, void* addr) { stacktrace_frame resolve_frame(HANDLE proc, uintptr_t addr) {
const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe const std::lock_guard<std::mutex> lock(dbghelp_lock); // all dbghelp functions are not thread safe
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer; SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
@ -332,8 +332,8 @@ namespace dbghelp {
symbol->MaxNameLen = MAX_SYM_NAME; symbol->MaxNameLen = MAX_SYM_NAME;
union { DWORD64 a; DWORD b; } displacement; union { DWORD64 a; DWORD b; } displacement;
IMAGEHLP_LINE64 line; IMAGEHLP_LINE64 line;
bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line); bool got_line = SymGetLineFromAddr64(proc, addr, &displacement.b, &line);
if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) { if(SymFromAddr(proc, addr, &displacement.a, symbol)) {
if(got_line) { if(got_line) {
IMAGEHLP_STACK_FRAME frame; IMAGEHLP_STACK_FRAME frame;
frame.InstructionOffset = symbol->Address; frame.InstructionOffset = symbol->Address;
@ -344,7 +344,7 @@ namespace dbghelp {
if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) { if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n"); fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
static_cast<std::uint_least32_t>(line.LineNumber), static_cast<std::uint_least32_t>(line.LineNumber),
0, 0,
line.FileName, line.FileName,
@ -375,7 +375,7 @@ namespace dbghelp {
static std::regex comma_re(R"(,(?=\S))"); static std::regex comma_re(R"(,(?=\S))");
signature = std::regex_replace(signature, comma_re, ", "); signature = std::regex_replace(signature, comma_re, ", ");
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
static_cast<std::uint_least32_t>(line.LineNumber), static_cast<std::uint_least32_t>(line.LineNumber),
0, 0,
line.FileName, line.FileName,
@ -383,7 +383,7 @@ namespace dbghelp {
}; };
} else { } else {
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
0, 0,
0, 0,
"", "",
@ -392,7 +392,7 @@ namespace dbghelp {
} }
} else { } else {
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
0, 0,
0, 0,
"", "",
@ -401,7 +401,7 @@ namespace dbghelp {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());

View File

@ -12,11 +12,11 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
namespace libdl { namespace libdl {
stacktrace_frame resolve_frame(const void* addr) { stacktrace_frame resolve_frame(const uintptr_t addr) {
Dl_info info; Dl_info info;
if(dladdr(addr, &info)) { // thread-safe if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
0, 0,
0, 0,
info.dli_fname ? info.dli_fname : "", info.dli_fname ? info.dli_fname : "",
@ -24,7 +24,7 @@ namespace libdl {
}; };
} else { } else {
return { return {
reinterpret_cast<uintptr_t>(addr), addr,
0, 0,
0, 0,
"", "",
@ -33,10 +33,10 @@ namespace libdl {
} }
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());
for(const void* frame : frames) { for(const auto frame : frames) {
trace.push_back(resolve_frame(frame)); trace.push_back(resolve_frame(frame));
} }
return trace; return trace;

View File

@ -58,12 +58,12 @@ namespace libbacktrace {
} }
// TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions // TODO: Handle backtrace_pcinfo calling the callback multiple times on inlined functions
stacktrace_frame resolve_frame(const void* addr) { stacktrace_frame resolve_frame(const uintptr_t addr) {
stacktrace_frame frame; stacktrace_frame frame;
frame.col = 0; frame.col = 0;
backtrace_pcinfo( backtrace_pcinfo(
get_backtrace_state(), get_backtrace_state(),
reinterpret_cast<uintptr_t>(addr), addr,
full_callback, full_callback,
error_callback, error_callback,
&frame &frame
@ -72,7 +72,7 @@ namespace libbacktrace {
// fallback, try to at least recover the symbol name with backtrace_syminfo // fallback, try to at least recover the symbol name with backtrace_syminfo
backtrace_syminfo( backtrace_syminfo(
get_backtrace_state(), get_backtrace_state(),
reinterpret_cast<uintptr_t>(addr), addr,
syminfo_callback, syminfo_callback,
error_callback, error_callback,
&frame &frame
@ -81,10 +81,10 @@ namespace libbacktrace {
return frame; return frame;
} }
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
std::vector<stacktrace_frame> trace; std::vector<stacktrace_frame> trace;
trace.reserve(frames.size()); trace.reserve(frames.size());
for(const void* frame : frames) { for(const auto frame : frames) {
trace.push_back(resolve_frame(frame)); trace.push_back(resolve_frame(frame));
} }
return trace; return trace;

View File

@ -1018,7 +1018,7 @@ namespace libdwarf {
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
stacktrace_frame resolve_frame(const dlframe& frame_info) { stacktrace_frame resolve_frame(const object_frame& frame_info) {
stacktrace_frame frame{}; stacktrace_frame frame{};
frame.filename = frame_info.obj_path; frame.filename = frame_info.obj_path;
frame.symbol = frame_info.symbol; frame.symbol = frame_info.symbol;
@ -1041,10 +1041,9 @@ namespace libdwarf {
}; };
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" }); std::vector<stacktrace_frame> trace(frames.size(), stacktrace_frame { 0, 0, 0, "", "" });
const auto dlframes = get_frames_object_info(frames); for(const auto& obj_entry : collate_frames(frames, trace)) {
for(const auto& obj_entry : collate_frames(dlframes, trace)) {
const auto& obj_name = obj_entry.first; const auto& obj_name = obj_entry.first;
dwarf_resolver resolver(obj_name); dwarf_resolver resolver(obj_name);
for(const auto& entry : obj_entry.second) { for(const auto& entry : obj_entry.second) {

View File

@ -8,7 +8,17 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
namespace nothing { namespace nothing {
std::vector<stacktrace_frame> resolve_frames(const std::vector<void*>& frames) { std::vector<stacktrace_frame> resolve_frames(const std::vector<uintptr_t>& frames) {
return std::vector<stacktrace_frame>(frames.size(), {
0,
0,
0,
"",
""
});
}
std::vector<stacktrace_frame> resolve_frames(const std::vector<object_frame>& frames) {
return std::vector<stacktrace_frame>(frames.size(), { return std::vector<stacktrace_frame>(frames.size(), {
0, 0,
0, 0,

View File

@ -15,7 +15,7 @@ namespace detail {
constexpr size_t hard_max_frames = 100; constexpr size_t hard_max_frames = 100;
#endif #endif
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip); std::vector<uintptr_t> capture_frames(size_t skip);
} }
} }

View File

@ -13,12 +13,16 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) { std::vector<uintptr_t> capture_frames(size_t skip) {
std::vector<void*> frames(hard_max_frames + skip, nullptr); std::vector<void*> addrs(hard_max_frames + skip, nullptr);
const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe const int n_frames = backtrace(addrs.data(), int(hard_max_frames + skip)); // thread safe
frames.resize(n_frames); addrs.resize(n_frames);
frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size()))); addrs.erase(addrs.begin(), addrs.begin() + ptrdiff_t(std::min(skip + 1, addrs.size())));
frames.shrink_to_fit(); addrs.shrink_to_fit();
std::vector<uintptr_t> frames(addrs.size(), 0);
for(std::size_t i = 0; i < addrs.size(); i++) {
frames[i] = reinterpret_cast<uintptr_t>(addrs[i]);
}
return frames; return frames;
} }
} }

View File

@ -7,7 +7,7 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
std::vector<void*> capture_frames(size_t) { std::vector<uintptr_t> capture_frames(size_t) {
return {}; return {};
} }
} }

View File

@ -17,7 +17,7 @@ namespace detail {
struct unwind_state { struct unwind_state {
std::size_t skip; std::size_t skip;
std::size_t count; std::size_t count;
std::vector<void*>& vec; std::vector<uintptr_t>& vec;
}; };
_Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) { _Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
@ -44,14 +44,14 @@ namespace detail {
return _URC_END_OF_STACK; return _URC_END_OF_STACK;
} else { } else {
// TODO: push_back?... // TODO: push_back?...
state.vec[state.count++] = (void*)ip; state.vec[state.count++] = ip;
return _URC_NO_REASON; return _URC_NO_REASON;
} }
} }
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) { std::vector<uintptr_t> capture_frames(size_t skip) {
std::vector<void*> frames(hard_max_frames, nullptr); std::vector<uintptr_t> frames(hard_max_frames, 0);
unwind_state state{skip + 1, 0, frames}; unwind_state state{skip + 1, 0, frames};
_Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe _Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe
frames.resize(state.count); frames.resize(state.count);

View File

@ -5,6 +5,7 @@
#include "../platform/common.hpp" #include "../platform/common.hpp"
#include "../platform/utils.hpp" #include "../platform/utils.hpp"
#include <cstdint>
#include <vector> #include <vector>
#include <windows.h> #include <windows.h>
@ -12,12 +13,15 @@
namespace cpptrace { namespace cpptrace {
namespace detail { namespace detail {
CPPTRACE_FORCE_NO_INLINE CPPTRACE_FORCE_NO_INLINE
std::vector<void*> capture_frames(size_t skip) { std::vector<uintptr_t> capture_frames(size_t skip) {
std::vector<PVOID> addrs(hard_max_frames, nullptr); std::vector<void*> addrs(hard_max_frames, nullptr);
int frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL); int n_frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL);
addrs.resize(frames); addrs.resize(n_frames);
addrs.shrink_to_fit(); std::vector<uintptr_t> frames(addrs.size(), 0);
return addrs; for(std::size_t i = 0; i < addrs.size(); i++) {
frames[i] = reinterpret_cast<uintptr_t>(addrs[i]);
}
return frames;
} }
} }
} }

View File

@ -6,6 +6,7 @@ add_executable(main main.cpp)
add_subdirectory(cpptrace) add_subdirectory(cpptrace)
target_link_libraries(main cpptrace) target_link_libraries(main cpptrace)
target_compile_features(main PRIVATE cxx_std_11)
if(WIN32) if(WIN32)
add_custom_command( add_custom_command(

View File

@ -1,7 +1,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
void trace() { void trace() {
cpptrace::print_trace(); cpptrace::generate_trace().print();
} }
void foo(int) { void foo(int) {

View File

@ -6,7 +6,7 @@
#include <string> #include <string>
void trace() { void trace() {
cpptrace::print_trace(); cpptrace::generate_trace().print();
} }
void foo(int n) { void foo(int n) {

View File

@ -14,6 +14,7 @@ FetchContent_Declare(
) )
FetchContent_MakeAvailable(cpptrace) FetchContent_MakeAvailable(cpptrace)
target_link_libraries(main cpptrace) target_link_libraries(main cpptrace)
target_compile_features(main PRIVATE cxx_std_11)
if(WIN32) if(WIN32)
add_custom_command( add_custom_command(

View File

@ -1,7 +1,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
void trace() { void trace() {
cpptrace::print_trace(); cpptrace::generate_trace().print();
} }
void foo(int) { void foo(int) {

View File

@ -6,3 +6,4 @@ add_executable(main main.cpp)
find_package(cpptrace REQUIRED) find_package(cpptrace REQUIRED)
target_link_libraries(main cpptrace::cpptrace) target_link_libraries(main cpptrace::cpptrace)
target_compile_features(main PRIVATE cxx_std_11)

View File

@ -1,7 +1,7 @@
#include <cpptrace/cpptrace.hpp> #include <cpptrace/cpptrace.hpp>
void trace() { void trace() {
cpptrace::print_trace(); cpptrace::generate_trace().print();
} }
void foo(int) { void foo(int) {

View File

@ -7,5 +7,5 @@
#include <exception> #include <exception>
TEST(TraceTest, trace_test) { TEST(TraceTest, trace_test) {
ASSERT_THROW((cpptrace::print_trace(), false), std::logic_error); ASSERT_THROW((cpptrace::generate_trace().print(), false), std::logic_error);
} }