Update backward to 1.6
This commit is contained in:
parent
ec3c8e7701
commit
b58b8537b5
|
@ -35,6 +35,9 @@
|
|||
#define BACKWARD_CXX11
|
||||
#define BACKWARD_ATLEAST_CXX11
|
||||
#define BACKWARD_ATLEAST_CXX98
|
||||
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
#define BACKWARD_ATLEAST_CXX17
|
||||
#endif
|
||||
#else
|
||||
#define BACKWARD_CXX98
|
||||
#define BACKWARD_ATLEAST_CXX98
|
||||
|
@ -49,6 +52,9 @@
|
|||
// #define BACKWARD_SYSTEM_DARWIN
|
||||
// - specialization for Mac OS X 10.5 and later.
|
||||
//
|
||||
// #define BACKWARD_SYSTEM_WINDOWS
|
||||
// - specialization for Windows (Clang 9 and MSVC2017)
|
||||
//
|
||||
// #define BACKWARD_SYSTEM_UNKNOWN
|
||||
// - placebo implementation, does nothing.
|
||||
//
|
||||
|
@ -84,6 +90,8 @@
|
|||
#include <streambuf>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
|
||||
#if defined(BACKWARD_SYSTEM_LINUX)
|
||||
|
||||
|
@ -97,6 +105,11 @@
|
|||
// exception.
|
||||
// - 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
|
||||
// - 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
|
||||
|
@ -109,10 +122,13 @@
|
|||
// Note that only one of the define should be set to 1 at a time.
|
||||
//
|
||||
#if BACKWARD_HAS_UNWIND == 1
|
||||
#elif BACKWARD_HAS_LIBUNWIND == 1
|
||||
#elif BACKWARD_HAS_BACKTRACE == 1
|
||||
#else
|
||||
#undef BACKWARD_HAS_UNWIND
|
||||
#define BACKWARD_HAS_UNWIND 1
|
||||
#undef BACKWARD_HAS_LIBUNWIND
|
||||
#define BACKWARD_HAS_LIBUNWIND 0
|
||||
#undef BACKWARD_HAS_BACKTRACE
|
||||
#define BACKWARD_HAS_BACKTRACE 0
|
||||
#endif
|
||||
|
@ -127,7 +143,8 @@
|
|||
// - source filename
|
||||
// - line and column numbers
|
||||
// - 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":
|
||||
// - apt-get install libdw-dev
|
||||
// - g++/clang++ -ldw ...
|
||||
|
@ -150,7 +167,8 @@
|
|||
// - source filename
|
||||
// - line and column numbers
|
||||
// - 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":
|
||||
// - apt-get install libdwarf-dev
|
||||
// - g++/clang++ -ldwarf ...
|
||||
|
@ -257,6 +275,12 @@
|
|||
// exception.
|
||||
// - 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
|
||||
// - backtrace is available by default, though it does not produce as much
|
||||
// information as another library might.
|
||||
|
@ -268,11 +292,14 @@
|
|||
//
|
||||
#if BACKWARD_HAS_UNWIND == 1
|
||||
#elif BACKWARD_HAS_BACKTRACE == 1
|
||||
#elif BACKWARD_HAS_LIBUNWIND == 1
|
||||
#else
|
||||
#undef BACKWARD_HAS_UNWIND
|
||||
#define BACKWARD_HAS_UNWIND 1
|
||||
#undef BACKWARD_HAS_BACKTRACE
|
||||
#define BACKWARD_HAS_BACKTRACE 0
|
||||
#undef BACKWARD_HAS_LIBUNWIND
|
||||
#define BACKWARD_HAS_LIBUNWIND 0
|
||||
#endif
|
||||
|
||||
// On Darwin, backward can extract detailed information about a stack trace
|
||||
|
@ -327,6 +354,11 @@ typedef SSIZE_T ssize_t;
|
|||
#define NOINLINE __declspec(noinline)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "psapi.lib")
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#endif
|
||||
|
||||
// Comment / packing is from stackoverflow:
|
||||
// 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
|
||||
|
@ -370,6 +402,11 @@ extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context *, int *);
|
|||
|
||||
#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
|
||||
#include <unordered_map>
|
||||
#include <utility> // for std::swap
|
||||
|
@ -529,7 +566,7 @@ typedef pdb_symbol current;
|
|||
|
||||
void update(T new_val) {
|
||||
_val = new_val;
|
||||
_empty = static_cast<bool>(new_val);
|
||||
_empty = !static_cast<bool>(new_val);
|
||||
}
|
||||
|
||||
operator const dummy *() const {
|
||||
|
@ -681,14 +718,17 @@ typedef pdb_symbol current;
|
|||
size_t size() const { return 0; }
|
||||
Trace operator[](size_t) const { return Trace(); }
|
||||
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; }
|
||||
void skip_n_firsts(size_t) {}
|
||||
};
|
||||
|
||||
class StackTraceImplBase {
|
||||
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; }
|
||||
|
||||
|
@ -716,17 +756,27 @@ typedef pdb_symbol current;
|
|||
#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; }
|
||||
|
||||
private:
|
||||
size_t _thread_id;
|
||||
size_t _skip;
|
||||
void *_context;
|
||||
void *_error_addr;
|
||||
};
|
||||
|
||||
class StackTraceImplHolder : public StackTraceImplBase {
|
||||
public:
|
||||
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 {
|
||||
if (idx >= size()) {
|
||||
|
@ -807,8 +857,11 @@ typedef pdb_symbol current;
|
|||
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
|
||||
public:
|
||||
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();
|
||||
set_context(context);
|
||||
set_error_addr(error_addr);
|
||||
if (depth == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -818,8 +871,9 @@ typedef pdb_symbol current;
|
|||
skip_n_firsts(0);
|
||||
return size();
|
||||
}
|
||||
size_t load_from(void *addr, size_t depth = 32) {
|
||||
load_here(depth + 8);
|
||||
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) {
|
||||
|
@ -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)
|
||||
|
||||
template <>
|
||||
class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder {
|
||||
public:
|
||||
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();
|
||||
if (depth == 0) {
|
||||
return 0;
|
||||
|
@ -859,8 +1093,9 @@ public:
|
|||
return size();
|
||||
}
|
||||
|
||||
size_t load_from(void *addr, size_t depth = 32) {
|
||||
load_here(depth + 8);
|
||||
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) {
|
||||
|
@ -887,8 +1122,10 @@ public:
|
|||
void set_thread_handle(HANDLE handle) { thd_ = handle; }
|
||||
|
||||
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
|
||||
|
||||
if (depth == 0) {
|
||||
|
@ -950,8 +1187,9 @@ public:
|
|||
return size();
|
||||
}
|
||||
|
||||
size_t load_from(void *addr, size_t depth = 32) {
|
||||
load_here(depth + 8);
|
||||
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) {
|
||||
|
@ -976,19 +1214,21 @@ private:
|
|||
|
||||
/*************** 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 {
|
||||
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:
|
||||
std::string demangle(const char *funcname) {
|
||||
return _demangler.demangle(funcname);
|
||||
|
@ -998,6 +1238,15 @@ public:
|
|||
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
|
||||
|
||||
class TraceResolverLinuxBase : public TraceResolverImplBase {
|
||||
|
@ -1014,6 +1263,12 @@ public:
|
|||
// variable; In that case, we actually open /proc/self/exe, which
|
||||
// is always the actual executable (even if it was deleted/replaced!)
|
||||
// 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";
|
||||
return exec_path_;
|
||||
} else {
|
||||
|
@ -1062,15 +1317,14 @@ public:
|
|||
class TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol>
|
||||
: public TraceResolverLinuxBase {
|
||||
public:
|
||||
template <class ST> void load_stacktrace(ST &st) {
|
||||
using namespace details;
|
||||
if (st.size() == 0) {
|
||||
void load_addresses(void *const*addresses, int address_count) override {
|
||||
if (address_count == 0) {
|
||||
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 *funcname = filename;
|
||||
while (*funcname && *funcname != '(') {
|
||||
|
@ -1107,9 +1361,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libbfd>
|
|||
public:
|
||||
TraceResolverLinuxImpl() : _bfd_loaded(false) {}
|
||||
|
||||
template <class ST> void load_stacktrace(ST &) {}
|
||||
|
||||
ResolvedTrace resolve(ResolvedTrace trace) {
|
||||
ResolvedTrace resolve(ResolvedTrace trace) override {
|
||||
Dl_info symbol_info;
|
||||
|
||||
// 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);
|
||||
bfd_fileobject &fobj = load_object_with_bfd(symbol_info.dli_fname);
|
||||
if (!fobj.handle) {
|
||||
return trace; // sad, we couldn't load the object :(
|
||||
bfd_fileobject *fobj;
|
||||
// Before rushing to resolution need to ensure the executable
|
||||
// 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.
|
||||
|
@ -1276,7 +1564,7 @@ private:
|
|||
typedef details::hashtable<std::string, bfd_fileobject>::type fobj_bfd_map_t;
|
||||
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;
|
||||
|
||||
if (!_bfd_loaded) {
|
||||
|
@ -1287,11 +1575,11 @@ private:
|
|||
|
||||
fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object);
|
||||
if (it != _fobj_bfd_map.end()) {
|
||||
return it->second;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
// 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;
|
||||
bfd_handle_t bfd_handle;
|
||||
|
@ -1340,9 +1628,9 @@ private:
|
|||
return r; // damned, that's a stripped file that you got there!
|
||||
}
|
||||
|
||||
r.handle = move(bfd_handle);
|
||||
r.symtab = move(symtab);
|
||||
r.dynamic_symtab = move(dynamic_symtab);
|
||||
r->handle = move(bfd_handle);
|
||||
r->symtab = move(symtab);
|
||||
r->dynamic_symtab = move(dynamic_symtab);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -1361,15 +1649,15 @@ private:
|
|||
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) {
|
||||
find_sym_context context;
|
||||
context.self = this;
|
||||
context.fobj = &fobj;
|
||||
context.fobj = fobj;
|
||||
context.addr = addr;
|
||||
context.base_addr = base_addr;
|
||||
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));
|
||||
return context.result;
|
||||
}
|
||||
|
@ -1378,24 +1666,24 @@ private:
|
|||
find_sym_context *context = static_cast<find_sym_context *>(data);
|
||||
context->self->find_in_section(
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (result.found)
|
||||
return;
|
||||
|
||||
#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
|
||||
if ((bfd_section_flags(section) & SEC_ALLOC) == 0)
|
||||
#endif
|
||||
return; // a debug section is never loaded automatically.
|
||||
|
||||
#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
|
||||
bfd_vma sec_addr = bfd_section_vma(section);
|
||||
#endif
|
||||
|
@ -1417,15 +1705,15 @@ private:
|
|||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
|
||||
#endif
|
||||
if (!result.found && fobj.symtab) {
|
||||
if (!result.found && fobj->symtab) {
|
||||
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);
|
||||
}
|
||||
|
||||
if (!result.found && fobj.dynamic_symtab) {
|
||||
if (!result.found && fobj->dynamic_symtab) {
|
||||
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);
|
||||
}
|
||||
#if defined(__clang__)
|
||||
|
@ -1434,13 +1722,13 @@ private:
|
|||
}
|
||||
|
||||
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
|
||||
// find_symbol_details. The state is global to the bfd_handle.
|
||||
ResolvedTrace::source_locs_t results;
|
||||
while (previous_result.found) {
|
||||
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);
|
||||
|
||||
if (result
|
||||
|
@ -1483,9 +1771,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libdw>
|
|||
public:
|
||||
TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {}
|
||||
|
||||
template <class ST> void load_stacktrace(ST &) {}
|
||||
|
||||
ResolvedTrace resolve(ResolvedTrace trace) {
|
||||
ResolvedTrace resolve(ResolvedTrace trace) override {
|
||||
using namespace details;
|
||||
|
||||
Dwarf_Addr trace_addr = (Dwarf_Addr)trace.addr;
|
||||
|
@ -1778,9 +2064,9 @@ private:
|
|||
|
||||
static const char *die_call_file(Dwarf_Die *die) {
|
||||
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) {
|
||||
return 0;
|
||||
|
@ -1812,9 +2098,7 @@ class TraceResolverLinuxImpl<trace_resolver_tag::libdwarf>
|
|||
public:
|
||||
TraceResolverLinuxImpl() : _dwarf_loaded(false) {}
|
||||
|
||||
template <class ST> void load_stacktrace(ST &) {}
|
||||
|
||||
ResolvedTrace resolve(ResolvedTrace trace) {
|
||||
ResolvedTrace resolve(ResolvedTrace trace) override {
|
||||
// trace.addr is a virtual address in memory pointing to some code.
|
||||
// Let's try to find from which loaded object it comes from.
|
||||
// The loaded object can be yourself btw.
|
||||
|
@ -3038,12 +3322,12 @@ private:
|
|||
Dwarf_Die cu_die) {
|
||||
Dwarf_Attribute attr_mem;
|
||||
Dwarf_Error error = DW_DLE_NE;
|
||||
Dwarf_Signed file_index;
|
||||
Dwarf_Unsigned file_index;
|
||||
|
||||
std::string file;
|
||||
|
||||
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;
|
||||
}
|
||||
dwarf_dealloc(dwarf, attr_mem, DW_DLA_ATTR);
|
||||
|
@ -3055,8 +3339,9 @@ private:
|
|||
char **srcfiles = 0;
|
||||
Dwarf_Signed file_count = 0;
|
||||
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]);
|
||||
}
|
||||
|
||||
// Deallocate all strings!
|
||||
for (int i = 0; i < file_count; ++i) {
|
||||
|
@ -3193,15 +3478,14 @@ template <>
|
|||
class TraceResolverDarwinImpl<trace_resolver_tag::backtrace_symbol>
|
||||
: public TraceResolverImplBase {
|
||||
public:
|
||||
template <class ST> void load_stacktrace(ST &st) {
|
||||
using namespace details;
|
||||
if (st.size() == 0) {
|
||||
void load_addresses(void *const*addresses, int address_count) override {
|
||||
if (address_count == 0) {
|
||||
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:
|
||||
// <n> <file> <addr> <mangled-name> + <offset>
|
||||
char *filename = _symbols[trace.idx];
|
||||
|
@ -3299,9 +3583,9 @@ public:
|
|||
ret.base_address = mi.lpBaseOfDll;
|
||||
ret.load_size = mi.SizeOfImage;
|
||||
|
||||
GetModuleFileNameEx(process, module, temp, sizeof(temp));
|
||||
GetModuleFileNameExA(process, module, temp, sizeof(temp));
|
||||
ret.image_name = temp;
|
||||
GetModuleBaseName(process, module, temp, sizeof(temp));
|
||||
GetModuleBaseNameA(process, module, temp, sizeof(temp));
|
||||
ret.module_name = temp;
|
||||
std::vector<char> img(ret.image_name.begin(), ret.image_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:
|
||||
TraceResolverImpl() {
|
||||
|
||||
|
@ -3336,8 +3621,6 @@ public:
|
|||
image_type = h->FileHeader.Machine;
|
||||
}
|
||||
|
||||
template <class ST> void load_stacktrace(ST &) {}
|
||||
|
||||
static const int max_sym_len = 255;
|
||||
struct symbol_t {
|
||||
SYMBOL_INFO sym;
|
||||
|
@ -3346,27 +3629,28 @@ public:
|
|||
|
||||
DWORD64 displacement;
|
||||
|
||||
ResolvedTrace resolve(ResolvedTrace t) {
|
||||
ResolvedTrace resolve(ResolvedTrace t) override {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
|
||||
char name[256];
|
||||
|
||||
memset(&sym, 0, sizeof sym);
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
sym.sym.SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
sym.sym.MaxNameLen = max_sym_len;
|
||||
|
||||
if (!SymFromAddr(process, (ULONG64)t.addr, &displacement, &sym.sym)) {
|
||||
// TODO: error handling everywhere
|
||||
LPTSTR lpMsgBuf;
|
||||
char* lpMsgBuf;
|
||||
DWORD dw = GetLastError();
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPTSTR)&lpMsgBuf, 0, NULL);
|
||||
|
||||
printf(lpMsgBuf);
|
||||
(char*)&lpMsgBuf, 0, NULL)) {
|
||||
std::fprintf(stderr, "%s\n", lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
|
||||
// abort();
|
||||
}
|
||||
|
@ -3605,7 +3889,7 @@ private:
|
|||
cfile_streambuf(FILE *_sink) : sink(_sink) {}
|
||||
int_type underflow() override { return traits_type::eof(); }
|
||||
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 traits_type::eof();
|
||||
|
@ -3928,13 +4212,19 @@ public:
|
|||
#elif defined(__arm__)
|
||||
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.arm_pc);
|
||||
#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);
|
||||
#endif
|
||||
#elif defined(__mips__)
|
||||
error_addr = reinterpret_cast<void *>(
|
||||
reinterpret_cast<struct sigcontext *>(&uctx->uc_mcontext)->sc_pc);
|
||||
#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \
|
||||
defined(__POWERPC__)
|
||||
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__)
|
||||
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.psw.addr);
|
||||
#elif defined(__APPLE__) && defined(__x86_64__)
|
||||
|
@ -3945,9 +4235,10 @@ public:
|
|||
#warning ":/ sorry, ain't know no nothing none not of your architecture!"
|
||||
#endif
|
||||
if (error_addr) {
|
||||
st.load_from(error_addr, 32);
|
||||
st.load_from(error_addr, 32, reinterpret_cast<void *>(uctx),
|
||||
info->si_addr);
|
||||
} else {
|
||||
st.load_here(32);
|
||||
st.load_here(32, reinterpret_cast<void *>(uctx), info->si_addr);
|
||||
}
|
||||
|
||||
Printer printer;
|
||||
|
@ -4016,7 +4307,9 @@ public:
|
|||
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
|
||||
|
||||
std::set_terminate(&terminator);
|
||||
#ifndef BACKWARD_ATLEAST_CXX17
|
||||
std::set_unexpected(&terminator);
|
||||
#endif
|
||||
_set_purecall_handler(&terminator);
|
||||
_set_invalid_parameter_handler(&invalid_parameter_handler);
|
||||
}
|
||||
|
@ -4144,9 +4437,8 @@ private:
|
|||
|
||||
StackTrace st;
|
||||
st.set_machine_type(printer.resolver().machine_type());
|
||||
st.set_context(ctx());
|
||||
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);
|
||||
|
||||
printer.address = true;
|
||||
|
|
Loading…
Reference in New Issue