Coverage Report

Created: 2026-06-17 13:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/common/stack_trace.cpp
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
}