[2573] | 1 | #ifdef CPPTRACE_FULL_TRACE_WITH_LIBBACKTRACE |
---|
| 2 | |
---|
| 3 | #include "cpptrace.hpp" |
---|
| 4 | #include "program_name.hpp" |
---|
| 5 | #include "common.hpp" |
---|
| 6 | |
---|
| 7 | #include <cstddef> |
---|
| 8 | #include <cstdint> |
---|
| 9 | #include <cstdio> |
---|
| 10 | #include <mutex> |
---|
| 11 | #include <vector> |
---|
| 12 | |
---|
| 13 | #ifdef CPPTRACE_BACKTRACE_PATH |
---|
| 14 | #include CPPTRACE_BACKTRACE_PATH |
---|
| 15 | #else |
---|
| 16 | #include <backtrace.h> |
---|
| 17 | #endif |
---|
| 18 | |
---|
| 19 | namespace cpptrace { |
---|
| 20 | namespace detail { |
---|
| 21 | struct trace_data { |
---|
| 22 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) |
---|
| 23 | std::vector<stacktrace_frame>& frames; |
---|
| 24 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members) |
---|
| 25 | size_t& skip; |
---|
| 26 | }; |
---|
| 27 | |
---|
| 28 | int full_callback(void* data_pointer, uintptr_t address, const char* file, int line, const char* symbol) { |
---|
| 29 | trace_data& data = *reinterpret_cast<trace_data*>(data_pointer); |
---|
| 30 | if(data.skip > 0) { |
---|
| 31 | data.skip--; |
---|
| 32 | } else if(address == uintptr_t(-1)) { |
---|
| 33 | // sentinel for libbacktrace, stop tracing |
---|
| 34 | return 1; |
---|
| 35 | } else { |
---|
| 36 | data.frames.push_back({ |
---|
| 37 | address, |
---|
| 38 | static_cast<std::uint_least32_t>(line), |
---|
| 39 | 0, |
---|
| 40 | file ? file : "", |
---|
| 41 | symbol ? symbol : "" |
---|
| 42 | }); |
---|
| 43 | } |
---|
| 44 | return 0; |
---|
| 45 | } |
---|
| 46 | |
---|
| 47 | void syminfo_callback(void* data, uintptr_t, const char* symbol, uintptr_t, uintptr_t) { |
---|
| 48 | stacktrace_frame& frame = *static_cast<stacktrace_frame*>(data); |
---|
| 49 | frame.symbol = symbol ? symbol : ""; |
---|
| 50 | } |
---|
| 51 | |
---|
| 52 | void error_callback(void*, const char* msg, int errnum) { |
---|
| 53 | fprintf(stderr, "Libbacktrace error: %s, code %d\n", msg, errnum); |
---|
| 54 | } |
---|
| 55 | |
---|
| 56 | backtrace_state* get_backtrace_state() { |
---|
| 57 | static std::mutex mutex; |
---|
| 58 | const std::lock_guard<std::mutex> lock(mutex); |
---|
| 59 | // backtrace_create_state must be called only one time per program |
---|
| 60 | // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |
---|
| 61 | static backtrace_state* state = nullptr; |
---|
| 62 | static bool called = false; |
---|
| 63 | if(!called) { |
---|
| 64 | state = backtrace_create_state(nullptr, true, error_callback, nullptr); |
---|
| 65 | called = true; |
---|
| 66 | } |
---|
| 67 | return state; |
---|
| 68 | } |
---|
| 69 | |
---|
| 70 | CPPTRACE_FORCE_NO_INLINE |
---|
| 71 | std::vector<stacktrace_frame> generate_trace(size_t skip) { |
---|
| 72 | std::vector<stacktrace_frame> frames; |
---|
| 73 | skip++; // add one for this call |
---|
| 74 | trace_data data { frames, skip }; |
---|
| 75 | backtrace_full(get_backtrace_state(), 0, full_callback, error_callback, &data); |
---|
| 76 | for(auto& frame : frames) { |
---|
| 77 | if(frame.symbol.empty()) { |
---|
| 78 | // fallback, try to at least recover the symbol name with backtrace_syminfo |
---|
| 79 | backtrace_syminfo( |
---|
| 80 | get_backtrace_state(), |
---|
| 81 | frame.address, |
---|
| 82 | syminfo_callback, |
---|
| 83 | error_callback, |
---|
| 84 | &frame |
---|
| 85 | ); |
---|
| 86 | } |
---|
| 87 | } |
---|
| 88 | return frames; |
---|
| 89 | } |
---|
| 90 | } |
---|
| 91 | } |
---|
| 92 | |
---|
| 93 | #endif |
---|