/src/duckdb/src/common/stacktrace.cpp
Line | Count | Source |
1 | | #include "duckdb/common/stacktrace.hpp" |
2 | | #include "duckdb/common/string_util.hpp" |
3 | | #include "duckdb/common/to_string.hpp" |
4 | | |
5 | | #if defined(__GLIBC__) || defined(__APPLE__) |
6 | | #include <execinfo.h> |
7 | | #include <cxxabi.h> |
8 | | #endif |
9 | | |
10 | | namespace duckdb { |
11 | | |
12 | | #if defined(__GLIBC__) || defined(__APPLE__) |
13 | 0 | static string UnmangleSymbol(string symbol) { |
14 | | // find the mangled name |
15 | 0 | idx_t mangle_start = symbol.size(); |
16 | 0 | idx_t mangle_end = 0; |
17 | 0 | for (idx_t i = 0; i < symbol.size(); ++i) { |
18 | 0 | if (symbol[i] == '_') { |
19 | 0 | mangle_start = i; |
20 | 0 | break; |
21 | 0 | } |
22 | 0 | } |
23 | 0 | for (idx_t i = mangle_start; i < symbol.size(); i++) { |
24 | 0 | if (StringUtil::CharacterIsSpace(symbol[i]) || symbol[i] == ')' || symbol[i] == '+') { |
25 | 0 | mangle_end = i; |
26 | 0 | break; |
27 | 0 | } |
28 | 0 | } |
29 | 0 | if (mangle_start >= mangle_end) { |
30 | 0 | return symbol; |
31 | 0 | } |
32 | 0 | string mangled_symbol = symbol.substr(mangle_start, mangle_end - mangle_start); |
33 | |
|
34 | 0 | int status; |
35 | 0 | auto demangle_result = abi::__cxa_demangle(mangled_symbol.c_str(), nullptr, nullptr, &status); |
36 | 0 | if (status != 0 || !demangle_result) { |
37 | 0 | return symbol; |
38 | 0 | } |
39 | 0 | string result; |
40 | 0 | result += symbol.substr(0, mangle_start); |
41 | 0 | result += demangle_result; |
42 | 0 | result += symbol.substr(mangle_end); |
43 | 0 | free(demangle_result); |
44 | 0 | return result; |
45 | 0 | } |
46 | | |
47 | 0 | static string CleanupStackTrace(string symbol) { |
48 | | #ifdef __APPLE__ |
49 | | // structure of frame pointers is [depth] [library] [pointer] [symbol] |
50 | | // we are only interested in [depth] and [symbol] |
51 | | |
52 | | // find the depth |
53 | | idx_t start; |
54 | | for (start = 0; start < symbol.size(); start++) { |
55 | | if (!StringUtil::CharacterIsDigit(symbol[start])) { |
56 | | break; |
57 | | } |
58 | | } |
59 | | |
60 | | // now scan forward until we find the frame pointer |
61 | | idx_t frame_end = symbol.size(); |
62 | | for (idx_t i = start; i + 1 < symbol.size(); ++i) { |
63 | | if (symbol[i] == '0' && symbol[i + 1] == 'x') { |
64 | | idx_t k; |
65 | | for (k = i + 2; k < symbol.size(); ++k) { |
66 | | if (!StringUtil::CharacterIsHex(symbol[k])) { |
67 | | break; |
68 | | } |
69 | | } |
70 | | frame_end = k; |
71 | | break; |
72 | | } |
73 | | } |
74 | | static constexpr idx_t STACK_TRACE_INDENTATION = 8; |
75 | | if (frame_end == symbol.size() || start >= STACK_TRACE_INDENTATION) { |
76 | | // frame pointer not found - just preserve the original frame |
77 | | return symbol; |
78 | | } |
79 | | idx_t space_count = STACK_TRACE_INDENTATION - start; |
80 | | return symbol.substr(0, start) + string(space_count, ' ') + symbol.substr(frame_end, symbol.size() - frame_end); |
81 | | #else |
82 | 0 | return symbol; |
83 | 0 | #endif |
84 | 0 | } |
85 | | |
86 | 0 | string StackTrace::GetStacktracePointers(idx_t max_depth) { |
87 | 0 | string result; |
88 | 0 | auto callstack = unique_ptr<void *[]>(new void *[max_depth]); |
89 | 0 | int frames = backtrace(callstack.get(), NumericCast<int32_t>(max_depth)); |
90 | | // skip two frames (these are always StackTrace::...) |
91 | 0 | for (idx_t i = 2; i < NumericCast<idx_t>(frames); i++) { |
92 | 0 | if (!result.empty()) { |
93 | 0 | result += ";"; |
94 | 0 | } |
95 | 0 | result += to_string(CastPointerToValue(callstack[i])); |
96 | 0 | } |
97 | 0 | return result; |
98 | 0 | } |
99 | | |
100 | 0 | string StackTrace::ResolveStacktraceSymbols(const string &pointers) { |
101 | 0 | auto splits = StringUtil::Split(pointers, ";"); |
102 | 0 | idx_t frame_count = splits.size(); |
103 | 0 | auto callstack = unique_ptr<void *[]>(new void *[frame_count]); |
104 | 0 | for (idx_t i = 0; i < frame_count; i++) { |
105 | 0 | callstack[i] = cast_uint64_to_pointer(StringUtil::ToUnsigned(splits[i])); |
106 | 0 | } |
107 | 0 | string result; |
108 | 0 | char **strs = backtrace_symbols(callstack.get(), NumericCast<int>(frame_count)); |
109 | 0 | for (idx_t i = 0; i < frame_count; i++) { |
110 | 0 | result += CleanupStackTrace(UnmangleSymbol(strs[i])); |
111 | 0 | result += "\n"; |
112 | 0 | } |
113 | 0 | free(reinterpret_cast<void *>(strs)); |
114 | 0 | return "\n" + result; |
115 | 0 | } |
116 | | |
117 | | #else |
118 | | string StackTrace::GetStacktracePointers(idx_t max_depth) { |
119 | | return string(); |
120 | | } |
121 | | |
122 | | string StackTrace::ResolveStacktraceSymbols(const string &pointers) { |
123 | | return string(); |
124 | | } |
125 | | #endif |
126 | | |
127 | | } // namespace duckdb |