/* * backward.hpp * Copyright 2013 Google Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
*/
// You can define one of the following (or leave it to the auto-detection): // // #define BACKWARD_SYSTEM_LINUX // - specialization for linux // // #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. // #ifdefined(BACKWARD_SYSTEM_LINUX) #elifdefined(BACKWARD_SYSTEM_DARWIN) #elifdefined(BACKWARD_SYSTEM_UNKNOWN) #elifdefined(BACKWARD_SYSTEM_WINDOWS) #else #ifdefined(__linux) || defined(__linux__) #define BACKWARD_SYSTEM_LINUX #elifdefined(__APPLE__) #define BACKWARD_SYSTEM_DARWIN #elifdefined(_WIN32) #define BACKWARD_SYSTEM_WINDOWS #else #define BACKWARD_SYSTEM_UNKNOWN #endif #endif
// On linux, backtrace can back-trace or "walk" the stack using the following // libraries: // // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtime in gcc/clang for stack unwinding on // 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 // sadly really important in order to get perfectly accurate stack traces. // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // 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
// On linux, backward can extract detailed information about a stack trace // using one of the following libraries: // // #define BACKWARD_HAS_DW 1 // - libdw gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - 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 ... // // #define BACKWARD_HAS_BFD 1 // - With libbfd, you get a fair amount of details: // - object filename // - function name // - source filename // - line numbers // - source code snippet (assuming the file is accessible) // - You need to link with the lib "bfd": // - apt-get install binutils-dev // - g++/clang++ -lbfd ... // // #define BACKWARD_HAS_DWARF 1 // - libdwarf gives you the most juicy details out of your stack traces: // - object filename // - function name // - source filename // - line and column numbers // - source code snippet (assuming the file is accessible) // - 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 ... // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // - backtrace is part of the (e)glib library. // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // // Note that only one of the define should be set to 1 at a time. // #if BACKWARD_HAS_DW == 1 #elif BACKWARD_HAS_BFD == 1 #elif BACKWARD_HAS_DWARF == 1 #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #else #undef BACKWARD_HAS_DW #define BACKWARD_HAS_DW 0 #undef BACKWARD_HAS_BFD #define BACKWARD_HAS_BFD 0 #undef BACKWARD_HAS_DWARF #define BACKWARD_HAS_DWARF 0 #undef BACKWARD_HAS_BACKTRACE_SYMBOL #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 #endif
#include <cxxabi.h> #include <fcntl.h> #ifdef __ANDROID__ // Old Android API levels define _Unwind_Ptr in both link.h and // unwind.h Rename the one in link.h as we are not going to be using // it #define _Unwind_Ptr _Unwind_Ptr_Custom #include <link.h> #undef _Unwind_Ptr #else #include <link.h> #endif #ifdefined(__ppc__) || defined(__powerpc) || defined(__powerpc__) \
|| defined(__POWERPC__) // Linux kernel header required for the struct pt_regs definition // to access the NIP (Next Instruction Pointer) register value #include <asm/ptrace.h> #endif #include <signal.h> #include <sys/stat.h> #include <syscall.h> #include <unistd.h> #ifndef _GNU_SOURCE #define _GNU_SOURCE #include <dlfcn.h> #undef _GNU_SOURCE #else #include <dlfcn.h> #endif
#if BACKWARD_HAS_BFD == 1 // NOTE: defining PACKAGE{,_VERSION} is required before including // bfd.h on some platforms, see also: // https://sourceware.org/bugzilla/show_bug.cgi?id=14243 #ifndef PACKAGE #define PACKAGE #endif #ifndef PACKAGE_VERSION #define PACKAGE_VERSION #endif #include <bfd.h> #endif
#if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1) // then we shall rely on backtrace #include <execinfo.h> #endif
#endif// defined(BACKWARD_SYSTEM_LINUX)
#ifdefined(BACKWARD_SYSTEM_DARWIN) // On Darwin, backtrace can back-trace or "walk" the stack using the following // libraries: // #define BACKWARD_HAS_UNWIND 1 // - unwind comes from libgcc, but I saw an equivalent inside clang itself. // - with unwind, the stacktrace is as accurate as it can possibly be, since // this is used by the C++ runtime in gcc/clang for stack unwinding on // 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. // // The default is: // #define BACKWARD_HAS_UNWIND == 1 // // Note that only one of the define should be set to 1 at a time. // #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 // using one of the following libraries: // // #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 // - backtrace provides minimal details for a stack trace: // - object filename // - function name // // The default is: // #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1 // #ifdefined(BACKWARD_HAS_BACKTRACE_SYMBOL) && BACKWARD_HAS_BACKTRACE_SYMBOL == 1 #else #undef BACKWARD_HAS_BACKTRACE_SYMBOL #define BACKWARD_HAS_BACKTRACE_SYMBOL 1 #endif
// TODO maybe these should be undefined somewhere else? #undef BACKWARD_HAS_UNWIND #undef BACKWARD_HAS_BACKTRACE #if BACKWARD_HAS_PDB_SYMBOL == 1 #else #undef BACKWARD_HAS_PDB_SYMBOL #define BACKWARD_HAS_PDB_SYMBOL 1 #endif
#endif
#if BACKWARD_HAS_UNWIND == 1
#include <unwind.h> // while gcc's unwind.h defines something like that: // extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *); // extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *); // // clang's unwind.h defines something like this: // uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context); // // Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we // cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr // anyway. // // Luckily we can play on the fact that the guard macros have a different name: #ifdef __CLANG_UNWIND_H // In fact, this function still comes from libgcc (on my different linux boxes, // clang links against libgcc). #include <inttypes.h> extern"C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*); #endif
namespace system_tag { struct linux_tag; // seems that I cannot call that "linux" because the name // is already defined... so I am adding _tag everywhere. struct darwin_tag; struct windows_tag; struct unknown_tag;
#ifdefined(BACKWARD_SYSTEM_LINUX) typedef linux_tag current_tag; #elifdefined(BACKWARD_SYSTEM_DARWIN) typedef darwin_tag current_tag; #elifdefined(BACKWARD_SYSTEM_WINDOWS) typedef windows_tag current_tag; #elifdefined(BACKWARD_SYSTEM_UNKNOWN) typedef unknown_tag current_tag; #else #error"May I please get my system defines?" #endif
} // namespace system_tag
#if BACKWARD_HAS_DW == 1 typedef libdw current; #elif BACKWARD_HAS_BFD == 1 typedef libbfd current; #elif BACKWARD_HAS_DWARF == 1 typedef libdwarf current; #elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; #else #error"You shall not pass, until you know what you want." #endif #elifdefined(BACKWARD_SYSTEM_DARWIN) struct backtrace_symbol;
#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1 typedef backtrace_symbol current; #else #error"You shall not pass, until you know what you want." #endif #elifdefined(BACKWARD_SYSTEM_WINDOWS) struct pdb_symbol; #if BACKWARD_HAS_PDB_SYMBOL == 1 typedef pdb_symbol current; #else #error"You shall not pass, until you know what you want." #endif #endif
} // namespace trace_resolver_tag
namespace details {
template <typename T> struct rm_ptr { typedef T type;
};
template <typename T> struct rm_ptr<T*> { typedef T type;
};
operatorconst dummy*() const { if (_empty) { return nullptr;
} returnreinterpret_cast<const dummy*>(_val);
}
T get() { return _val;
}
T release() {
_empty = true; return _val;
} void swap(handle& b) { using std::swap;
swap(b._val, _val); // can throw, we are safe here.
swap(b._empty, _empty); // should not throw: if you cannot swap two // bools without throwing... It's a lost cause anyway!
}
// In which binary object this trace is located.
std::string object_filename;
// The function in the object that contain the trace. This is not the same // as source.function which can be an function inlined in object_function.
std::string object_function;
// The source location of this trace. It is possible for filename to be // empty and for line/col to be invalid (value 0) if this information // couldn't be deduced, for example if there is no debug information in the // binary object.
SourceLoc source;
// An optionals list of "inliners". All the successive sources location // from where the source location of the trace (the attribute right above) // is inlined. It is especially useful when you compiled with optimization. typedef std::vector<SourceLoc> source_locs_t;
source_locs_t inliners;
protected: void load_thread_info() { #ifdef BACKWARD_SYSTEM_LINUX #ifndef __ANDROID__
_thread_id = static_cast<size_t>(syscall(SYS_gettid)); #else
_thread_id = static_cast<size_t>(gettid()); #endif if (_thread_id == static_cast<size_t>(getpid())) { // If the thread is the main one, let's hide that. // I like to keep little secret sometimes.
_thread_id = 0;
} #elifdefined(BACKWARD_SYSTEM_DARWIN)
_thread_id = reinterpret_cast<size_t>(pthread_self()); if (pthread_main_np() == 1) { // If the thread is the main one, let's hide that.
_thread_id = 0;
} #endif
}
int ip_before_instruction = 0;
uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
if (!ip_before_instruction) { // calculating 0-1 for unsigned, looks like a possible bug to // sanitizers, so let's do it explicitly: if (ip == 0) {
ip = std::numeric_limits<uintptr_t>::max(); // set it to 0xffff... // (as from casting // 0-1)
} else {
ip -= 1; // else just normally decrement it (no overflow/underflow // will happen)
}
}
if (_index >= 0) { // ignore first frame.
(*_f)(static_cast<size_t>(_index), reinterpret_cast<void*>(ip));
}
_index += 1; return _URC_NO_REASON;
}
};
// 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); #elifdefined(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); #elifdefined(__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<unsignedlong>(error_addr())
== uctx->uc_mcontext.arm_pc) {
ctx.regs[UNW_ARM_R15]
= uctx->uc_mcontext.arm_lr - sizeof(unsignedlong);
}
_stacktrace[index] = reinterpret_cast<void*>(ctx.regs[UNW_ARM_R15]);
++index; #elifdefined(__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; #elifdefined(__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()) { #ifdefined(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;
template <> class StackTraceImpl<system_tag::current_tag> : public StackTraceImplHolder { public: // We have to load the machine type from the image info // So we first initialize the resolver, and it tells us this info void set_machine_type(DWORD machine_type) {
machine_type_ = machine_type;
} void set_context(CONTEXT* ctx) {
ctx_ = ctx;
} void set_thread_handle(HANDLE handle) {
thd_ = handle;
}
NOINLINE
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) { return 0;
}
if (!ctx_) {
ctx_ = &localCtx;
RtlCaptureContext(ctx_);
}
for (;;) { // NOTE: this only works if PDBs are already loaded!
SetLastError(0); if (!StackWalk64(machine_type_,
process,
thd_,
&s,
ctx_,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL)) break;
template <> class TraceResolverImpl<system_tag::unknown_tag>
: public TraceResolverImplBase {};
#endif
#ifdef BACKWARD_SYSTEM_LINUX
class TraceResolverLinuxBase : public TraceResolverImplBase { public:
TraceResolverLinuxBase()
: argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {}
std::string resolve_exec_path(Dl_info& symbol_info) const { // mutates symbol_info.dli_fname to be filename to open and returns // filename to display if (symbol_info.dli_fname == argv0_) { // dladdr returns argv[0] in dli_fname for symbols contained in // the main executable, which is not a valid path if the // executable was found by a search of the PATH environment // 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 { return symbol_info.dli_fname;
}
}
// 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. if (!dladdr(trace.addr, &symbol_info)) { return trace; // dat broken trace...
}
// Now we get in symbol_info: // .dli_fname: // pathname of the shared object that contains the address. // .dli_fbase: // where the object is loaded in memory. // .dli_sname: // the name of the nearest symbol to trace.addr, we expect a // function name. // .dli_saddr: // the exact address corresponding to .dli_sname.
if (symbol_info.dli_sname) {
trace.object_function = demangle(symbol_info.dli_sname);
}
if (!symbol_info.dli_fname) { return trace;
}
trace.object_filename = resolve_exec_path(symbol_info);
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.
// trace.addr is the next instruction to be executed after returning // from the nested stack frame. In C++ this usually relate to the next // statement right after the function call that leaded to a new stack // frame. This is not usually what you want to see when printing out a // stacktrace...
find_sym_result details_call_site
= find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);
details_selected = &details_call_site;
#if BACKWARD_HAS_UNWIND == 0 // ...this is why we also try to resolve the symbol that is right // before the return address. If we are lucky enough, we will get the // line of the function that was called. But if the code is optimized, // we might get something absolutely not related since the compiler // can reschedule the return address with inline functions and // tail-call optimization (among other things that I don't even know // or cannot even dream about with my tiny limited brain).
find_sym_result details_adjusted_call_site = find_symbol_details(
fobj, (void*) (uintptr_t(trace.addr) - 1), symbol_info.dli_fbase);
// In debug mode, we should always get the right thing(TM). if (details_call_site.found && details_adjusted_call_site.found) { // Ok, we assume that details_adjusted_call_site is a better estimation.
details_selected = &details_adjusted_call_site;
trace.addr = (void*) (uintptr_t(trace.addr) - 1);
}
if (details_selected == &details_call_site && details_call_site.found) { // we have to re-resolve the symbol in order to reset some // internal state in BFD... so we can call backtrace_inliners // thereafter...
details_call_site
= find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);
} #endif// BACKWARD_HAS_UNWIND
if (details_selected->found) { if (details_selected->filename) {
trace.source.filename = details_selected->filename;
}
trace.source.line = details_selected->line;
if (details_selected->funcname) { // this time we get the name of the function where the code is // located, instead of the function were the address is // located. In short, if the code was inlined, we get the // function corresponding to the code. Else we already got in // trace.function.
trace.source.function = demangle(details_selected->funcname);
if (!symbol_info.dli_sname) { // for the case dladdr failed to find the symbol name of // the function, we might as well try to put something // here.
trace.object_function = trace.source.function;
}
}
// Maybe the source of the trace got inlined inside the function // (trace.source.function). Let's see if we can get all the inlined // calls along the way up to the initial call site.
trace.inliners = backtrace_inliners(fobj, *details_selected);
#if 0 if (trace.inliners.size() == 0) { // Maybe the trace was not inlined... or maybe it was and we // are lacking the debug information. Let's try to make the // world better and see if we can get the line number of the // function (trace.source.function) now. // // We will get the location of where the function start (to be // exact: the first instruction that really start the // function), not where the name of the function is defined. // This can be quite far away from the name of the function // btw. // // If the source of the function is the same as the source of // the trace, we cannot say if the trace was really inlined or // not. However, if the filename of the source is different // between the function and the trace... we can declare it as // an inliner. This is not 100% accurate, but better than // nothing.
if (symbol_info.dli_saddr) {
find_sym_result details = find_symbol_details(fobj,
symbol_info.dli_saddr,
symbol_info.dli_fbase);
if (details.found) {
ResolvedTrace::SourceLoc diy_inliner;
diy_inliner.line = details.line; if (details.filename) {
diy_inliner.filename = details.filename;
} if (details.funcname) {
diy_inliner.function = demangle(details.funcname);
} else {
diy_inliner.function = trace.source.function;
} if (diy_inliner != trace.source) {
trace.inliners.push_back(diy_inliner);
}
}
}
} #endif
}
// are we in the boundaries of the section? if (addr < sec_addr || addr >= sec_addr + size) {
addr -= base_addr; // oops, a relocated object, lets try again... if (addr < sec_addr || addr >= sec_addr + size) { return;
}
}
ResolvedTrace::source_locs_t
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.funcname,
&result.line);
if (result.found) /* and not ( cstrings_eq(previous_result.filename, result.filename) and cstrings_eq(previous_result.funcname, result.funcname) and result.line == previous_result.line
)) */
{
ResolvedTrace::SourceLoc src_loc;
src_loc.line = result.line; if (result.filename) {
src_loc.filename = result.filename;
} if (result.funcname) {
src_loc.function = demangle(result.funcname);
}
results.push_back(src_loc);
}
previous_result = result;
} return results;
}
bool cstrings_eq(constchar* a, constchar* b) { if (!a || !b) { returnfalse;
} return strcmp(a, b) == 0;
}
}; #endif// BACKWARD_HAS_BFD == 1
#if BACKWARD_HAS_DW == 1
template <> class TraceResolverLinuxImpl<trace_resolver_tag::libdw>
: public TraceResolverLinuxBase { public:
TraceResolverLinuxImpl() : _dwfl_handle_initialized(false) {}
// ...from the current process.
dwfl_report_begin(_dwfl_handle.get()); int r = dwfl_linux_proc_report(_dwfl_handle.get(), getpid());
dwfl_report_end(_dwfl_handle.get(), NULL, NULL); if (r < 0) { return trace;
}
}
if (!_dwfl_handle) { return trace;
}
// find the module (binary object) that contains the trace's address. // This is not using any debug information, but the addresses ranges of // all the currently loaded binary object.
Dwfl_Module* mod = dwfl_addrmodule(_dwfl_handle.get(), trace_addr); if (mod) { // now that we found it, lets get the name of it, this will be the // full path to the running binary or one of the loaded library. constchar* module_name = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); if (module_name) {
trace.object_filename = module_name;
} // We also look after the name of the symbol, equal or before this // address. This is found by walking the symtab. We should get the // symbol corresponding to the function (mangled) containing the // address. If the code corresponding to the address was inlined, // this is the name of the out-most inliner function. constchar* sym_name = dwfl_module_addrname(mod, trace_addr); if (sym_name) {
trace.object_function = demangle(sym_name);
}
}
// now let's get serious, and find out the source location (file and // line number) of the address.
// This function will look in .debug_aranges for the address and map it // to the location of the compilation unit DIE in .debug_info and // return it.
Dwarf_Addr mod_bias = 0;
Dwarf_Die* cudie = dwfl_module_addrdie(mod, trace_addr, &mod_bias);
#if 1 if (!cudie) { // Sadly clang does not generate the section .debug_aranges, thus // dwfl_module_addrdie will fail early. Clang doesn't either set // the lowpc/highpc/range info for every compilation unit. // // So in order to save the world: // for every compilation unit, we will iterate over every single // DIEs. Normally functions should have a lowpc/highpc/range, which // we will use to infer the compilation unit.
// note that this is probably badly inefficient. while ((cudie = dwfl_module_nextcu(mod, cudie, &mod_bias))) {
Dwarf_Die die_mem;
Dwarf_Die* fundie
= find_fundie_by_pc(cudie, trace_addr - mod_bias, &die_mem); if (fundie) { break;
}
}
} #endif
// #define BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE #ifdef BACKWARD_I_DO_NOT_RECOMMEND_TO_ENABLE_THIS_HORRIBLE_PIECE_OF_CODE if (!cudie) { // If it's still not enough, lets dive deeper in the shit, and try // to save the world again: for every compilation unit, we will // load the corresponding .debug_line section, and see if we can // find our address in it.
Dwarf_Addr bias; while ((cudie = dwfl_module_nextcu(mod, cudie, &bias))) { if (dwarf_getsrc_die(cudie, trace_addr - bias)) { // ...but if we get a match, it might be a false positive // because our (address - bias) might as well be valid in a // different compilation unit. So we throw our last card on // the table and lookup for the address into the .eh_frame // section.
if (!cudie) { return trace; // this time we lost the game :/
}
// Now that we have a compilation unit DIE, this function will be able // to load the corresponding section in .debug_line (if not already // loaded) and hopefully find the source location mapped to our // address.
Dwarf_Line* srcloc = dwarf_getsrc_die(cudie, trace_addr - mod_bias);
if (srcloc) { constchar* srcfile = dwarf_linesrc(srcloc, 0, 0); if (srcfile) {
trace.source.filename = srcfile;
} int line = 0, col = 0;
dwarf_lineno(srcloc, &line);
dwarf_linecol(srcloc, &col);
trace.source.line = static_cast<unsigned>(line);
trace.source.col = static_cast<unsigned>(col);
}
// defined here because in C++98, template function cannot take locally // defined types... grrr. struct inliners_search_cb { voidoperator()(Dwarf_Die* die) { switch (dwarf_tag(die)) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.