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 |
---|