aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/dwarf_backtrace/backtrace.cpp
blob: 7dcfd12ea77d3096af00cb735ee424c4986dde4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include "backtrace.h"

#include <contrib/libs/backtrace/backtrace.h>

#include <util/generic/yexception.h>
#include <util/system/type_name.h>
#include <util/system/execpath.h>

#include <mutex>

namespace NDwarf {
    namespace {
        struct TContext {
            TCallback& Callback;
            int Counter = 0;
            TMaybe<TError> Error;
        };

        void HandleLibBacktraceError(void* data, const char* msg, int errnum) {
            auto* context = reinterpret_cast<TContext*>(data);
            context->Error = TError{.Code = errnum, .Message=msg};
        }

        int HandleLibBacktraceFrame(void* data, uintptr_t pc, const char* filename, int lineno, const char* function) {
            auto* context = reinterpret_cast<TContext*>(data);
            TLineInfo lineInfo{
                .FileName = filename != nullptr ? filename : "???",
                .Line = lineno,
                .Col = 0, // libbacktrace doesn't provide column numbers, so fill this field with a dummy value.
                .FunctionName = function != nullptr ? CppDemangle(function) : "???",
                .Address = pc,
                .Index = context->Counter++,
            };
            return static_cast<int>(context->Callback(lineInfo));
        }

        auto CreateBacktraceState(TContext& context, bool threaded) {
            return backtrace_create_state(
                GetPersistentExecPath().c_str(),
                static_cast<int>(threaded),
                HandleLibBacktraceError,
                &context /* data for the error callback */
            );
        }

        TMaybe<TError> ResolveBacktraceImpl(backtrace_state* state, TArrayRef<const void* const> backtrace, TContext& context) {
            if (nullptr == state) {
                static const auto initError = context.Error;
                return initError;
            }
            for (const void* address : backtrace) {
                int status = backtrace_pcinfo(
                    state,
                    reinterpret_cast<uintptr_t>(address) - 1, // last byte of the call instruction
                    HandleLibBacktraceFrame,
                    HandleLibBacktraceError,
                    &context /* data for both callbacks */);
                if (0 != status) {
                    break;
                }
            }
            return context.Error;
        }
    }

    TMaybe<TError> ResolveBacktrace(TArrayRef<const void* const> backtrace, TCallback callback) {
        TContext context{.Callback = callback};
        // Intentionally never freed (see the documentation to `backtrace_create_state`).
        static auto* state{CreateBacktraceState(context, true /* enable threading support */)};

        return ResolveBacktraceImpl(state, backtrace, context);
    }

    TMaybe<TError> ResolveBacktraceLocked(TArrayRef<const void* const> backtrace, TCallback callback) {
        TContext context{.Callback = callback};
        // Intentionally never freed (see the documentation to `backtrace_create_state`).
        static auto* state{CreateBacktraceState(context, false /* disable threading support */)};
        static std::mutex mutex;

        const std::lock_guard lock{mutex};
        return ResolveBacktraceImpl(state, backtrace, context);
    }
}