/*
* 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.
*/
#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89
#define H_6B9572DA_A64B_49E6_B234_051480991C89
#ifndef __cplusplus
#error "It's not going to compile without a C++ compiler..."
#endif
#if defined(BACKWARD_CXX11)
#elif defined(BACKWARD_CXX98)
#else
#if __cplusplus >= 201103L || (
defined(_MSC_VER) && _MSC_VER >= 1800)
#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
#endif
#endif
// 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.
//
#if defined(BACKWARD_SYSTEM_LINUX)
#elif defined(BACKWARD_SYSTEM_DARWIN)
#elif defined(BACKWARD_SYSTEM_UNKNOWN)
#elif defined(BACKWARD_SYSTEM_WINDOWS)
#else
#if defined(__linux) ||
defined(__linux__)
#define BACKWARD_SYSTEM_LINUX
#elif defined(__APPLE__)
#define BACKWARD_SYSTEM_DARWIN
#elif defined(_WIN32)
#define BACKWARD_SYSTEM_WINDOWS
#else
#define BACKWARD_SYSTEM_UNKNOWN
#endif
#endif
#define NOINLINE __attribute__((noinline))
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <limits>
#include <
new>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>
#if defined(BACKWARD_SYSTEM_LINUX)
// 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
#if defined(__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_DW == 1
#include <dwarf.h>
#include <elfutils/libdw.h>
#include <elfutils/libdwfl.h>
#endif
#if BACKWARD_HAS_DWARF == 1
#include <algorithm>
#include <dwarf.h>
#include <libdwarf.h>
#include <libelf.h>
#include <map>
#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)
#if defined(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
//
#if defined(BACKWARD_HAS_BACKTRACE_SYMBOL) && BACKWARD_HAS_BACKTRACE_SYMBOL == 1
#else
#undef BACKWARD_HAS_BACKTRACE_SYMBOL
#define BACKWARD_HAS_BACKTRACE_SYMBOL 1
#endif
#include <cxxabi.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
#if defined(BACKWARD_HAS_BACKTRACE) && (BACKWARD_HAS_BACKTRACE == 1) \
|| (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)
#include <execinfo.h>
#endif
#endif // defined(BACKWARD_SYSTEM_DARWIN)
#if defined(BACKWARD_SYSTEM_WINDOWS)
#include <condition_variable>
#include <mutex>
#include <thread>
#include <basetsd.h>
#ifdef _WIN64
typedef SSIZE_T ssize_t;
#else
typedef int ssize_t;
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <winnt.h>
#include <psapi.h>
#include <signal.h>
#ifndef __clang__
#undef NOINLINE
#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
// so we need to do it.
#pragma pack(push, before_imagehlp, 8)
#include <imagehlp.h>
#pragma pack(pop, before_imagehlp)
// 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
#endif // BACKWARD_HAS_UNWIND == 1
#if defined(BACKWARD_HAS_LIBUNWIND) && 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
namespace backward {
namespace details {
template <
typename K,
typename V>
struct hashtable {
typedef std::unordered_map<K, V> type;
};
using std::move;
}
// namespace details
}
// namespace backward
#else // NOT BACKWARD_ATLEAST_CXX11
#define nullptr NULL
#define override
#include <map>
namespace backward {
namespace details {
template <
typename K,
typename V>
struct hashtable {
typedef std::map<K, V> type;
};
template <
typename T>
const T& move(
const T& v) {
return v;
}
template <
typename T>
T& move(T& v) {
return v;
}
}
// namespace details
}
// namespace backward
#endif // BACKWARD_ATLEAST_CXX11
namespace backward {
namespace details {
#if defined(BACKWARD_SYSTEM_WINDOWS)
const char kBackwardPathDelimiter[] =
";";
#else
const char kBackwardPathDelimiter[] =
":";
#endif
}
// namespace details
}
// namespace backward
namespace backward {
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;
#if defined(BACKWARD_SYSTEM_LINUX)
typedef linux_tag current_tag;
#elif defined(BACKWARD_SYSTEM_DARWIN)
typedef darwin_tag current_tag;
#elif defined(BACKWARD_SYSTEM_WINDOWS)
typedef windows_tag current_tag;
#elif defined(BACKWARD_SYSTEM_UNKNOWN)
typedef unknown_tag current_tag;
#else
#error "May I please get my system defines?"
#endif
}
// namespace system_tag
namespace trace_resolver_tag {
#if defined(BACKWARD_SYSTEM_LINUX)
struct libdw;
struct libbfd;
struct libdwarf;
struct backtrace_symbol;
#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
#elif defined(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
#elif defined(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;
};
template <
typename T>
struct rm_ptr<
const T*> {
typedef const T type;
};
template <
typename R,
typename T, R (*F)(T)>
struct deleter {
template <
typename U>
void operator()(U& ptr)
const {
(*F)(ptr);
}
};
template <
typename T>
struct default_delete {
void operator()(T& ptr)
const {
delete ptr;
}
};
template <
typename T,
typename Deleter = deleter<
void,
void*, &::free>>
class handle {
struct dummy;
T _val;
bool _empty;
#ifdef BACKWARD_ATLEAST_CXX11
handle(
const handle&) =
delete;
handle&
operator=(
const handle&) =
delete;
#endif
public:
~handle() {
if (!_empty) {
Deleter()(_val);
}
}
explicit handle() : _val(), _empty(
true) {}
explicit handle(T val) : _val(val), _empty(
false) {
if (!_val)
_empty =
true;
}
#ifdef BACKWARD_ATLEAST_CXX11
handle(handle&& from) : _empty(
true) {
swap(from);
}
handle&
operator=(handle&& from) {
swap(from);
return *
this;
}
#else
explicit handle(
const handle& from) : _empty(
true) {
// some sort of poor man's move semantic.
swap(
const_cast<handle&>(from));
}
handle&
operator=(
const handle& from) {
// some sort of poor man's move semantic.
swap(
const_cast<handle&>(from));
return *
this;
}
#endif
void reset(T new_val) {
handle tmp(new_val);
swap(tmp);
}
void update(T new_val) {
_val = new_val;
_empty = !
static_cast<
bool>(new_val);
}
operator const dummy*()
const {
if (_empty) {
return nullptr;
}
return reinterpret_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!
}
T& operator->() {
return _val;
}
const T& operator->()
const {
return _val;
}
typedef typename rm_ptr<T>::type& ref_t;
typedef const typename rm_ptr<T>::type& const_ref_t;
ref_t
operator*() {
return *_val;
}
const_ref_t
operator*()
const {
return *_val;
}
ref_t
operator[](size_t idx) {
return _val[idx];
}
// Watch out, we've got a badass over here
T*
operator&() {
_empty =
false;
return &_val;
}
};
// Default demangler implementation (do nothing).
template <
typename TAG>
struct demangler_impl {
static std::string demangle(
const char* funcname) {
return funcname;
}
};
#if defined(BACKWARD_SYSTEM_LINUX) ||
defined(BACKWARD_SYSTEM_DARWIN)
template <>
struct demangler_impl<system_tag::current_tag> {
demangler_impl() : _demangle_buffer_length(0) {}
std::string demangle(
const char* funcname) {
using namespace details;
char* result = abi::__cxa_demangle(funcname,
_demangle_buffer.get(),
&_demangle_buffer_length,
nullptr);
if (result) {
_demangle_buffer.update(result);
return result;
}
return funcname;
}
private:
details::handle<
char*> _demangle_buffer;
size_t _demangle_buffer_length;
};
#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN
struct demangler :
public demangler_impl<system_tag::current_tag> {};
// Split a string on the platform's PATH delimiter. Example: if delimiter
// is ":" then:
// "" --> []
// ":" --> ["",""]
// "::" --> ["","",""]
// "/a/b/c" --> ["/a/b/c"]
// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"]
// etc.
inline std::vector<std::string>
split_source_prefixes(
const std::string& s) {
std::vector<std::string> out;
size_t last = 0;
size_t next = 0;
size_t delimiter_size =
sizeof(kBackwardPathDelimiter) - 1;
while ((next = s.find(kBackwardPathDelimiter, last))
!= std::string::npos) {
out.push_back(s.substr(last, next - last));
last = next + delimiter_size;
}
if (last <= s.length()) {
out.push_back(s.substr(last));
}
return out;
}
}
// namespace details
/*************** A TRACE ***************/
struct Trace {
void* addr;
size_t idx;
Trace() : addr(nullptr), idx(0) {}
explicit Trace(
void* _addr, size_t _idx) : addr(_addr), idx(_idx) {}
};
struct ResolvedTrace :
public Trace {
struct SourceLoc {
std::string function;
std::string filename;
unsigned line;
unsigned col;
SourceLoc() : line(0), col(0) {}
bool operator==(
const SourceLoc& b)
const {
return function == b.function && filename == b.filename
&& line == b.line && col == b.col;
}
bool operator!=(
const SourceLoc& b)
const {
return !(*
this == b);
}
};
// 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;
ResolvedTrace() : Trace() {}
ResolvedTrace(
const Trace& mini_trace) : Trace(mini_trace) {}
};
/*************** STACK TRACE ***************/
// default implemention.
template <
typename TAG>
class StackTraceImpl {
public:
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,
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), _context(nullptr), _error_addr(nullptr) {}
size_t thread_id()
const {
return _thread_id;
}
void skip_n_firsts(size_t n) {
_skip = n;
}
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;
}
#elif defined(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
}
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;
}
Trace
operator[](size_t idx)
const {
if (idx >= size()) {
return Trace();
}
return Trace(_stacktrace[idx + skip_n_firsts()], idx);
}
void*
const* begin()
const {
if (size()) {
return &_stacktrace[skip_n_firsts()];
}
return nullptr;
}
protected:
std::vector<
void*> _stacktrace;
};
#if BACKWARD_HAS_UNWIND == 1
namespace details {
template <
typename F>
class Unwinder {
public:
size_t
operator()(F& f, size_t depth) {
_f = &f;
_index = -1;
_depth = depth;
_Unwind_Backtrace(&this->backtrace_trampoline,
this);
if (_index == -1) {
// _Unwind_Backtrace has failed to obtain any backtraces
return 0;
}
else {
return static_cast<size_t>(_index);
}
}
private:
F* _f;
ssize_t _index;
size_t _depth;
static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context* ctx,
void* self) {
return (
static_cast<Unwinder*>(self))->backtrace(ctx);
}
_Unwind_Reason_Code backtrace(_Unwind_Context* ctx) {
if (_index >= 0 &&
static_cast<size_t>(_index) >= _depth)
return _URC_END_OF_STACK;
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;
}
};
template <
typename F>
size_t unwind(F f, size_t depth) {
Unwinder<F> unwinder;
return unwinder(f, depth);
}
}
// namespace details
template <>
class StackTraceImpl<system_tag::current_tag> :
public StackTraceImplHolder {
public:
NOINLINE
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;
}
_stacktrace.resize(depth);
size_t trace_cnt = details::unwind(callback(*
this), depth);
_stacktrace.resize(trace_cnt);
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);
break;
}
}
_stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
return size();
}
private:
struct callback {
StackTraceImpl& self;
callback(StackTraceImpl& _self) : self(_self) {}
void operator()(size_t idx,
void* addr) {
self._stacktrace[idx] = addr;
}
};
};
#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,
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);
size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size());
_stacktrace.resize(trace_cnt);
skip_n_firsts(1);
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] + 1);
break;
}
}
_stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
return size();
}
};
#elif defined(BACKWARD_SYSTEM_WINDOWS)
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_);
}
if (!thd_) {
thd_ = GetCurrentThread();
}
HANDLE process = GetCurrentProcess();
STACKFRAME64 s;
memset(&s, 0,
sizeof(STACKFRAME64));
// TODO: 32 bit context capture
s.AddrStack.Mode = AddrModeFlat;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrPC.Mode = AddrModeFlat;
#ifdef _M_X64
s.AddrPC.Offset = ctx_->Rip;
s.AddrStack.Offset = ctx_->Rsp;
s.AddrFrame.Offset = ctx_->Rbp;
#else
s.AddrPC.Offset = ctx_->Eip;
s.AddrStack.Offset = ctx_->Esp;
s.AddrFrame.Offset = ctx_->Ebp;
#endif
if (!machine_type_) {
#ifdef _M_X64
machine_type_ = IMAGE_FILE_MACHINE_AMD64;
#else
machine_type_ = IMAGE_FILE_MACHINE_I386;
#endif
}
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;
if (s.AddrReturn.Offset == 0)
break;
_stacktrace.push_back(
reinterpret_cast<
void*>(s.AddrPC.Offset));
if (size() >= depth)
break;
}
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);
break;
}
}
_stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
return size();
}
private:
DWORD machine_type_ = 0;
HANDLE thd_ = 0;
CONTEXT* ctx_ = nullptr;
};
#endif
class StackTrace :
public StackTraceImpl<system_tag::current_tag> {};
/*************** TRACE RESOLVER ***************/
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(),
static_cast<
int>(st.size()));
}
virtual ResolvedTrace resolve(ResolvedTrace t) {
return t;
}
protected:
std::string demangle(
const char* funcname) {
return _demangler.demangle(funcname);
}
private:
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 {
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;
}
}
private:
std::string argv0_;
std::string exec_path_;
static std::string get_argv0() {
std::string argv0;
std::ifstream ifs(
"/proc/self/cmdline");
std::getline(ifs, argv0,
'\0');
return argv0;
}
static std::string read_symlink(std::string
const& symlink_path) {
std::string path;
path.resize(100);
while (
true) {
ssize_t len
= ::readlink(symlink_path.c_str(), &*path.begin(), path.size());
if (len < 0) {
return "";
}
if (
static_cast<size_t>(len) == path.size()) {
path.resize(path.size() * 2);
}
else {
path.resize(
static_cast<std::string::size_type>(len));
break;
}
}
return path;
}
};
template <
typename STACKTRACE_TAG>
class TraceResolverLinuxImpl;
#if BACKWARD_HAS_BACKTRACE_SYMBOL == 1
template <>
class TraceResolverLinuxImpl<trace_resolver_tag::backtrace_symbol>
:
public TraceResolverLinuxBase {
public:
void load_addresses(
void*
const* addresses,
int address_count) override {
if (address_count == 0) {
return;
}
_symbols.reset(backtrace_symbols(addresses, address_count));
}
ResolvedTrace resolve(ResolvedTrace trace) override {
char* filename = _symbols[trace.idx];
char* funcname = filename;
while (*funcname && *funcname !=
'(') {
funcname += 1;
}
trace.object_filename.assign(
filename,
funcname);
// ok even if funcname is the ending
// \0 (then we assign entire string)
if (*funcname) {
// if it's not end of string (e.g. from last frame
// ip==0)
funcname += 1;
char* funcname_end = funcname;
while (*funcname_end && *funcname_end !=
')' && *funcname_end !=
'+') {
funcname_end += 1;
}
*funcname_end =
'\0';
trace.object_function = this->demangle(funcname);
trace.source.function = trace.object_function;
// we cannot do better.
}
return trace;
}
private:
details::handle<
char**> _symbols;
};
#endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1
#if BACKWARD_HAS_BFD == 1
template <>
class TraceResolverLinuxImpl<trace_resolver_tag::libbfd>
:
public TraceResolverLinuxBase {
public:
TraceResolverLinuxImpl() : _bfd_loaded(
false) {}
ResolvedTrace resolve(ResolvedTrace trace) override {
Dl_info symbol_info;
// 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
}
return trace;
}
private:
bool _bfd_loaded;
typedef details::handle<bfd*,
details::deleter<bfd_boolean, bfd*, &bfd_close>>
bfd_handle_t;
typedef details::handle<asymbol**> bfd_symtab_t;
struct bfd_fileobject {
bfd_handle_t handle;
bfd_vma base_addr;
bfd_symtab_t symtab;
bfd_symtab_t dynamic_symtab;
};
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) {
using namespace details;
if (!_bfd_loaded) {
using namespace details;
bfd_init();
_bfd_loaded =
true;
}
fobj_bfd_map_t::iterator it = _fobj_bfd_map.find(filename_object);
if (it != _fobj_bfd_map.end()) {
return &it->second;
}
// this new object is empty for now.
bfd_fileobject* r = &_fobj_bfd_map[filename_object];
// we do the work temporary in this one;
bfd_handle_t bfd_handle;
int fd = open(filename_object.c_str(), O_RDONLY);
bfd_handle.reset(bfd_fdopenr(filename_object.c_str(),
"default", fd));
if (!bfd_handle) {
close(fd);
return r;
}
if (!bfd_check_format(bfd_handle.get(), bfd_object)) {
return r;
// not an object? You lose.
}
if ((bfd_get_file_flags(bfd_handle.get()) & HAS_SYMS) == 0) {
return r;
// that's what happen when you forget to compile in debug.
}
ssize_t symtab_storage_size
= bfd_get_symtab_upper_bound(bfd_handle.get());
ssize_t dyn_symtab_storage_size
= bfd_get_dynamic_symtab_upper_bound(bfd_handle.get());
if (symtab_storage_size <= 0 && dyn_symtab_storage_size <= 0) {
return r;
// weird, is the file is corrupted?
}
bfd_symtab_t symtab, dynamic_symtab;
ssize_t symcount = 0, dyn_symcount = 0;
if (symtab_storage_size > 0) {
symtab.reset(
static_cast<bfd_symbol**>(
malloc(
static_cast<size_t>(symtab_storage_size))));
symcount = bfd_canonicalize_symtab(bfd_handle.get(), symtab.get());
}
if (dyn_symtab_storage_size > 0) {
dynamic_symtab.reset(
static_cast<bfd_symbol**>(
malloc(
static_cast<size_t>(dyn_symtab_storage_size))));
dyn_symcount = bfd_canonicalize_dynamic_symtab(bfd_handle.get(),
dynamic_symtab.get());
}
if (symcount <= 0 && dyn_symcount <= 0) {
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);
return r;
}
struct find_sym_result {
bool found;
const char* filename;
const char* funcname;
unsigned int line;
};
struct find_sym_context {
TraceResolverLinuxImpl* self;
bfd_fileobject* fobj;
void* addr;
void* base_addr;
find_sym_result result;
};
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.addr = addr;
context.base_addr = base_addr;
context.result.found =
false;
bfd_map_over_sections(fobj->handle.get(),
&find_in_section_trampoline,
static_cast<
void*>(&context));
return context.result;
}
static void find_in_section_trampoline(bfd*,
asection* section,
void* data) {
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,
context->result);
}
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)
#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);
#else
bfd_vma sec_addr = bfd_section_vma(section);
#endif
#ifdef bfd_get_section_size
bfd_size_type size = bfd_get_section_size(section);
#else
bfd_size_type size = bfd_section_size(section);
#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;
}
}
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored
"-Wzero-as-null-pointer-constant"
#endif
if (!result.found && fobj->symtab) {
result.found = bfd_find_nearest_line(fobj->handle.get(),
section,
fobj->symtab.get(),
addr - sec_addr,
&result.filename,
&result.funcname,
&result.line);
}
if (!result.found && fobj->dynamic_symtab) {
result.found = bfd_find_nearest_line(fobj->handle.get(),
section,
fobj->dynamic_symtab.get(),
addr - sec_addr,
&result.filename,
&result.funcname,
&result.line);
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif
}
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(
const char* a,
const char* b) {
if (!a || !b) {
return false;
}
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) {}
ResolvedTrace resolve(ResolvedTrace trace) override {
using namespace details;
Dwarf_Addr trace_addr =
reinterpret_cast<Dwarf_Addr>(trace.addr);
if (!_dwfl_handle_initialized) {
// initialize dwfl...
_dwfl_cb.reset(
new Dwfl_Callbacks);
_dwfl_cb->find_elf = &dwfl_linux_proc_find_elf;
_dwfl_cb->find_debuginfo = &dwfl_standard_find_debuginfo;
_dwfl_cb->debuginfo_path = 0;
_dwfl_handle.reset(dwfl_begin(_dwfl_cb.get()));
_dwfl_handle_initialized =
true;
if (!_dwfl_handle) {
return trace;
}
// ...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.
const char* 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.
const char* 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 cfi_bias;
Dwarf_CFI* cfi_cache = dwfl_module_eh_cfi(mod, &cfi_bias);
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.
handle<Dwarf_Frame*> frame;
dwarf_cfi_addrframe(cfi_cache, trace_addr - cfi_bias, &frame);
if (frame) {
break;
}
}
}
}
#endif
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) {
const char* 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);
}
deep_first_search_by_pc(
cudie, trace_addr - mod_bias, inliners_search_cb(trace));
if (trace.source.function.size() == 0) {
// fallback.
trace.source.function = trace.object_function;
}
return trace;
}
private:
typedef details::handle<Dwfl*, details::deleter<
void, Dwfl*, &dwfl_end>>
dwfl_handle_t;
details::handle<Dwfl_Callbacks*, details::default_delete<Dwfl_Callbacks*>>
_dwfl_cb;
dwfl_handle_t _dwfl_handle;
bool _dwfl_handle_initialized;
// defined here because in C++98, template function cannot take locally
// defined types... grrr.
struct inliners_search_cb {
void operator()(Dwarf_Die* die) {
switch (dwarf_tag(die)) {
--> --------------------
--> maximum size reached
--> --------------------