Coverage Report

Created: 2026-07-17 18:33

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