Line data Source code
1 : #include "Backtrace.h" 2 : 3 : #include <cxxabi.h> 4 : #include <dlfcn.h> 5 : #include <execinfo.h> 6 : #include <sstream> 7 : 8 : namespace elsa::detail 9 : { 10 : 11 : std::string symbolDemangle(const char* symbol) 12 0 : { 13 0 : int status; 14 0 : char* buf = abi::__cxa_demangle(symbol, nullptr, nullptr, &status); 15 : 16 0 : if (status != 0) { 17 0 : return symbol; 18 0 : } else { 19 0 : std::string result{buf}; 20 0 : ::free(buf); 21 0 : return result; 22 0 : } 23 0 : } 24 : 25 : std::string addrToString(const void* addr) 26 0 : { 27 0 : std::ostringstream out; 28 0 : out << "[" << addr << "]"; 29 0 : return out.str(); 30 0 : } 31 : 32 : std::string symbolName(const void* addr, bool require_exact_addr, bool no_pure_addrs) 33 0 : { 34 0 : Dl_info addr_info; 35 : 36 0 : if (::dladdr(addr, &addr_info) == 0) { 37 : // dladdr has... failed. 38 0 : return no_pure_addrs ? "" : addrToString(addr); 39 0 : } else { 40 0 : size_t symbol_offset = 41 0 : reinterpret_cast<size_t>(addr) - reinterpret_cast<size_t>(addr_info.dli_saddr); 42 : 43 0 : if (addr_info.dli_sname == nullptr or (symbol_offset != 0 and require_exact_addr)) { 44 : 45 0 : return no_pure_addrs ? "" : addrToString(addr); 46 0 : } 47 : 48 0 : if (symbol_offset == 0) { 49 : // this is our symbol name. 50 0 : return symbolDemangle(addr_info.dli_sname); 51 0 : } else { 52 0 : std::ostringstream out; 53 0 : out << symbolDemangle(addr_info.dli_sname) << "+0x" << std::hex << symbol_offset 54 0 : << std::dec; 55 0 : return out.str(); 56 0 : } 57 0 : } 58 0 : } 59 : 60 : } // namespace elsa::detail 61 : 62 : namespace elsa 63 : { 64 : 65 : void Backtrace::analyze() 66 383 : { 67 383 : std::vector<void*> buffer{32}; 68 : 69 : // increase buffer size until it's enough 70 383 : while (true) { 71 383 : int buff_size = static_cast<int>(buffer.size()); 72 383 : auto elements = static_cast<size_t>(::backtrace(buffer.data(), buff_size)); 73 383 : if (elements < buffer.size()) { 74 383 : buffer.resize(elements); 75 383 : break; 76 383 : } 77 0 : buffer.resize(buffer.size() * 2); 78 0 : } 79 : 80 3775 : for (void* element : buffer) { 81 3775 : this->stack_addrs.push_back(element); 82 3775 : } 83 383 : } 84 : 85 : void Backtrace::getSymbols(const std::function<void(const backtrace_symbol*)>& cb, 86 : bool reversed) const 87 0 : { 88 0 : backtrace_symbol symbol; 89 : 90 0 : if (reversed) { 91 0 : for (size_t idx = this->stack_addrs.size(); idx-- > 0;) { 92 0 : void* pc = this->stack_addrs[idx]; 93 : 94 0 : symbol.functionname = detail::symbolName(pc, false, true); 95 0 : symbol.pc = pc; 96 : 97 0 : cb(&symbol); 98 0 : } 99 0 : } else { 100 0 : for (void* pc : this->stack_addrs) { 101 0 : symbol.functionname = detail::symbolName(pc, false, true); 102 0 : symbol.pc = pc; 103 : 104 0 : cb(&symbol); 105 0 : } 106 0 : } 107 0 : } 108 : 109 : void Backtrace::trimToCurrentStackFrame() 110 2 : { 111 2 : Backtrace current; 112 2 : current.analyze(); 113 : 114 14 : while (not current.stack_addrs.empty() and not this->stack_addrs.empty()) { 115 14 : if (this->stack_addrs.back() != current.stack_addrs.back()) { 116 2 : break; 117 2 : } 118 : 119 12 : this->stack_addrs.pop_back(); 120 12 : current.stack_addrs.pop_back(); 121 12 : } 122 2 : } 123 : 124 : } // namespace elsa