Update backward to 1.6

This commit is contained in:
Deukhoofd 2021-08-29 16:21:30 +02:00
parent ec3c8e7701
commit b58b8537b5
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
1 changed files with 3537 additions and 3245 deletions

474
extern/backward.hpp vendored
View File

@ -35,6 +35,9 @@
#define BACKWARD_CXX11 #define BACKWARD_CXX11
#define BACKWARD_ATLEAST_CXX11 #define BACKWARD_ATLEAST_CXX11
#define BACKWARD_ATLEAST_CXX98 #define BACKWARD_ATLEAST_CXX98
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
#define BACKWARD_ATLEAST_CXX17
#endif
#else #else
#define BACKWARD_CXX98 #define BACKWARD_CXX98
#define BACKWARD_ATLEAST_CXX98 #define BACKWARD_ATLEAST_CXX98
@ -49,6 +52,9 @@
// #define BACKWARD_SYSTEM_DARWIN // #define BACKWARD_SYSTEM_DARWIN
// - specialization for Mac OS X 10.5 and later. // - specialization for Mac OS X 10.5 and later.
// //
// #define BACKWARD_SYSTEM_WINDOWS
// - specialization for Windows (Clang 9 and MSVC2017)
//
// #define BACKWARD_SYSTEM_UNKNOWN // #define BACKWARD_SYSTEM_UNKNOWN
// - placebo implementation, does nothing. // - placebo implementation, does nothing.
// //
@ -84,6 +90,8 @@
#include <streambuf> #include <streambuf>
#include <string> #include <string>
#include <vector> #include <vector>
#include <exception>
#include <iterator>
#if defined(BACKWARD_SYSTEM_LINUX) #if defined(BACKWARD_SYSTEM_LINUX)
@ -97,6 +105,11 @@
// exception. // exception.
// - normally libgcc is already linked to your program by default. // - normally libgcc is already linked to your program by default.
// //
// #define BACKWARD_HAS_LIBUNWIND 1
// - libunwind provides, in some cases, a more accurate stacktrace as it knows
// to decode signal handler frames and lets us edit the context registers when
// unwinding, allowing stack traces over bad function references.
//
// #define BACKWARD_HAS_BACKTRACE == 1 // #define BACKWARD_HAS_BACKTRACE == 1
// - backtrace seems to be a little bit more portable than libunwind, but on // - backtrace seems to be a little bit more portable than libunwind, but on
// linux, it uses unwind anyway, but abstract away a tiny information that is // linux, it uses unwind anyway, but abstract away a tiny information that is
@ -109,10 +122,13 @@
// Note that only one of the define should be set to 1 at a time. // Note that only one of the define should be set to 1 at a time.
// //
#if BACKWARD_HAS_UNWIND == 1 #if BACKWARD_HAS_UNWIND == 1
#elif BACKWARD_HAS_LIBUNWIND == 1
#elif BACKWARD_HAS_BACKTRACE == 1 #elif BACKWARD_HAS_BACKTRACE == 1
#else #else
#undef BACKWARD_HAS_UNWIND #undef BACKWARD_HAS_UNWIND
#define BACKWARD_HAS_UNWIND 1 #define BACKWARD_HAS_UNWIND 1
#undef BACKWARD_HAS_LIBUNWIND
#define BACKWARD_HAS_LIBUNWIND 0
#undef BACKWARD_HAS_BACKTRACE #undef BACKWARD_HAS_BACKTRACE
#define BACKWARD_HAS_BACKTRACE 0 #define BACKWARD_HAS_BACKTRACE 0
#endif #endif
@ -127,7 +143,8 @@
// - source filename // - source filename
// - line and column numbers // - line and column numbers
// - source code snippet (assuming the file is accessible) // - source code snippet (assuming the file is accessible)
// - variables name and values (if not optimized out) // - variable names (if not optimized out)
// - variable values (not supported by backward-cpp)
// - You need to link with the lib "dw": // - You need to link with the lib "dw":
// - apt-get install libdw-dev // - apt-get install libdw-dev
// - g++/clang++ -ldw ... // - g++/clang++ -ldw ...
@ -150,7 +167,8 @@
// - source filename // - source filename
// - line and column numbers // - line and column numbers
// - source code snippet (assuming the file is accessible) // - source code snippet (assuming the file is accessible)
// - variables name and values (if not optimized out) // - variable names (if not optimized out)
// - variable values (not supported by backward-cpp)
// - You need to link with the lib "dwarf": // - You need to link with the lib "dwarf":
// - apt-get install libdwarf-dev // - apt-get install libdwarf-dev
// - g++/clang++ -ldwarf ... // - g++/clang++ -ldwarf ...
@ -257,6 +275,12 @@
// exception. // exception.
// - normally libgcc is already linked to your program by default. // - normally libgcc is already linked to your program by default.
// //
// #define BACKWARD_HAS_LIBUNWIND 1
// - libunwind comes from clang, which implements an API compatible version.
// - libunwind provides, in some cases, a more accurate stacktrace as it knows
// to decode signal handler frames and lets us edit the context registers when
// unwinding, allowing stack traces over bad function references.
//
// #define BACKWARD_HAS_BACKTRACE == 1 // #define BACKWARD_HAS_BACKTRACE == 1
// - backtrace is available by default, though it does not produce as much // - backtrace is available by default, though it does not produce as much
// information as another library might. // information as another library might.
@ -268,11 +292,14 @@
// //
#if BACKWARD_HAS_UNWIND == 1 #if BACKWARD_HAS_UNWIND == 1
#elif BACKWARD_HAS_BACKTRACE == 1 #elif BACKWARD_HAS_BACKTRACE == 1
#elif BACKWARD_HAS_LIBUNWIND == 1
#else #else
#undef BACKWARD_HAS_UNWIND #undef BACKWARD_HAS_UNWIND
#define BACKWARD_HAS_UNWIND 1 #define BACKWARD_HAS_UNWIND 1
#undef BACKWARD_HAS_BACKTRACE #undef BACKWARD_HAS_BACKTRACE
#define BACKWARD_HAS_BACKTRACE 0 #define BACKWARD_HAS_BACKTRACE 0
#undef BACKWARD_HAS_LIBUNWIND
#define BACKWARD_HAS_LIBUNWIND 0
#endif #endif
// On Darwin, backward can extract detailed information about a stack trace // On Darwin, backward can extract detailed information about a stack trace
@ -327,6 +354,11 @@ typedef SSIZE_T ssize_t;
#define NOINLINE __declspec(noinline) #define NOINLINE __declspec(noinline)
#endif #endif
#ifdef _MSC_VER
#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "dbghelp.lib")
#endif
// Comment / packing is from stackoverflow: // Comment / packing is from stackoverflow:
// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227 // https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227
// Some versions of imagehlp.dll lack the proper packing directives themselves // Some versions of imagehlp.dll lack the proper packing directives themselves
@ -370,6 +402,11 @@ extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *);
#endif // BACKWARD_HAS_UNWIND == 1 #endif // BACKWARD_HAS_UNWIND == 1
#if BACKWARD_HAS_LIBUNWIND == 1
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#endif // BACKWARD_HAS_LIBUNWIND == 1
#ifdef BACKWARD_ATLEAST_CXX11 #ifdef BACKWARD_ATLEAST_CXX11
#include <unordered_map> #include <unordered_map>
#include <utility> // for std::swap #include <utility> // for std::swap
@ -529,7 +566,7 @@ typedef pdb_symbol current;
void update(T new_val) { void update(T new_val) {
_val = new_val; _val = new_val;
_empty = static_cast<bool>(new_val); _empty = !static_cast<bool>(new_val);
} }
operator const dummy *() const { operator const dummy *() const {
@ -681,14 +718,17 @@ typedef pdb_symbol current;
size_t size() const { return 0; } size_t size() const { return 0; }
Trace operator[](size_t) const { return Trace(); } Trace operator[](size_t) const { return Trace(); }
size_t load_here(size_t = 0) { return 0; } size_t load_here(size_t = 0) { return 0; }
size_t load_from(void *, size_t = 0) { return 0; } size_t load_from(void *, size_t = 0, void * = nullptr, void * = nullptr) {
return 0;
}
size_t thread_id() const { return 0; } size_t thread_id() const { return 0; }
void skip_n_firsts(size_t) {} void skip_n_firsts(size_t) {}
}; };
class StackTraceImplBase { class StackTraceImplBase {
public: public:
StackTraceImplBase() : _thread_id(0), _skip(0) {} StackTraceImplBase()
: _thread_id(0), _skip(0), _context(nullptr), _error_addr(nullptr) {}
size_t thread_id() const { return _thread_id; } size_t thread_id() const { return _thread_id; }
@ -716,17 +756,27 @@ typedef pdb_symbol current;
#endif #endif
} }
void set_context(void *context) { _context = context; }
void *context() const { return _context; }
void set_error_addr(void *error_addr) { _error_addr = error_addr; }
void *error_addr() const { return _error_addr; }
size_t skip_n_firsts() const { return _skip; } size_t skip_n_firsts() const { return _skip; }
private: private:
size_t _thread_id; size_t _thread_id;
size_t _skip; size_t _skip;
void *_context;
void *_error_addr;
}; };
class StackTraceImplHolder : public StackTraceImplBase { class StackTraceImplHolder : public StackTraceImplBase {
public: public:
size_t size() const { size_t size() const {
return (_stacktrace.size() >= skip_n_firsts()) ? _stacktrace.size() - skip_n_firsts() : 0; return (_stacktrace.size() >= skip_n_firsts())
? _stacktrace.size() - skip_n_firsts()
: 0;
} }
Trace operator[](size_t idx) const { Trace operator[](size_t idx) const {
if (idx >= size()) { if (idx >= size()) {
@ -807,8 +857,11 @@ typedef pdb_symbol current;
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder { class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
public: public:
NOINLINE NOINLINE
size_t load_here(size_t depth = 32) { size_t load_here(size_t depth = 32, void *context = nullptr,
void *error_addr = nullptr) {
load_thread_info(); load_thread_info();
set_context(context);
set_error_addr(error_addr);
if (depth == 0) { if (depth == 0) {
return 0; return 0;
} }
@ -818,8 +871,9 @@ typedef pdb_symbol current;
skip_n_firsts(0); skip_n_firsts(0);
return size(); return size();
} }
size_t load_from(void *addr, size_t depth = 32) { size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,
load_here(depth + 8); void *error_addr = nullptr) {
load_here(depth + 8, context, error_addr);
for (size_t i = 0; i < _stacktrace.size(); ++i) { for (size_t i = 0; i < _stacktrace.size(); ++i) {
if (_stacktrace[i] == addr) { if (_stacktrace[i] == addr) {
@ -841,13 +895,193 @@ typedef pdb_symbol current;
}; };
}; };
#elif BACKWARD_HAS_LIBUNWIND == 1
template <>
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
public:
__attribute__((noinline)) size_t load_here(size_t depth = 32,
void *_context = nullptr,
void *_error_addr = nullptr) {
set_context(_context);
set_error_addr(_error_addr);
load_thread_info();
if (depth == 0) {
return 0;
}
_stacktrace.resize(depth + 1);
int result = 0;
unw_context_t ctx;
size_t index = 0;
// Add the tail call. If the Instruction Pointer is the crash address it
// means we got a bad function pointer dereference, so we "unwind" the
// bad pointer manually by using the return address pointed to by the
// Stack Pointer as the Instruction Pointer and letting libunwind do
// the rest
if (context()) {
ucontext_t *uctx = reinterpret_cast<ucontext_t *>(context());
#ifdef REG_RIP // x86_64
if (uctx->uc_mcontext.gregs[REG_RIP] ==
reinterpret_cast<greg_t>(error_addr())) {
uctx->uc_mcontext.gregs[REG_RIP] =
*reinterpret_cast<size_t *>(uctx->uc_mcontext.gregs[REG_RSP]);
}
_stacktrace[index] =
reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_RIP]);
++index;
ctx = *reinterpret_cast<unw_context_t *>(uctx);
#elif defined(REG_EIP) // x86_32
if (uctx->uc_mcontext.gregs[REG_EIP] ==
reinterpret_cast<greg_t>(error_addr())) {
uctx->uc_mcontext.gregs[REG_EIP] =
*reinterpret_cast<size_t *>(uctx->uc_mcontext.gregs[REG_ESP]);
}
_stacktrace[index] =
reinterpret_cast<void *>(uctx->uc_mcontext.gregs[REG_EIP]);
++index;
ctx = *reinterpret_cast<unw_context_t *>(uctx);
#elif defined(__arm__)
// libunwind uses its own context type for ARM unwinding.
// Copy the registers from the signal handler's context so we can
// unwind
unw_getcontext(&ctx);
ctx.regs[UNW_ARM_R0] = uctx->uc_mcontext.arm_r0;
ctx.regs[UNW_ARM_R1] = uctx->uc_mcontext.arm_r1;
ctx.regs[UNW_ARM_R2] = uctx->uc_mcontext.arm_r2;
ctx.regs[UNW_ARM_R3] = uctx->uc_mcontext.arm_r3;
ctx.regs[UNW_ARM_R4] = uctx->uc_mcontext.arm_r4;
ctx.regs[UNW_ARM_R5] = uctx->uc_mcontext.arm_r5;
ctx.regs[UNW_ARM_R6] = uctx->uc_mcontext.arm_r6;
ctx.regs[UNW_ARM_R7] = uctx->uc_mcontext.arm_r7;
ctx.regs[UNW_ARM_R8] = uctx->uc_mcontext.arm_r8;
ctx.regs[UNW_ARM_R9] = uctx->uc_mcontext.arm_r9;
ctx.regs[UNW_ARM_R10] = uctx->uc_mcontext.arm_r10;
ctx.regs[UNW_ARM_R11] = uctx->uc_mcontext.arm_fp;
ctx.regs[UNW_ARM_R12] = uctx->uc_mcontext.arm_ip;
ctx.regs[UNW_ARM_R13] = uctx->uc_mcontext.arm_sp;
ctx.regs[UNW_ARM_R14] = uctx->uc_mcontext.arm_lr;
ctx.regs[UNW_ARM_R15] = uctx->uc_mcontext.arm_pc;
// If we have crashed in the PC use the LR instead, as this was
// a bad function dereference
if (reinterpret_cast<unsigned long>(error_addr()) ==
uctx->uc_mcontext.arm_pc) {
ctx.regs[UNW_ARM_R15] =
uctx->uc_mcontext.arm_lr - sizeof(unsigned long);
}
_stacktrace[index] = reinterpret_cast<void *>(ctx.regs[UNW_ARM_R15]);
++index;
#elif defined(__APPLE__) && defined(__x86_64__)
unw_getcontext(&ctx);
// OS X's implementation of libunwind uses its own context object
// so we need to convert the passed context to libunwind's format
// (information about the data layout taken from unw_getcontext.s
// in Apple's libunwind source
ctx.data[0] = uctx->uc_mcontext->__ss.__rax;
ctx.data[1] = uctx->uc_mcontext->__ss.__rbx;
ctx.data[2] = uctx->uc_mcontext->__ss.__rcx;
ctx.data[3] = uctx->uc_mcontext->__ss.__rdx;
ctx.data[4] = uctx->uc_mcontext->__ss.__rdi;
ctx.data[5] = uctx->uc_mcontext->__ss.__rsi;
ctx.data[6] = uctx->uc_mcontext->__ss.__rbp;
ctx.data[7] = uctx->uc_mcontext->__ss.__rsp;
ctx.data[8] = uctx->uc_mcontext->__ss.__r8;
ctx.data[9] = uctx->uc_mcontext->__ss.__r9;
ctx.data[10] = uctx->uc_mcontext->__ss.__r10;
ctx.data[11] = uctx->uc_mcontext->__ss.__r11;
ctx.data[12] = uctx->uc_mcontext->__ss.__r12;
ctx.data[13] = uctx->uc_mcontext->__ss.__r13;
ctx.data[14] = uctx->uc_mcontext->__ss.__r14;
ctx.data[15] = uctx->uc_mcontext->__ss.__r15;
ctx.data[16] = uctx->uc_mcontext->__ss.__rip;
// If the IP is the same as the crash address we have a bad function
// dereference The caller's address is pointed to by %rsp, so we
// dereference that value and set it to be the next frame's IP.
if (uctx->uc_mcontext->__ss.__rip ==
reinterpret_cast<__uint64_t>(error_addr())) {
ctx.data[16] =
*reinterpret_cast<__uint64_t *>(uctx->uc_mcontext->__ss.__rsp);
}
_stacktrace[index] = reinterpret_cast<void *>(ctx.data[16]);
++index;
#elif defined(__APPLE__)
unw_getcontext(&ctx)
// TODO: Convert the ucontext_t to libunwind's unw_context_t like
// we do in 64 bits
if (ctx.uc_mcontext->__ss.__eip ==
reinterpret_cast<greg_t>(error_addr())) {
ctx.uc_mcontext->__ss.__eip = ctx.uc_mcontext->__ss.__esp;
}
_stacktrace[index] =
reinterpret_cast<void *>(ctx.uc_mcontext->__ss.__eip);
++index;
#endif
}
unw_cursor_t cursor;
if (context()) {
#if defined(UNW_INIT_SIGNAL_FRAME)
result = unw_init_local2(&cursor, &ctx, UNW_INIT_SIGNAL_FRAME);
#else
result = unw_init_local(&cursor, &ctx);
#endif
} else {
unw_getcontext(&ctx);
;
result = unw_init_local(&cursor, &ctx);
}
if (result != 0)
return 1;
unw_word_t ip = 0;
while (index <= depth && unw_step(&cursor) > 0) {
result = unw_get_reg(&cursor, UNW_REG_IP, &ip);
if (result == 0) {
_stacktrace[index] = reinterpret_cast<void *>(--ip);
++index;
}
}
--index;
_stacktrace.resize(index + 1);
skip_n_firsts(0);
return size();
}
size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,
void *error_addr = nullptr) {
load_here(depth + 8, context, error_addr);
for (size_t i = 0; i < _stacktrace.size(); ++i) {
if (_stacktrace[i] == addr) {
skip_n_firsts(i);
_stacktrace[i] = (void *)((uintptr_t)_stacktrace[i]);
break;
}
}
_stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
return size();
}
};
#elif defined(BACKWARD_HAS_BACKTRACE) #elif defined(BACKWARD_HAS_BACKTRACE)
template <> template <>
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder { class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
public: public:
NOINLINE NOINLINE
size_t load_here(size_t depth = 32) { size_t load_here(size_t depth = 32, void *context = nullptr,
void *error_addr = nullptr) {
set_context(context);
set_error_addr(error_addr);
load_thread_info(); load_thread_info();
if (depth == 0) { if (depth == 0) {
return 0; return 0;
@ -859,8 +1093,9 @@ public:
return size(); return size();
} }
size_t load_from(void *addr, size_t depth = 32) { size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,
load_here(depth + 8); void *error_addr = nullptr) {
load_here(depth + 8, context, error_addr);
for (size_t i = 0; i < _stacktrace.size(); ++i) { for (size_t i = 0; i < _stacktrace.size(); ++i) {
if (_stacktrace[i] == addr) { if (_stacktrace[i] == addr) {
@ -887,8 +1122,10 @@ public:
void set_thread_handle(HANDLE handle) { thd_ = handle; } void set_thread_handle(HANDLE handle) { thd_ = handle; }
NOINLINE NOINLINE
size_t load_here(size_t depth = 32) { size_t load_here(size_t depth = 32, void *context = nullptr,
void *error_addr = nullptr) {
set_context(static_cast<CONTEXT*>(context));
set_error_addr(error_addr);
CONTEXT localCtx; // used when no context is provided CONTEXT localCtx; // used when no context is provided
if (depth == 0) { if (depth == 0) {
@ -950,8 +1187,9 @@ public:
return size(); return size();
} }
size_t load_from(void *addr, size_t depth = 32) { size_t load_from(void *addr, size_t depth = 32, void *context = nullptr,
load_here(depth + 8); void *error_addr = nullptr) {
load_here(depth + 8, context, error_addr);
for (size_t i = 0; i < _stacktrace.size(); ++i) { for (size_t i = 0; i < _stacktrace.size(); ++i) {
if (_stacktrace[i] == addr) { if (_stacktrace[i] == addr) {
@ -976,19 +1214,21 @@ private:
/*************** TRACE RESOLVER ***************/ /*************** TRACE RESOLVER ***************/
template <typename TAG> class TraceResolverImpl;
#ifdef BACKWARD_SYSTEM_UNKNOWN
template <> class TraceResolverImpl<system_tag::unknown_tag> {
public:
template <class ST> void load_stacktrace(ST &) {}
ResolvedTrace resolve(ResolvedTrace t) { return t; }
};
#endif
class TraceResolverImplBase { class TraceResolverImplBase {
public:
virtual ~TraceResolverImplBase() {}
virtual void load_addresses(void *const*addresses, int address_count) {
(void)addresses;
(void)address_count;
}
template <class ST> void load_stacktrace(ST &st) {
load_addresses(st.begin(), (int)st.size());
}
virtual ResolvedTrace resolve(ResolvedTrace t) { return t; }
protected: protected:
std::string demangle(const char *funcname) { std::string demangle(const char *funcname) {
return _demangler.demangle(funcname); return _demangler.demangle(funcname);
@ -998,6 +1238,15 @@ public:
details::demangler _demangler; details::demangler _demangler;
}; };
template <typename TAG> class TraceResolverImpl;
#ifdef BACKWARD_SYSTEM_UNKNOWN
template <> class TraceResolverImpl<system_tag::unknown_tag>
: public TraceResolverImplBase {};
#endif
#ifdef BACKWARD_SYSTEM_LINUX #ifdef BACKWARD_SYSTEM_LINUX
class TraceResolverLinuxBase : public TraceResolverImplBase { class TraceResolverLinuxBase : public TraceResolverImplBase {
@ -1014,6 +1263,12 @@ public:
// variable; In that case, we actually open /proc/self/exe, which // variable; In that case, we actually open /proc/self/exe, which
// is always the actual executable (even if it was deleted/replaced!) // is always the actual executable (even if it was deleted/replaced!)
// but display the path that /proc/self/exe links to. // but display the path that /proc/self/exe links to.
// However, this right away reduces probability of successful symbol
// resolution, because libbfd may try to find *.debug files in the
// same dir, in case symbols are stripped. As a result, it may try
// to find a file /proc/self/<exe_name>.debug, which obviously does
// not exist. /proc/self/exe is a last resort. First load attempt
// should go for the original executable file path.
symbol_info.dli_fname = "/proc/self/exe"; symbol_info.dli_fname = "/proc/self/exe";
return exec_path_; return exec_path_;
} else { } else {
@ -1062,15 +1317,14 @@ public:
class TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol> class TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol>
: public TraceResolverLinuxBase { : public TraceResolverLinuxBase {
public: public:
template <class ST> void load_stacktrace(ST &st) { void load_addresses(void *const*addresses, int address_count) override {
using namespace details; if (address_count == 0) {
if (st.size() == 0) {
return; return;
} }
_symbols.reset(backtrace_symbols(st.begin(), (int)st.size())); _symbols.reset(backtrace_symbols(addresses, address_count));
} }
ResolvedTrace resolve(ResolvedTrace trace) { ResolvedTrace resolve(ResolvedTrace trace) override {
char *filename = _symbols[trace.idx]; char *filename = _symbols[trace.idx];
char *funcname = filename; char *funcname = filename;
while (*funcname && *funcname != '(') { while (*funcname && *funcname != '(') {
@ -1107,9 +1361,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libbfd>
public: public:
TraceResolverLinuxImpl() : _bfd_loaded(false) {} TraceResolverLinuxImpl() : _bfd_loaded(false) {}
template <class ST> void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) override {
ResolvedTrace resolve(ResolvedTrace trace) {
Dl_info symbol_info; Dl_info symbol_info;
// trace.addr is a virtual address in memory pointing to some code. // trace.addr is a virtual address in memory pointing to some code.
@ -1139,9 +1391,45 @@ public:
} }
trace.object_filename = resolve_exec_path(symbol_info); trace.object_filename = resolve_exec_path(symbol_info);
bfd_fileobject &fobj = load_object_with_bfd(symbol_info.dli_fname); bfd_fileobject *fobj;
if (!fobj.handle) { // Before rushing to resolution need to ensure the executable
return trace; // sad, we couldn't load the object :( // file still can be used. For that compare inode numbers of
// what is stored by the executable's file path, and in the
// dli_fname, which not necessarily equals to the executable.
// It can be a shared library, or /proc/self/exe, and in the
// latter case has drawbacks. See the exec path resolution for
// details. In short - the dli object should be used only as
// the last resort.
// If inode numbers are equal, it is known dli_fname and the
// executable file are the same. This is guaranteed by Linux,
// because if the executable file is changed/deleted, it will
// be done in a new inode. The old file will be preserved in
// /proc/self/exe, and may even have inode 0. The latter can
// happen if the inode was actually reused, and the file was
// kept only in the main memory.
//
struct stat obj_stat;
struct stat dli_stat;
if (stat(trace.object_filename.c_str(), &obj_stat) == 0 &&
stat(symbol_info.dli_fname, &dli_stat) == 0 &&
obj_stat.st_ino == dli_stat.st_ino) {
// The executable file, and the shared object containing the
// address are the same file. Safe to use the original path.
// this is preferable. Libbfd will search for stripped debug
// symbols in the same directory.
fobj = load_object_with_bfd(trace.object_filename);
} else{
// The original object file was *deleted*! The only hope is
// that the debug symbols are either inside the shared
// object file, or are in the same directory, and this is
// not /proc/self/exe.
fobj = nullptr;
}
if (fobj == nullptr || !fobj->handle) {
fobj = load_object_with_bfd(symbol_info.dli_fname);
if (!fobj->handle) {
return trace;
}
} }
find_sym_result *details_selected; // to be filled. find_sym_result *details_selected; // to be filled.
@ -1276,7 +1564,7 @@ private:
typedef details::hashtable<std::string, bfd_fileobject>::type fobj_bfd_map_t; typedef details::hashtable<std::string, bfd_fileobject>::type fobj_bfd_map_t;
fobj_bfd_map_t _fobj_bfd_map; fobj_bfd_map_t _fobj_bfd_map;
bfd_fileobject &load_object_with_bfd(const std::string &filename_object) { bfd_fileobject *load_object_with_bfd(const std::string &filename_object) {
using namespace details; using namespace details;
if (!_bfd_loaded) { if (!_bfd_loaded) {
@ -1287,11 +1575,11 @@ private:
fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object); fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object);
if (it != _fobj_bfd_map.end()) { if (it != _fobj_bfd_map.end()) {
return it->second; return &it->second;
} }
// this new object is empty for now. // this new object is empty for now.
bfd_fileobject &r = _fobj_bfd_map[filename_object]; bfd_fileobject *r = &_fobj_bfd_map[filename_object];
// we do the work temporary in this one; // we do the work temporary in this one;
bfd_handle_t bfd_handle; bfd_handle_t bfd_handle;
@ -1340,9 +1628,9 @@ private:
return r; // damned, that's a stripped file that you got there! return r; // damned, that's a stripped file that you got there!
} }
r.handle = move(bfd_handle); r->handle = move(bfd_handle);
r.symtab = move(symtab); r->symtab = move(symtab);
r.dynamic_symtab = move(dynamic_symtab); r->dynamic_symtab = move(dynamic_symtab);
return r; return r;
} }
@ -1361,15 +1649,15 @@ private:
find_sym_result result; find_sym_result result;
}; };
find_sym_result find_symbol_details(bfd_fileobject &fobj, void *addr, find_sym_result find_symbol_details(bfd_fileobject *fobj, void *addr,
void *base_addr) { void *base_addr) {
find_sym_context context; find_sym_context context;
context.self = this; context.self = this;
context.fobj = &fobj; context.fobj = fobj;
context.addr = addr; context.addr = addr;
context.base_addr = base_addr; context.base_addr = base_addr;
context.result.found = false; context.result.found = false;
bfd_map_over_sections(fobj.handle.get(), &find_in_section_trampoline, bfd_map_over_sections(fobj->handle.get(), &find_in_section_trampoline,
static_cast<void *>(&context)); static_cast<void *>(&context));
return context.result; return context.result;
} }
@ -1378,24 +1666,24 @@ private:
find_sym_context *context = static_cast<find_sym_context *>(data); find_sym_context *context = static_cast<find_sym_context *>(data);
context->self->find_in_section( context->self->find_in_section(
reinterpret_cast<bfd_vma>(context->addr), reinterpret_cast<bfd_vma>(context->addr),
reinterpret_cast<bfd_vma>(context->base_addr), *context->fobj, section, reinterpret_cast<bfd_vma>(context->base_addr), context->fobj, section,
context->result); context->result);
} }
void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject &fobj, void find_in_section(bfd_vma addr, bfd_vma base_addr, bfd_fileobject *fobj,
asection *section, find_sym_result &result) { asection *section, find_sym_result &result) {
if (result.found) if (result.found)
return; return;
#ifdef bfd_get_section_flags #ifdef bfd_get_section_flags
if ((bfd_get_section_flags(fobj.handle.get(), section) & SEC_ALLOC) == 0) if ((bfd_get_section_flags(fobj->handle.get(), section) & SEC_ALLOC) == 0)
#else #else
if ((bfd_section_flags(section) & SEC_ALLOC) == 0) if ((bfd_section_flags(section) & SEC_ALLOC) == 0)
#endif #endif
return; // a debug section is never loaded automatically. return; // a debug section is never loaded automatically.
#ifdef bfd_get_section_vma #ifdef bfd_get_section_vma
bfd_vma sec_addr = bfd_get_section_vma(fobj.handle.get(), section); bfd_vma sec_addr = bfd_get_section_vma(fobj->handle.get(), section);
#else #else
bfd_vma sec_addr = bfd_section_vma(section); bfd_vma sec_addr = bfd_section_vma(section);
#endif #endif
@ -1417,15 +1705,15 @@ private:
#pragma clang diagnostic push #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif #endif
if (!result.found && fobj.symtab) { if (!result.found && fobj->symtab) {
result.found = bfd_find_nearest_line( result.found = bfd_find_nearest_line(
fobj.handle.get(), section, fobj.symtab.get(), addr - sec_addr, fobj->handle.get(), section, fobj->symtab.get(), addr - sec_addr,
&result.filename, &result.funcname, &result.line); &result.filename, &result.funcname, &result.line);
} }
if (!result.found && fobj.dynamic_symtab) { if (!result.found && fobj->dynamic_symtab) {
result.found = bfd_find_nearest_line( result.found = bfd_find_nearest_line(
fobj.handle.get(), section, fobj.dynamic_symtab.get(), fobj->handle.get(), section, fobj->dynamic_symtab.get(),
addr - sec_addr, &result.filename, &result.funcname, &result.line); addr - sec_addr, &result.filename, &result.funcname, &result.line);
} }
#if defined(__clang__) #if defined(__clang__)
@ -1434,13 +1722,13 @@ private:
} }
ResolvedTrace::source_locs_t ResolvedTrace::source_locs_t
backtrace_inliners(bfd_fileobject &fobj, find_sym_result previous_result) { backtrace_inliners(bfd_fileobject *fobj, find_sym_result previous_result) {
// This function can be called ONLY after a SUCCESSFUL call to // This function can be called ONLY after a SUCCESSFUL call to
// find_symbol_details. The state is global to the bfd_handle. // find_symbol_details. The state is global to the bfd_handle.
ResolvedTrace::source_locs_t results; ResolvedTrace::source_locs_t results;
while (previous_result.found) { while (previous_result.found) {
find_sym_result result; find_sym_result result;
result.found = bfd_find_inliner_info(fobj.handle.get(), &result.filename, result.found = bfd_find_inliner_info(fobj->handle.get(), &result.filename,
&result.funcname, &result.line); &result.funcname, &result.line);
if (result if (result
@ -1483,9 +1771,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libdw>
public: public:
TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {} TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {}
template <class ST> void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) override {
ResolvedTrace resolve(ResolvedTrace trace) {
using namespace details; using namespace details;
Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr; Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr;
@ -1778,9 +2064,9 @@ private:
static const char *die_call_file(Dwarf_Die *die) { static const char *die_call_file(Dwarf_Die *die) {
Dwarf_Attribute attr_mem; Dwarf_Attribute attr_mem;
Dwarf_Sword file_idx = 0; Dwarf_Word file_idx = 0;
dwarf_formsdata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx); dwarf_formudata(dwarf_attr(die, DW_AT_call_file, &attr_mem), &file_idx);
if (file_idx == 0) { if (file_idx == 0) {
return 0; return 0;
@ -1812,9 +2098,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libdwarf>
public: public:
TraceResolverLinuxImpl() : _dwarf_loaded(false) {} TraceResolverLinuxImpl() : _dwarf_loaded(false) {}
template <class ST> void load_stacktrace(ST &) {} ResolvedTrace resolve(ResolvedTrace trace) override {
ResolvedTrace resolve(ResolvedTrace trace) {
// trace.addr is a virtual address in memory pointing to some code. // trace.addr is a virtual address in memory pointing to some code.
// Let's try to find from which loaded object it comes from. // Let's try to find from which loaded object it comes from.
// The loaded object can be yourself btw. // The loaded object can be yourself btw.
@ -3038,12 +3322,12 @@ private:
Dwarf_Die cu_die) { Dwarf_Die cu_die) {
Dwarf_Attribute attr_mem; Dwarf_Attribute attr_mem;
Dwarf_Error error = DW_DLE_NE; Dwarf_Error error = DW_DLE_NE;
Dwarf_Signed file_index; Dwarf_Unsigned file_index;
std::string file; std::string file;
if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) { if (dwarf_attr(die, DW_AT_call_file, &attr_mem, &error) == DW_DLV_OK) {
if (dwarf_formsdata(attr_mem, &file_index, &error) != DW_DLV_OK) { if (dwarf_formudata(attr_mem, &file_index, &error) != DW_DLV_OK) {
file_index = 0; file_index = 0;
} }
dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR); dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);
@ -3055,8 +3339,9 @@ private:
char **srcfiles = 0; char **srcfiles = 0;
Dwarf_Signed file_count = 0; Dwarf_Signed file_count = 0;
if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) { if (dwarf_srcfiles(cu_die, &srcfiles, &file_count, &error) == DW_DLV_OK) {
if (file_index <= file_count) if (file_count > 0 && file_index <= static_cast<Dwarf_Unsigned>(file_count)) {
file = std::string(srcfiles[file_index - 1]); file = std::string(srcfiles[file_index - 1]);
}
// Deallocate all strings! // Deallocate all strings!
for (int i = 0; i < file_count; ++i) { for (int i = 0; i < file_count; ++i) {
@ -3193,15 +3478,14 @@ template <>
class TraceResolverDarwinImpl<trace_resolver_tag::backtrace_symbol> class TraceResolverDarwinImpl<trace_resolver_tag::backtrace_symbol>
: public TraceResolverImplBase { : public TraceResolverImplBase {
public: public:
template <class ST> void load_stacktrace(ST &st) { void load_addresses(void *const*addresses, int address_count) override {
using namespace details; if (address_count == 0) {
if (st.size() == 0) {
return; return;
} }
_symbols.reset(backtrace_symbols(st.begin(), st.size())); _symbols.reset(backtrace_symbols(addresses, address_count));
} }
ResolvedTrace resolve(ResolvedTrace trace) { ResolvedTrace resolve(ResolvedTrace trace) override {
// parse: // parse:
// <n> <file> <addr> <mangled-name> + <offset> // <n> <file> <addr> <mangled-name> + <offset>
char *filename = _symbols[trace.idx]; char *filename = _symbols[trace.idx];
@ -3299,9 +3583,9 @@ public:
ret.base_address = mi.lpBaseOfDll; ret.base_address = mi.lpBaseOfDll;
ret.load_size = mi.SizeOfImage; ret.load_size = mi.SizeOfImage;
GetModuleFileNameEx(process, module, temp, sizeof(temp)); GetModuleFileNameExA(process, module, temp, sizeof(temp));
ret.image_name = temp; ret.image_name = temp;
GetModuleBaseName(process, module, temp, sizeof(temp)); GetModuleBaseNameA(process, module, temp, sizeof(temp));
ret.module_name = temp; ret.module_name = temp;
std::vector<char> img(ret.image_name.begin(), ret.image_name.end()); std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end()); std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
@ -3311,7 +3595,8 @@ public:
} }
}; };
template <> class TraceResolverImpl<system_tag::windows_tag> { template <> class TraceResolverImpl<system_tag::windows_tag>
: public TraceResolverImplBase {
public: public:
TraceResolverImpl() { TraceResolverImpl() {
@ -3336,8 +3621,6 @@ public:
image_type = h->FileHeader.Machine; image_type = h->FileHeader.Machine;
} }
template <class ST> void load_stacktrace(ST &) {}
static const int max_sym_len = 255; static const int max_sym_len = 255;
struct symbol_t { struct symbol_t {
SYMBOL_INFO sym; SYMBOL_INFO sym;
@ -3346,27 +3629,28 @@ public:
DWORD64 displacement; DWORD64 displacement;
ResolvedTrace resolve(ResolvedTrace t) { ResolvedTrace resolve(ResolvedTrace t) override {
HANDLE process = GetCurrentProcess(); HANDLE process = GetCurrentProcess();
char name[256]; char name[256];
memset(&sym, 0, sizeof sym); memset(&sym, 0, sizeof(sym));
sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO); sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);
sym.sym.MaxNameLen = max_sym_len; sym.sym.MaxNameLen = max_sym_len;
if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) { if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {
// TODO: error handling everywhere // TODO: error handling everywhere
LPTSTR lpMsgBuf; char* lpMsgBuf;
DWORD dw = GetLastError(); DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL); (char*)&lpMsgBuf, 0, NULL)) {
std::fprintf(stderr, "%s\n", lpMsgBuf);
printf(lpMsgBuf); LocalFree(lpMsgBuf);
}
// abort(); // abort();
} }
@ -3605,7 +3889,7 @@ private:
cfile_streambuf(FILE *_sink) : sink(_sink) {} cfile_streambuf(FILE *_sink) : sink(_sink) {}
int_type underflow() override { return traits_type::eof(); } int_type underflow() override { return traits_type::eof(); }
int_type overflow(int_type ch) override { int_type overflow(int_type ch) override {
if (traits_type::not_eof(ch) && fwrite(&ch, sizeof ch, 1, sink) == 1) { if (traits_type::not_eof(ch) && fputc(ch, sink) != EOF) {
return ch; return ch;
} }
return traits_type::eof(); return traits_type::eof();
@ -3928,13 +4212,19 @@ public:
#elif defined(__arm__) #elif defined(__arm__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.arm_pc); error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.arm_pc);
#elif defined(__aarch64__) #elif defined(__aarch64__)
#if defined(__APPLE__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__pc);
#else
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.pc); error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.pc);
#endif
#elif defined(__mips__) #elif defined(__mips__)
error_addr = reinterpret_cast<void *>( error_addr = reinterpret_cast<void *>(
reinterpret_cast<struct sigcontext *>(&uctx->uc_mcontext)->sc_pc); reinterpret_cast<struct sigcontext *>(&uctx->uc_mcontext)->sc_pc);
#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \ #elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \
defined(__POWERPC__) defined(__POWERPC__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.regs->nip); error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.regs->nip);
#elif defined(__riscv)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.__gregs[REG_PC]);
#elif defined(__s390x__) #elif defined(__s390x__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.psw.addr); error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.psw.addr);
#elif defined(__APPLE__) && defined(__x86_64__) #elif defined(__APPLE__) && defined(__x86_64__)
@ -3945,9 +4235,10 @@ public:
#warning ":/ sorry, ain't know no nothing none not of your architecture!" #warning ":/ sorry, ain't know no nothing none not of your architecture!"
#endif #endif
if (error_addr) { if (error_addr) {
st.load_from(error_addr, 32); st.load_from(error_addr, 32, reinterpret_cast<void *>(uctx),
info->si_addr);
} else { } else {
st.load_here(32); st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr);
} }
Printer printer; Printer printer;
@ -4016,7 +4307,9 @@ public:
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
std::set_terminate(&terminator); std::set_terminate(&terminator);
#ifndef BACKWARD_ATLEAST_CXX17
std::set_unexpected(&terminator); std::set_unexpected(&terminator);
#endif
_set_purecall_handler(&terminator); _set_purecall_handler(&terminator);
_set_invalid_parameter_handler(&invalid_parameter_handler); _set_invalid_parameter_handler(&invalid_parameter_handler);
} }
@ -4144,9 +4437,8 @@ private:
StackTrace st; StackTrace st;
st.set_machine_type(printer.resolver().machine_type()); st.set_machine_type(printer.resolver().machine_type());
st.set_context(ctx());
st.set_thread_handle(thread_handle()); st.set_thread_handle(thread_handle());
st.load_here(32 + skip_frames); st.load_here(32 + skip_frames, ctx());
st.skip_n_firsts(skip_frames); st.skip_n_firsts(skip_frames);
printer.address = true; printer.address = true;