#include "backtrace.h" #include #include #include #include #include namespace NDwarf { namespace { struct TContext { TCallback& Callback; int Counter = 0; TMaybe Error; }; void HandleLibBacktraceError(void* data, const char* msg, int errnum) { auto* context = reinterpret_cast(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(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(context->Callback(lineInfo)); } auto CreateBacktraceState(TContext& context, bool threaded) { return backtrace_create_state( GetPersistentExecPath().c_str(), static_cast(threaded), HandleLibBacktraceError, &context /* data for the error callback */ ); } TMaybe ResolveBacktraceImpl(backtrace_state* state, TArrayRef 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(address) - 1, // last byte of the call instruction HandleLibBacktraceFrame, HandleLibBacktraceError, &context /* data for both callbacks */); if (0 != status) { break; } } return context.Error; } } TMaybe ResolveBacktrace(TArrayRef 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 ResolveBacktraceLocked(TArrayRef 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); } }