Expand cpptrace API (#37)
This commit is contained in:
parent
43a50c734c
commit
0b32df64e4
@ -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: '*'
|
||||
HeaderFilterRegex: ''
|
||||
AnalyzeTemporaryDtors: false
|
||||
|
||||
120
README.md
120
README.md
@ -46,14 +46,40 @@ Generating traces is as easy as calling `cpptrace::print_trace`:
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
// ...
|
||||
/* other stuff */
|
||||
```
|
||||
|
||||

|
||||
|
||||
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
|
||||
@ -73,8 +99,9 @@ On windows and macos some extra work is required, see [below](#platform-logistic
|
||||
|
||||
## API
|
||||
|
||||
`cpptrace::print_trace()` can be used to print a stacktrace at the current call site, `cpptrace::generate_trace()` can
|
||||
be used to get raw frame information for custom use.
|
||||
`cpptrace::generate_trace()` can used to generate a stacktrace object at the current call site. Resolved frames can be
|
||||
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.
|
||||
|
||||
@ -83,15 +110,96 @@ for generating these is included above.
|
||||
|
||||
```cpp
|
||||
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 {
|
||||
uintptr_t address;
|
||||
std::uint_least32_t line;
|
||||
std::uint_least32_t col;
|
||||
std::string filename;
|
||||
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 { ... };
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#define CPPTRACE_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -12,15 +14,171 @@
|
||||
#endif
|
||||
|
||||
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 {
|
||||
uintptr_t address;
|
||||
std::uint_least32_t line;
|
||||
std::uint_least32_t col;
|
||||
std::string filename;
|
||||
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
|
||||
|
||||
117
src/cpptrace.cpp
117
src/cpptrace.cpp
@ -2,16 +2,18 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "symbols/symbols.hpp"
|
||||
#include "unwind/unwind.hpp"
|
||||
#include "demangle/demangle.hpp"
|
||||
#include "platform/common.hpp"
|
||||
#include "platform/utils.hpp"
|
||||
#include "platform/object.hpp"
|
||||
|
||||
#define ESC "\033["
|
||||
#define RESET ESC "0m"
|
||||
@ -23,29 +25,59 @@
|
||||
#define CYAN ESC "36m"
|
||||
|
||||
namespace cpptrace {
|
||||
CPPTRACE_FORCE_NO_INLINE CPPTRACE_API
|
||||
std::vector<stacktrace_frame> generate_trace(std::uint32_t skip) {
|
||||
std::vector<void*> frames = detail::capture_frames(skip + 1);
|
||||
CPPTRACE_API
|
||||
object_trace raw_trace::resolve_object_trace() const {
|
||||
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);
|
||||
for(auto& frame : trace) {
|
||||
frame.symbol = detail::demangle(frame.symbol);
|
||||
}
|
||||
return trace;
|
||||
return stacktrace(std::move(trace));
|
||||
}
|
||||
|
||||
CPPTRACE_API
|
||||
void print_trace(std::uint32_t skip) {
|
||||
detail::enable_virtual_terminal_processing_if_needed();
|
||||
std::cerr<<"Stack trace (most recent call first):"<<std::endl;
|
||||
void raw_trace::clear() {
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
const auto trace = generate_trace(skip + 1);
|
||||
if(trace.empty()) {
|
||||
std::cerr<<"<empty trace>"<<std::endl;
|
||||
if(frames.empty()) {
|
||||
stream<<"<empty trace>"<<std::endl;
|
||||
return;
|
||||
}
|
||||
const auto frame_number_width = detail::n_digits(static_cast<int>(trace.size()) - 1);
|
||||
for(const auto& frame : trace) {
|
||||
std::cerr
|
||||
const auto frame_number_width = detail::n_digits(static_cast<int>(frames.size()) - 1);
|
||||
for(const auto& frame : frames) {
|
||||
stream
|
||||
<< '#'
|
||||
<< std::setw(static_cast<int>(frame_number_width))
|
||||
<< std::left
|
||||
@ -53,28 +85,65 @@ namespace cpptrace {
|
||||
<< std::right
|
||||
<< " "
|
||||
<< std::hex
|
||||
<< BLUE
|
||||
<< (color ? BLUE : "")
|
||||
<< "0x"
|
||||
<< std::setw(2 * sizeof(uintptr_t))
|
||||
<< std::setfill('0')
|
||||
<< frame.address
|
||||
<< std::dec
|
||||
<< std::setfill(' ')
|
||||
<< RESET
|
||||
<< (color ? RESET : "")
|
||||
<< " in "
|
||||
<< YELLOW
|
||||
<< (color ? YELLOW : "")
|
||||
<< frame.symbol
|
||||
<< RESET
|
||||
<< (color ? RESET : "")
|
||||
<< " at "
|
||||
<< GREEN
|
||||
<< (color ? GREEN : "")
|
||||
<< frame.filename
|
||||
<< RESET
|
||||
<< (color ? RESET : "")
|
||||
<< ":"
|
||||
<< BLUE
|
||||
<< (color ? BLUE : "")
|
||||
<< frame.line
|
||||
<< RESET
|
||||
<< (frame.col > 0 ? ":" BLUE + std::to_string(frame.col) + RESET : "")
|
||||
<< (color ? RESET : "")
|
||||
<< (frame.col > 0 ? (color ? ":" BLUE : ":") + std::to_string(frame.col) + (color ? RESET : "") : "")
|
||||
<< 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +45,8 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
// Placed here instead of utils because it's used by error.hpp and utils.hpp
|
||||
|
||||
@ -24,13 +24,6 @@
|
||||
|
||||
namespace cpptrace {
|
||||
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_APPLE
|
||||
inline uintptr_t get_module_image_base(const std::string& obj_path) {
|
||||
@ -69,19 +62,19 @@ namespace detail {
|
||||
}
|
||||
#endif
|
||||
// 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
|
||||
std::vector<dlframe> frames;
|
||||
std::vector<object_frame> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
for(const uintptr_t addr : addrs) {
|
||||
Dl_info info;
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
if(dladdr(addr, &info)) { // thread safe
|
||||
object_frame frame;
|
||||
frame.raw_address = addr;
|
||||
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread safe
|
||||
// dli_sname and dli_saddr are only present with -rdynamic, sname will be included
|
||||
// but we don't really need dli_saddr
|
||||
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)
|
||||
+ get_module_image_base(info.dli_fname);
|
||||
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
|
||||
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
|
||||
std::vector<dlframe> frames;
|
||||
std::vector<object_frame> frames;
|
||||
frames.reserve(addrs.size());
|
||||
for(const void* addr : addrs) {
|
||||
dlframe frame;
|
||||
frame.raw_address = reinterpret_cast<uintptr_t>(addr);
|
||||
for(const uintptr_t addr : addrs) {
|
||||
object_frame frame;
|
||||
frame.raw_address = addr;
|
||||
HMODULE handle;
|
||||
// Multithread safe as long as another thread doesn't come along and free the module
|
||||
if(GetModuleHandleExA(
|
||||
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
|
||||
)) {
|
||||
frame.obj_path = get_module_name(handle);
|
||||
frame.obj_address = reinterpret_cast<uintptr_t>(addr)
|
||||
frame.obj_address = addr
|
||||
- reinterpret_cast<uintptr_t>(handle)
|
||||
+ get_module_image_base(frame.obj_path);
|
||||
} else {
|
||||
|
||||
@ -31,7 +31,6 @@ namespace detail {
|
||||
errno_t ret = fopen_s(&file, obj_path.c_str(), "rb");
|
||||
if(ret != 0 || file == nullptr) {
|
||||
throw file_error();
|
||||
return 0;
|
||||
}
|
||||
auto magic = load_bytes<std::array<char, 2>>(file, 0);
|
||||
CPPTRACE_VERIFY(memcmp(magic.data(), "MZ", 2) == 0);
|
||||
|
||||
@ -11,46 +11,48 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
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(
|
||||
const std::vector<dlframe>& frames,
|
||||
const std::vector<object_frame>& frames,
|
||||
std::vector<stacktrace_frame>& trace
|
||||
);
|
||||
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,12 +3,13 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../platform/common.hpp"
|
||||
#include "../platform/object.hpp"
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
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::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());
|
||||
#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
|
||||
apply_trace(trace, libdl::resolve_frames(frames));
|
||||
apply_trace(trace, libdl::resolve_frames(raw_frames));
|
||||
#endif
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBDWARF
|
||||
apply_trace(trace, libdwarf::resolve_frames(frames));
|
||||
apply_trace(trace, libdwarf::resolve_frames(frames));
|
||||
#endif
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_DBGHELP
|
||||
apply_trace(trace, dbghelp::resolve_frames(frames));
|
||||
apply_trace(trace, dbghelp::resolve_frames(raw_frames));
|
||||
#endif
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_ADDR2LINE
|
||||
apply_trace(trace, addr2line::resolve_frames(frames));
|
||||
apply_trace(trace, addr2line::resolve_frames(frames));
|
||||
#endif
|
||||
#ifdef CPPTRACE_GET_SYMBOLS_WITH_LIBBACKTRACE
|
||||
apply_trace(trace, libbacktrace::resolve_frames(frames));
|
||||
apply_trace(trace, libbacktrace::resolve_frames(raw_frames));
|
||||
#endif
|
||||
#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
|
||||
return trace;
|
||||
}
|
||||
|
||||
@ -271,18 +271,17 @@ namespace addr2line {
|
||||
}
|
||||
|
||||
// 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
|
||||
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 < dlframes.size(); i++) {
|
||||
trace[i].address = dlframes[i].raw_address;
|
||||
for(size_t i = 0; i < frames.size(); i++) {
|
||||
trace[i].address = frames[i].raw_address;
|
||||
// Set what is known for now, and resolutions from addr2line should overwrite
|
||||
trace[i].filename = dlframes[i].obj_path;
|
||||
trace[i].symbol = dlframes[i].symbol;
|
||||
trace[i].filename = frames[i].obj_path;
|
||||
trace[i].symbol = frames[i].symbol;
|
||||
}
|
||||
if(has_addr2line()) {
|
||||
const auto entries = collate_frames(dlframes, trace);
|
||||
const auto entries = collate_frames(frames, trace);
|
||||
for(const auto& entry : entries) {
|
||||
const auto& object_name = entry.first;
|
||||
const auto& entries_vec = entry.second;
|
||||
|
||||
@ -324,7 +324,7 @@ namespace dbghelp {
|
||||
std::mutex dbghelp_lock;
|
||||
|
||||
// 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
|
||||
alignas(SYMBOL_INFO) char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
SYMBOL_INFO* symbol = (SYMBOL_INFO*)buffer;
|
||||
@ -332,8 +332,8 @@ namespace dbghelp {
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
union { DWORD64 a; DWORD b; } displacement;
|
||||
IMAGEHLP_LINE64 line;
|
||||
bool got_line = SymGetLineFromAddr64(proc, (DWORD64)addr, &displacement.b, &line);
|
||||
if(SymFromAddr(proc, (DWORD64)addr, &displacement.a, symbol)) {
|
||||
bool got_line = SymGetLineFromAddr64(proc, addr, &displacement.b, &line);
|
||||
if(SymFromAddr(proc, addr, &displacement.a, symbol)) {
|
||||
if(got_line) {
|
||||
IMAGEHLP_STACK_FRAME frame;
|
||||
frame.InstructionOffset = symbol->Address;
|
||||
@ -344,7 +344,7 @@ namespace dbghelp {
|
||||
if(SymSetContext(proc, &frame, nullptr) == FALSE && GetLastError() != ERROR_SUCCESS) {
|
||||
fprintf(stderr, "Stack trace: Internal error while calling SymSetContext\n");
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||
0,
|
||||
line.FileName,
|
||||
@ -375,7 +375,7 @@ namespace dbghelp {
|
||||
static std::regex comma_re(R"(,(?=\S))");
|
||||
signature = std::regex_replace(signature, comma_re, ", ");
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
static_cast<std::uint_least32_t>(line.LineNumber),
|
||||
0,
|
||||
line.FileName,
|
||||
@ -383,7 +383,7 @@ namespace dbghelp {
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
0,
|
||||
0,
|
||||
"",
|
||||
@ -392,7 +392,7 @@ namespace dbghelp {
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
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;
|
||||
trace.reserve(frames.size());
|
||||
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
namespace libdl {
|
||||
stacktrace_frame resolve_frame(const void* addr) {
|
||||
stacktrace_frame resolve_frame(const uintptr_t addr) {
|
||||
Dl_info info;
|
||||
if(dladdr(addr, &info)) { // thread-safe
|
||||
if(dladdr(reinterpret_cast<void*>(addr), &info)) { // thread-safe
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
0,
|
||||
0,
|
||||
info.dli_fname ? info.dli_fname : "",
|
||||
@ -24,7 +24,7 @@ namespace libdl {
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
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;
|
||||
trace.reserve(frames.size());
|
||||
for(const void* frame : frames) {
|
||||
for(const auto frame : frames) {
|
||||
trace.push_back(resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
|
||||
@ -58,12 +58,12 @@ namespace libbacktrace {
|
||||
}
|
||||
|
||||
// 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;
|
||||
frame.col = 0;
|
||||
backtrace_pcinfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
full_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
@ -72,7 +72,7 @@ namespace libbacktrace {
|
||||
// fallback, try to at least recover the symbol name with backtrace_syminfo
|
||||
backtrace_syminfo(
|
||||
get_backtrace_state(),
|
||||
reinterpret_cast<uintptr_t>(addr),
|
||||
addr,
|
||||
syminfo_callback,
|
||||
error_callback,
|
||||
&frame
|
||||
@ -81,10 +81,10 @@ namespace libbacktrace {
|
||||
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;
|
||||
trace.reserve(frames.size());
|
||||
for(const void* frame : frames) {
|
||||
for(const auto frame : frames) {
|
||||
trace.push_back(resolve_frame(frame));
|
||||
}
|
||||
return trace;
|
||||
|
||||
@ -1018,7 +1018,7 @@ namespace libdwarf {
|
||||
}
|
||||
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
stacktrace_frame resolve_frame(const dlframe& frame_info) {
|
||||
stacktrace_frame resolve_frame(const object_frame& frame_info) {
|
||||
stacktrace_frame frame{};
|
||||
frame.filename = frame_info.obj_path;
|
||||
frame.symbol = frame_info.symbol;
|
||||
@ -1041,10 +1041,9 @@ namespace libdwarf {
|
||||
};
|
||||
|
||||
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, "", "" });
|
||||
const auto dlframes = get_frames_object_info(frames);
|
||||
for(const auto& obj_entry : collate_frames(dlframes, trace)) {
|
||||
for(const auto& obj_entry : collate_frames(frames, trace)) {
|
||||
const auto& obj_name = obj_entry.first;
|
||||
dwarf_resolver resolver(obj_name);
|
||||
for(const auto& entry : obj_entry.second) {
|
||||
|
||||
@ -8,7 +8,17 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
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(), {
|
||||
0,
|
||||
0,
|
||||
|
||||
@ -15,7 +15,7 @@ namespace detail {
|
||||
constexpr size_t hard_max_frames = 100;
|
||||
#endif
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
std::vector<void*> capture_frames(size_t skip);
|
||||
std::vector<uintptr_t> capture_frames(size_t skip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,12 +13,16 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
std::vector<void*> capture_frames(size_t skip) {
|
||||
std::vector<void*> frames(hard_max_frames + skip, nullptr);
|
||||
const int n_frames = backtrace(frames.data(), int(hard_max_frames + skip)); // thread safe
|
||||
frames.resize(n_frames);
|
||||
frames.erase(frames.begin(), frames.begin() + ptrdiff_t(std::min(skip + 1, frames.size())));
|
||||
frames.shrink_to_fit();
|
||||
std::vector<uintptr_t> capture_frames(size_t skip) {
|
||||
std::vector<void*> addrs(hard_max_frames + skip, nullptr);
|
||||
const int n_frames = backtrace(addrs.data(), int(hard_max_frames + skip)); // thread safe
|
||||
addrs.resize(n_frames);
|
||||
addrs.erase(addrs.begin(), addrs.begin() + ptrdiff_t(std::min(skip + 1, addrs.size())));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
std::vector<void*> capture_frames(size_t) {
|
||||
std::vector<uintptr_t> capture_frames(size_t) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ namespace detail {
|
||||
struct unwind_state {
|
||||
std::size_t skip;
|
||||
std::size_t count;
|
||||
std::vector<void*>& vec;
|
||||
std::vector<uintptr_t>& vec;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code unwind_callback(_Unwind_Context* context, void* arg) {
|
||||
@ -44,14 +44,14 @@ namespace detail {
|
||||
return _URC_END_OF_STACK;
|
||||
} else {
|
||||
// TODO: push_back?...
|
||||
state.vec[state.count++] = (void*)ip;
|
||||
state.vec[state.count++] = ip;
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
}
|
||||
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
std::vector<void*> capture_frames(size_t skip) {
|
||||
std::vector<void*> frames(hard_max_frames, nullptr);
|
||||
std::vector<uintptr_t> capture_frames(size_t skip) {
|
||||
std::vector<uintptr_t> frames(hard_max_frames, 0);
|
||||
unwind_state state{skip + 1, 0, frames};
|
||||
_Unwind_Backtrace(unwind_callback, &state); // presumably thread-safe
|
||||
frames.resize(state.count);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "../platform/common.hpp"
|
||||
#include "../platform/utils.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
@ -12,12 +13,15 @@
|
||||
namespace cpptrace {
|
||||
namespace detail {
|
||||
CPPTRACE_FORCE_NO_INLINE
|
||||
std::vector<void*> capture_frames(size_t skip) {
|
||||
std::vector<PVOID> addrs(hard_max_frames, nullptr);
|
||||
int frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL);
|
||||
addrs.resize(frames);
|
||||
addrs.shrink_to_fit();
|
||||
return addrs;
|
||||
std::vector<uintptr_t> capture_frames(size_t skip) {
|
||||
std::vector<void*> addrs(hard_max_frames, nullptr);
|
||||
int n_frames = CaptureStackBackTrace(static_cast<DWORD>(skip + 1), hard_max_frames, addrs.data(), NULL);
|
||||
addrs.resize(n_frames);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ add_executable(main main.cpp)
|
||||
|
||||
add_subdirectory(cpptrace)
|
||||
target_link_libraries(main cpptrace)
|
||||
target_compile_features(main PRIVATE cxx_std_11)
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
void foo(int) {
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
void foo(int n) {
|
||||
|
||||
@ -14,6 +14,7 @@ FetchContent_Declare(
|
||||
)
|
||||
FetchContent_MakeAvailable(cpptrace)
|
||||
target_link_libraries(main cpptrace)
|
||||
target_compile_features(main PRIVATE cxx_std_11)
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
void foo(int) {
|
||||
|
||||
@ -6,3 +6,4 @@ add_executable(main main.cpp)
|
||||
|
||||
find_package(cpptrace REQUIRED)
|
||||
target_link_libraries(main cpptrace::cpptrace)
|
||||
target_compile_features(main PRIVATE cxx_std_11)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#include <cpptrace/cpptrace.hpp>
|
||||
|
||||
void trace() {
|
||||
cpptrace::print_trace();
|
||||
cpptrace::generate_trace().print();
|
||||
}
|
||||
|
||||
void foo(int) {
|
||||
|
||||
@ -7,5 +7,5 @@
|
||||
#include <exception>
|
||||
|
||||
TEST(TraceTest, trace_test) {
|
||||
ASSERT_THROW((cpptrace::print_trace(), false), std::logic_error);
|
||||
ASSERT_THROW((cpptrace::generate_trace().print(), false), std::logic_error);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user