Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // This file is copied from |
18 | | // https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/StackTrace.cpp |
19 | | // and modified by Doris |
20 | | |
21 | | #include "common/stack_trace.h" |
22 | | |
23 | | #include <fmt/format.h> |
24 | | |
25 | | #include <atomic> |
26 | | #include <filesystem> |
27 | | #include <limits> |
28 | | #include <map> |
29 | | #include <mutex> |
30 | | #include <sstream> |
31 | | #include <string_view> |
32 | | #include <unordered_map> |
33 | | |
34 | | #include "common/config.h" |
35 | | #include "common/demangle.h" |
36 | | #include "common/dwarf.h" |
37 | | #include "common/elf.h" |
38 | | #include "common/memory_sanitizer.h" |
39 | | #include "common/symbol_index.h" |
40 | | #include "exec/common/hex.h" |
41 | | #include "util/string_util.h" |
42 | | |
43 | | #if defined(USE_UNWIND) && USE_UNWIND && defined(__x86_64__) |
44 | | #include <libunwind.h> |
45 | | #else |
46 | | #include <execinfo.h> |
47 | | #endif |
48 | | |
49 | | namespace { |
50 | | /// Currently this variable is set up once on server startup. |
51 | | /// But we use atomic just in case, so it is possible to be modified at runtime. |
52 | | std::atomic<bool> show_addresses = true; |
53 | | |
54 | | // #if defined(__ELF__) && !defined(__FreeBSD__) |
55 | | // void writePointerHex(const void* ptr, std::stringstream& buf) { |
56 | | // buf.write("0x", 2); |
57 | | // char hex_str[2 * sizeof(ptr)]; |
58 | | // doris::write_hex_uint_lowercase(reinterpret_cast<uintptr_t>(ptr), hex_str); |
59 | | // buf.write(hex_str, 2 * sizeof(ptr)); |
60 | | // } |
61 | | // #endif |
62 | | |
63 | 0 | bool shouldShowAddress(const void* addr) { |
64 | | /// If the address is less than 4096, most likely it is a nullptr dereference with offset, |
65 | | /// and showing this offset is secure nevertheless. |
66 | | /// NOTE: 4096 is the page size on x86 and it can be different on other systems, |
67 | | /// but for the purpose of this branch, it does not matter. |
68 | 0 | if (reinterpret_cast<uintptr_t>(addr) < 4096) { |
69 | 0 | return true; |
70 | 0 | } |
71 | | |
72 | 0 | return show_addresses.load(std::memory_order_relaxed); |
73 | 0 | } |
74 | | } // namespace |
75 | | |
76 | 0 | void StackTrace::setShowAddresses(bool show) { |
77 | 0 | show_addresses.store(show, std::memory_order_relaxed); |
78 | 0 | } |
79 | | |
80 | 0 | std::string SigsegvErrorString(const siginfo_t& info, [[maybe_unused]] const ucontext_t& context) { |
81 | 0 | using namespace std::string_literals; |
82 | 0 | std::string address = |
83 | 0 | info.si_addr == nullptr |
84 | 0 | ? "NULL pointer"s |
85 | 0 | : (shouldShowAddress(info.si_addr) ? fmt::format("{}", info.si_addr) : ""s); |
86 | |
|
87 | 0 | const std::string_view access = |
88 | 0 | #if defined(__x86_64__) && !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(__arm__) && \ |
89 | 0 | !defined(__powerpc__) |
90 | 0 | (context.uc_mcontext.gregs[REG_ERR] & 0x02) ? "write" : "read"; |
91 | | #else |
92 | | ""; |
93 | | #endif |
94 | |
|
95 | 0 | std::string_view message; |
96 | |
|
97 | 0 | switch (info.si_code) { |
98 | 0 | case SEGV_ACCERR: |
99 | 0 | message = "Attempted access has violated the permissions assigned to the memory area"; |
100 | 0 | break; |
101 | 0 | case SEGV_MAPERR: |
102 | 0 | message = "Address not mapped to object"; |
103 | 0 | break; |
104 | 0 | default: |
105 | 0 | message = "Unknown si_code"; |
106 | 0 | break; |
107 | 0 | } |
108 | | |
109 | 0 | return fmt::format("Address: {}. Access: {}. {}.", std::move(address), access, message); |
110 | 0 | } |
111 | | |
112 | 0 | constexpr std::string_view SigbusErrorString(int si_code) { |
113 | 0 | switch (si_code) { |
114 | 0 | case BUS_ADRALN: |
115 | 0 | return "Invalid address alignment."; |
116 | 0 | case BUS_ADRERR: |
117 | 0 | return "Non-existent physical address."; |
118 | 0 | case BUS_OBJERR: |
119 | 0 | return "Object specific hardware error."; |
120 | | |
121 | | // Linux specific |
122 | 0 | #if defined(BUS_MCEERR_AR) |
123 | 0 | case BUS_MCEERR_AR: |
124 | 0 | return "Hardware memory error: action required."; |
125 | 0 | #endif |
126 | 0 | #if defined(BUS_MCEERR_AO) |
127 | 0 | case BUS_MCEERR_AO: |
128 | 0 | return "Hardware memory error: action optional."; |
129 | 0 | #endif |
130 | 0 | default: |
131 | 0 | return "Unknown si_code."; |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | 0 | constexpr std::string_view SigfpeErrorString(int si_code) { |
136 | 0 | switch (si_code) { |
137 | 0 | case FPE_INTDIV: |
138 | 0 | return "Integer divide by zero."; |
139 | 0 | case FPE_INTOVF: |
140 | 0 | return "Integer overflow."; |
141 | 0 | case FPE_FLTDIV: |
142 | 0 | return "Floating point divide by zero."; |
143 | 0 | case FPE_FLTOVF: |
144 | 0 | return "Floating point overflow."; |
145 | 0 | case FPE_FLTUND: |
146 | 0 | return "Floating point underflow."; |
147 | 0 | case FPE_FLTRES: |
148 | 0 | return "Floating point inexact result."; |
149 | 0 | case FPE_FLTINV: |
150 | 0 | return "Floating point invalid operation."; |
151 | 0 | case FPE_FLTSUB: |
152 | 0 | return "Subscript out of range."; |
153 | 0 | default: |
154 | 0 | return "Unknown si_code."; |
155 | 0 | } |
156 | 0 | } |
157 | | |
158 | 0 | constexpr std::string_view SigillErrorString(int si_code) { |
159 | 0 | switch (si_code) { |
160 | 0 | case ILL_ILLOPC: |
161 | 0 | return "Illegal opcode."; |
162 | 0 | case ILL_ILLOPN: |
163 | 0 | return "Illegal operand."; |
164 | 0 | case ILL_ILLADR: |
165 | 0 | return "Illegal addressing mode."; |
166 | 0 | case ILL_ILLTRP: |
167 | 0 | return "Illegal trap."; |
168 | 0 | case ILL_PRVOPC: |
169 | 0 | return "Privileged opcode."; |
170 | 0 | case ILL_PRVREG: |
171 | 0 | return "Privileged register."; |
172 | 0 | case ILL_COPROC: |
173 | 0 | return "Coprocessor error."; |
174 | 0 | case ILL_BADSTK: |
175 | 0 | return "Internal stack error."; |
176 | 0 | default: |
177 | 0 | return "Unknown si_code."; |
178 | 0 | } |
179 | 0 | } |
180 | | |
181 | | std::string signalToErrorMessage(int sig, const siginfo_t& info, |
182 | 0 | [[maybe_unused]] const ucontext_t& context) { |
183 | 0 | switch (sig) { |
184 | 0 | case SIGSEGV: |
185 | 0 | return SigsegvErrorString(info, context); |
186 | 0 | case SIGBUS: |
187 | 0 | return std::string {SigbusErrorString(info.si_code)}; |
188 | 0 | case SIGILL: |
189 | 0 | return std::string {SigillErrorString(info.si_code)}; |
190 | 0 | case SIGFPE: |
191 | 0 | return std::string {SigfpeErrorString(info.si_code)}; |
192 | 0 | case SIGTSTP: |
193 | 0 | return "This is a signal used for debugging purposes by the user."; |
194 | 0 | default: |
195 | 0 | return ""; |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | 0 | static void* getCallerAddress(const ucontext_t& context) { |
200 | 0 | #if defined(__x86_64__) |
201 | | /// Get the address at the time the signal was raised from the RIP (x86-64) |
202 | | #if defined(__FreeBSD__) |
203 | | return reinterpret_cast<void*>(context.uc_mcontext.mc_rip); |
204 | | #elif defined(__APPLE__) |
205 | | return reinterpret_cast<void*>(context.uc_mcontext->__ss.__rip); |
206 | | #else |
207 | 0 | return reinterpret_cast<void*>(context.uc_mcontext.gregs[REG_RIP]); |
208 | 0 | #endif |
209 | | #elif defined(__APPLE__) && defined(__aarch64__) |
210 | | return reinterpret_cast<void*>(context.uc_mcontext->__ss.__pc); |
211 | | #elif defined(__FreeBSD__) && defined(__aarch64__) |
212 | | return reinterpret_cast<void*>(context.uc_mcontext.mc_gpregs.gp_elr); |
213 | | #elif defined(__aarch64__) |
214 | | return reinterpret_cast<void*>(context.uc_mcontext.pc); |
215 | | #elif defined(__powerpc64__) && defined(__linux__) |
216 | | return reinterpret_cast<void*>(context.uc_mcontext.gp_regs[PT_NIP]); |
217 | | #elif defined(__powerpc64__) && defined(__FreeBSD__) |
218 | | return reinterpret_cast<void*>(context.uc_mcontext.mc_srr0); |
219 | | #elif defined(__riscv) |
220 | | return reinterpret_cast<void*>(context.uc_mcontext.__gregs[REG_PC]); |
221 | | #elif defined(__s390x__) |
222 | | return reinterpret_cast<void*>(context.uc_mcontext.psw.addr); |
223 | | #else |
224 | | return nullptr; |
225 | | #endif |
226 | 0 | } |
227 | | |
228 | | // FIXME: looks like this is used only for Sentry but duplicates the whole algo, maybe replace? |
229 | | void StackTrace::symbolize(const StackTrace::FramePointers& frame_pointers, |
230 | | [[maybe_unused]] size_t offset, size_t size, |
231 | 0 | StackTrace::Frames& frames) { |
232 | 0 | #if defined(__ELF__) && !defined(__FreeBSD__) |
233 | 0 | auto symbol_index_ptr = doris::SymbolIndex::instance(); |
234 | 0 | const doris::SymbolIndex& symbol_index = *symbol_index_ptr; |
235 | 0 | std::unordered_map<std::string, doris::Dwarf> dwarfs; |
236 | |
|
237 | 0 | for (size_t i = 0; i < offset; ++i) { |
238 | 0 | frames[i].virtual_addr = frame_pointers[i]; |
239 | 0 | } |
240 | |
|
241 | 0 | for (size_t i = offset; i < size; ++i) { |
242 | 0 | StackTrace::Frame& current_frame = frames[i]; |
243 | 0 | current_frame.virtual_addr = frame_pointers[i]; |
244 | 0 | const auto* object = symbol_index.findObject(current_frame.virtual_addr); |
245 | 0 | uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; |
246 | 0 | current_frame.physical_addr = |
247 | 0 | reinterpret_cast<void*>(uintptr_t(current_frame.virtual_addr) - virtual_offset); |
248 | |
|
249 | 0 | if (object) { |
250 | 0 | current_frame.object = object->name; |
251 | 0 | if (std::error_code ec; |
252 | 0 | std::filesystem::exists(current_frame.object.value(), ec) && !ec) { |
253 | 0 | auto dwarf_it = dwarfs.try_emplace(object->name, object->elf).first; |
254 | |
|
255 | 0 | doris::Dwarf::LocationInfo location; |
256 | 0 | std::vector<doris::Dwarf::SymbolizedFrame> inline_frames; |
257 | 0 | if (dwarf_it->second.findAddress(uintptr_t(current_frame.physical_addr), location, |
258 | 0 | doris::Dwarf::LocationInfoMode::FAST, |
259 | 0 | inline_frames)) { |
260 | 0 | current_frame.file = location.file.toString(); |
261 | 0 | current_frame.line = location.line; |
262 | 0 | } |
263 | 0 | } |
264 | 0 | } else { |
265 | 0 | current_frame.object = "?"; |
266 | 0 | } |
267 | |
|
268 | 0 | if (const auto* symbol = symbol_index.findSymbol(current_frame.virtual_addr)) { |
269 | 0 | current_frame.symbol = demangle(symbol->name); |
270 | 0 | } else { |
271 | 0 | current_frame.symbol = "?"; |
272 | 0 | } |
273 | 0 | } |
274 | | #else |
275 | | for (size_t i = 0; i < size; ++i) frames[i].virtual_addr = frame_pointers[i]; |
276 | | #endif |
277 | 0 | } |
278 | | |
279 | 0 | StackTrace::StackTrace(const ucontext_t& signal_context) { |
280 | 0 | tryCapture(); |
281 | | |
282 | | /// This variable from signal handler is not instrumented by Memory Sanitizer. |
283 | 0 | __msan_unpoison(&signal_context, sizeof(signal_context)); |
284 | |
|
285 | 0 | void* caller_address = getCallerAddress(signal_context); |
286 | |
|
287 | 0 | if (size == 0 && caller_address) { |
288 | 0 | frame_pointers[0] = caller_address; |
289 | 0 | size = 1; |
290 | 0 | } else { |
291 | | /// Skip excessive stack frames that we have created while finding stack trace. |
292 | 0 | for (size_t i = 0; i < size; ++i) { |
293 | 0 | const auto frame_address = reinterpret_cast<uintptr_t>(frame_pointers[i]); |
294 | 0 | const auto caller_address_value = reinterpret_cast<uintptr_t>(caller_address); |
295 | 0 | if (caller_address_value != 0 && |
296 | 0 | (frame_address == caller_address_value || |
297 | 0 | (caller_address_value < std::numeric_limits<uintptr_t>::max() && |
298 | 0 | frame_address == caller_address_value + 1))) { |
299 | 0 | offset = i; |
300 | 0 | break; |
301 | 0 | } |
302 | 0 | } |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | 117M | void StackTrace::tryCapture() { |
307 | | // When unw_backtrace is not available, fall back on the standard |
308 | | // `backtrace` function from execinfo.h. |
309 | 117M | #if defined(USE_UNWIND) && USE_UNWIND && defined(__x86_64__) // TODO |
310 | 117M | size = unw_backtrace(frame_pointers.data(), capacity); |
311 | | #else |
312 | | size = backtrace(frame_pointers.data(), capacity); |
313 | | #endif |
314 | 117M | __msan_unpoison(frame_pointers.data(), size * sizeof(frame_pointers[0])); |
315 | 117M | } |
316 | | |
317 | | /// ClickHouse uses bundled libc++ so type names will be the same on every system thus it's safe to hardcode them |
318 | | constexpr std::pair<std::string_view, std::string_view> replacements[] = { |
319 | | {"::__1", ""}, |
320 | | {"std::basic_string<char, std::char_traits<char>, std::allocator<char>>", "std::string"}}; |
321 | | |
322 | 6.37M | std::string collapseNames(std::string&& haystack) { |
323 | | // TODO: surely there is a written version already for better in place search&replace |
324 | 12.7M | for (auto [needle, to] : replacements) { |
325 | 12.7M | size_t pos = 0; |
326 | 12.7M | while ((pos = haystack.find(needle, pos)) != std::string::npos) { |
327 | 0 | haystack.replace(pos, needle.length(), to); |
328 | 0 | pos += to.length(); |
329 | 0 | } |
330 | 12.7M | } |
331 | | |
332 | 6.37M | return haystack; |
333 | 6.37M | } |
334 | | |
335 | | struct StackTraceRefTriple { |
336 | | const StackTrace::FramePointers& pointers; |
337 | | size_t offset; |
338 | | size_t size; |
339 | | std::string_view dwarf_location_info_mode; |
340 | | }; |
341 | | |
342 | | struct StackTraceTriple { |
343 | | StackTrace::FramePointers pointers; |
344 | | size_t offset; |
345 | | size_t size; |
346 | | std::string dwarf_location_info_mode; |
347 | | }; |
348 | | |
349 | | template <class T> |
350 | | concept MaybeRef = std::is_same_v<T, StackTraceTriple> || std::is_same_v<T, StackTraceRefTriple>; |
351 | | |
352 | 1.98G | constexpr bool operator<(const MaybeRef auto& left, const MaybeRef auto& right) { |
353 | | // The same PCs can be rendered with different DWARF detail levels. Keeping the mode in the |
354 | | // key prevents a cheap disabled rendering from poisoning a later full rendering, and prevents |
355 | | // full file/line detail from leaking into a disabled request. |
356 | 1.98G | return std::tuple {left.pointers, left.size, left.offset, |
357 | 1.98G | std::string_view(left.dwarf_location_info_mode)} < |
358 | 1.98G | std::tuple {right.pointers, right.size, right.offset, |
359 | 1.98G | std::string_view(right.dwarf_location_info_mode)}; |
360 | 1.98G | } _ZltITk8MaybeRef16StackTraceTripleTk8MaybeRef19StackTraceRefTripleEbRKT_RKT0_ Line | Count | Source | 352 | 1.86G | constexpr bool operator<(const MaybeRef auto& left, const MaybeRef auto& right) { | 353 | | // The same PCs can be rendered with different DWARF detail levels. Keeping the mode in the | 354 | | // key prevents a cheap disabled rendering from poisoning a later full rendering, and prevents | 355 | | // full file/line detail from leaking into a disabled request. | 356 | 1.86G | return std::tuple {left.pointers, left.size, left.offset, | 357 | 1.86G | std::string_view(left.dwarf_location_info_mode)} < | 358 | 1.86G | std::tuple {right.pointers, right.size, right.offset, | 359 | 1.86G | std::string_view(right.dwarf_location_info_mode)}; | 360 | 1.86G | } |
_ZltITk8MaybeRef19StackTraceRefTripleTk8MaybeRef16StackTraceTripleEbRKT_RKT0_ Line | Count | Source | 352 | 118M | constexpr bool operator<(const MaybeRef auto& left, const MaybeRef auto& right) { | 353 | | // The same PCs can be rendered with different DWARF detail levels. Keeping the mode in the | 354 | | // key prevents a cheap disabled rendering from poisoning a later full rendering, and prevents | 355 | | // full file/line detail from leaking into a disabled request. | 356 | 118M | return std::tuple {left.pointers, left.size, left.offset, | 357 | 118M | std::string_view(left.dwarf_location_info_mode)} < | 358 | 118M | std::tuple {right.pointers, right.size, right.offset, | 359 | 118M | std::string_view(right.dwarf_location_info_mode)}; | 360 | 118M | } |
_ZltITk8MaybeRef16StackTraceTripleTk8MaybeRefS0_EbRKT_RKT0_ Line | Count | Source | 352 | 4.82M | constexpr bool operator<(const MaybeRef auto& left, const MaybeRef auto& right) { | 353 | | // The same PCs can be rendered with different DWARF detail levels. Keeping the mode in the | 354 | | // key prevents a cheap disabled rendering from poisoning a later full rendering, and prevents | 355 | | // full file/line detail from leaking into a disabled request. | 356 | 4.82M | return std::tuple {left.pointers, left.size, left.offset, | 357 | 4.82M | std::string_view(left.dwarf_location_info_mode)} < | 358 | 4.82M | std::tuple {right.pointers, right.size, right.offset, | 359 | 4.82M | std::string_view(right.dwarf_location_info_mode)}; | 360 | 4.82M | } |
|
361 | | |
362 | | static void toStringEveryLineImpl([[maybe_unused]] const std::string dwarf_location_info_mode, |
363 | | const StackTraceRefTriple& stack_trace, |
364 | 248k | std::function<void(std::string_view)> callback) { |
365 | 248k | if (stack_trace.size == 0) { |
366 | 0 | return callback("<Empty trace>"); |
367 | 0 | } |
368 | 248k | #if defined(__ELF__) && !defined(__FreeBSD__) |
369 | | |
370 | 248k | using enum doris::Dwarf::LocationInfoMode; |
371 | 248k | doris::Dwarf::LocationInfoMode mode; |
372 | 248k | auto dwarf_location_info_mode_lower = doris::to_lower(dwarf_location_info_mode); |
373 | 248k | if (dwarf_location_info_mode_lower == "disabled") { |
374 | 248k | mode = DISABLED; |
375 | 248k | } else if (dwarf_location_info_mode_lower == "fast") { |
376 | 222 | mode = FAST; |
377 | 222 | } else if (dwarf_location_info_mode_lower == "full") { |
378 | 0 | mode = FULL; |
379 | 0 | } else if (dwarf_location_info_mode_lower == "full_with_inline") { |
380 | 0 | mode = FULL_WITH_INLINE; |
381 | 0 | } else { |
382 | 0 | LOG(INFO) << "invalid LocationInfoMode: " << dwarf_location_info_mode; |
383 | 0 | mode = DISABLED; |
384 | 0 | } |
385 | 248k | auto symbol_index_ptr = doris::SymbolIndex::instance(); |
386 | 248k | const doris::SymbolIndex& symbol_index = *symbol_index_ptr; |
387 | 248k | std::unordered_map<std::string, doris::Dwarf> dwarfs; |
388 | 6.86M | for (size_t i = stack_trace.offset; i < stack_trace.size; ++i) { |
389 | 6.61M | std::vector<doris::Dwarf::SymbolizedFrame> inline_frames; |
390 | 6.61M | const void* virtual_addr = stack_trace.pointers[i]; |
391 | 6.61M | const auto* object = symbol_index.findObject(virtual_addr); |
392 | 6.61M | uintptr_t virtual_offset = object ? uintptr_t(object->address_begin) : 0; |
393 | 6.61M | const void* physical_addr = |
394 | 6.61M | reinterpret_cast<const void*>(uintptr_t(virtual_addr) - virtual_offset); |
395 | | |
396 | 6.61M | std::stringstream out; |
397 | 6.61M | out << "\t" << i << "# "; |
398 | 6.61M | if (i < 10) { // for alignment |
399 | 2.48M | out << " "; |
400 | 2.48M | } |
401 | | |
402 | 6.61M | if (const auto* const symbol = symbol_index.findSymbol(virtual_addr)) { |
403 | 6.37M | out << collapseNames(demangle(symbol->name)); |
404 | 6.37M | } else { |
405 | 245k | out << "?"; |
406 | 245k | } |
407 | | |
408 | 6.61M | if (std::error_code ec; object && std::filesystem::exists(object->name, ec) && !ec) { |
409 | 6.13M | auto dwarf_it = dwarfs.try_emplace(object->name, object->elf).first; |
410 | | |
411 | 6.13M | doris::Dwarf::LocationInfo location; |
412 | | |
413 | 6.13M | if (dwarf_it->second.findAddress(uintptr_t(physical_addr), location, mode, |
414 | 6.13M | inline_frames)) { |
415 | 3.27k | out << " at " << location.file.toString() << ":" << location.line; |
416 | 3.27k | } |
417 | 6.13M | } |
418 | | |
419 | | // Do not display the stack address and file name, it is not important. |
420 | | // if (shouldShowAddress(physical_addr)) { |
421 | | // out << " @ "; |
422 | | // writePointerHex(physical_addr, out); |
423 | | // } |
424 | | |
425 | | // out << " in " << (object ? object->name : "?"); |
426 | | |
427 | 6.61M | callback(out.str()); |
428 | | |
429 | 6.61M | for (size_t j = 0; j < inline_frames.size(); ++j) { |
430 | 0 | const auto& frame = inline_frames[j]; |
431 | 0 | callback(fmt::format("\t{}.{}. inlined from {}: {}:{}", i, j + 1, |
432 | 0 | collapseNames(demangle(frame.name)), |
433 | 0 | frame.location.file.toString(), frame.location.line)); |
434 | 0 | } |
435 | 6.61M | } |
436 | | #else |
437 | | for (size_t i = stack_trace.offset; i < stack_trace.size; ++i) |
438 | | if (const void* const addr = stack_trace.pointers[i]; shouldShowAddress(addr)) |
439 | | callback(fmt::format("{}. {}", i, addr)); |
440 | | #endif |
441 | 248k | } |
442 | | |
443 | 0 | void StackTrace::toStringEveryLine(std::function<void(std::string_view)> callback) const { |
444 | 0 | toStringEveryLineImpl("FULL_WITH_INLINE", {frame_pointers, offset, size, "FULL_WITH_INLINE"}, |
445 | 0 | std::move(callback)); |
446 | 0 | } |
447 | | |
448 | | using StackTraceCache = std::map<StackTraceTriple, std::string, std::less<>>; |
449 | | |
450 | 118M | static StackTraceCache& cacheInstance() { |
451 | 118M | static StackTraceCache cache; |
452 | 118M | return cache; |
453 | 118M | } |
454 | | |
455 | | static std::mutex stacktrace_cache_mutex; |
456 | | |
457 | | std::string toStringCached(const StackTrace::FramePointers& pointers, size_t offset, size_t size, |
458 | 117M | const std::string& dwarf_location_info_mode) { |
459 | | /// Calculation of stack trace text is extremely slow. |
460 | | /// We use simple cache because otherwise the server could be overloaded by trash queries. |
461 | | /// Note that this cache can grow unconditionally, but practically it should be small. |
462 | 117M | std::lock_guard lock {stacktrace_cache_mutex}; |
463 | | |
464 | 117M | StackTraceCache& cache = cacheInstance(); |
465 | 117M | const StackTraceRefTriple key {pointers, offset, size, dwarf_location_info_mode}; |
466 | | |
467 | 117M | if (auto it = cache.find(key); it != cache.end()) { |
468 | 117M | return it->second; |
469 | 117M | } else { |
470 | 55.6k | std::stringstream out; |
471 | 55.6k | toStringEveryLineImpl(dwarf_location_info_mode, key, |
472 | 6.61M | [&](std::string_view str) { out << str << '\n'; }); |
473 | | |
474 | 55.6k | return cache |
475 | 55.6k | .emplace(StackTraceTriple {pointers, offset, size, dwarf_location_info_mode}, |
476 | 55.6k | out.str()) |
477 | 55.6k | .first->second; |
478 | 55.6k | } |
479 | 117M | } |
480 | | |
481 | | std::string StackTrace::toString(int start_pointers_index, |
482 | 117M | const std::string& dwarf_location_info_mode) const { |
483 | | // Default delete the first three frame pointers, which are inside the stack_trace.cpp. |
484 | 117M | start_pointers_index += 3; |
485 | 117M | StackTrace::FramePointers frame_pointers_raw {}; |
486 | 117M | std::copy(frame_pointers.begin() + start_pointers_index, frame_pointers.end(), |
487 | 117M | frame_pointers_raw.begin()); |
488 | 117M | return toStringCached(frame_pointers_raw, offset, size - start_pointers_index, |
489 | 117M | dwarf_location_info_mode); |
490 | 117M | } |
491 | | |
492 | | std::string StackTrace::toString(void** frame_pointers_raw, size_t offset, size_t size, |
493 | 5 | const std::string& dwarf_location_info_mode) { |
494 | 5 | __msan_unpoison(frame_pointers_raw, size * sizeof(*frame_pointers_raw)); |
495 | | |
496 | 5 | StackTrace::FramePointers frame_pointers {}; |
497 | 5 | std::copy_n(frame_pointers_raw, size, frame_pointers.begin()); |
498 | | |
499 | 5 | return toStringCached(frame_pointers, offset, size, dwarf_location_info_mode); |
500 | 5 | } |
501 | | |
502 | 8 | void StackTrace::createCache() { |
503 | 8 | std::lock_guard lock {stacktrace_cache_mutex}; |
504 | 8 | cacheInstance(); |
505 | 8 | } |
506 | | |
507 | 2 | void StackTrace::dropCache() { |
508 | 2 | std::lock_guard lock {stacktrace_cache_mutex}; |
509 | 2 | cacheInstance().clear(); |
510 | 2 | } |