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: '*'
|
WarningsAsErrors: '*'
|
||||||
HeaderFilterRegex: ''
|
HeaderFilterRegex: ''
|
||||||
AnalyzeTemporaryDtors: false
|
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>
|
#include <cpptrace/cpptrace.hpp>
|
||||||
|
|
||||||
void trace() {
|
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 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 { ... };
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
117
src/cpptrace.cpp
117
src/cpptrace.cpp
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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());
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user