From c8ba4d42b871248b432d9765fbd58dfa21b84b35 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 10 Apr 2026 20:28:59 -0400 Subject: [PATCH] xo-arena: + print_backtrace() + print_backtrace_dwarf() using libunwind and elfutils --- include/xo/arena/backtrace.hpp | 13 +++ src/arena/backtrace.cpp | 158 +++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 include/xo/arena/backtrace.hpp create mode 100644 src/arena/backtrace.cpp diff --git a/include/xo/arena/backtrace.hpp b/include/xo/arena/backtrace.hpp new file mode 100644 index 00000000..d29bcb60 --- /dev/null +++ b/include/xo/arena/backtrace.hpp @@ -0,0 +1,13 @@ +/** @file backtrace.hpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#pragma once + +namespace xo { + void print_backtrace(bool demangle_flag); + void print_backtrace_dwarf(bool demangle_flag); +} + +/* end backtrace.hpp */ diff --git a/src/arena/backtrace.cpp b/src/arena/backtrace.cpp new file mode 100644 index 00000000..a1f5e009 --- /dev/null +++ b/src/arena/backtrace.cpp @@ -0,0 +1,158 @@ +/** @file backtrace.cpp + * + * @author Roland Conybeare, Apr 2026 + **/ + +#include "backtrace.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace xo { + void + print_backtrace(bool demangle_flag) { + unw_cursor_t cursor; + unw_context_t cx; + + // capture cpu register state at this call site + unw_getcontext(&cx); + + // stack frame iterator for current thread. + // local -> this process + // + unw_init_local(&cursor, &cx); + + // depth relative to top of call stack + int depth = 0; + while (unw_step(&cursor) > 0) { + unw_word_t pc = 0; + + // read return address of current frame into pc. + // This determines the function that is executing + // when print_backtrace() invoked + // + unw_get_reg(&cursor, UNW_REG_IP, &pc); + + std::array name; + unw_word_t offset = 0; + + // mangled function name for current frame's pc. + // + if (unw_get_proc_name(&cursor, name.data(), sizeof(name), &offset) == 0) { + int status = 0; + + // we are resaponsible for calling ::free() on non-null demangled value + char * demangled = nullptr; + + if (demangle_flag) + demangled = abi::__cxa_demangle(name.data(), nullptr, nullptr, &status); + + if ((status == 0) && demangled) { + fprintf(stderr, "#%d 0x%lx %s+0x%lx\n", + depth, (long)pc, demangled, (long)offset); + free(demangled); + } else { + // demangle failed (or disabled) + fprintf(stderr, "#%d 0x%lx %s+0x%lx\n", + depth, (long)pc, name.data(), (long)offset); + } + } else { + // unable to get function name + fprintf(stderr, "#%d 0x%lx ???\n", depth, (long)pc); + } + } + } + namespace { + // libdwfl requires callbacks for find_elf and find_debuginfo. + // The offline defaults work for the current process. + // + static const Dwfl_Callbacks dwfl_callbacks = { + .find_elf = dwfl_linux_proc_find_elf, + .find_debuginfo = dwfl_standard_find_debuginfo, + .section_address = nullptr, + .debuginfo_path = nullptr, + }; + } + + void + print_backtrace_dwarf(bool demangle_flag) { + unw_cursor_t cursor; + unw_context_t cx; + + unw_getcontext(&cx); + unw_init_local(&cursor, &cx); + + // set up dwfl for resolving addresses to source locations. + // + Dwfl * dwfl = dwfl_begin(&dwfl_callbacks); + + if (dwfl) { + // populate module list from /proc/self/maps + dwfl_linux_proc_report(dwfl, getpid()); + dwfl_report_end(dwfl, nullptr, nullptr); + } + + int depth = 0; + while (unw_step(&cursor) > 0) { + unw_word_t pc = 0; + unw_get_reg(&cursor, UNW_REG_IP, &pc); + + std::array name; + unw_word_t offset = 0; + + // resolve function name via libunwind + // + const char * func_name = "???"; + char * demangled = nullptr; + int status = -1; + + if (unw_get_proc_name(&cursor, name.data(), name.size(), &offset) == 0) { + if (demangle_flag) + demangled = abi::__cxa_demangle(name.data(), nullptr, nullptr, &status); + + func_name = ((status == 0) && demangled) ? demangled : name.data(); + } + + // resolve source file + line via DWARF debug info + // + const char * source_file = nullptr; + int line = 0; + + if (dwfl) { + Dwfl_Module * module = dwfl_addrmodule(dwfl, pc); + + if (module) { + Dwfl_Line * dwfl_line = dwfl_module_getsrc(module, pc); + + if (dwfl_line) { + source_file = dwfl_lineinfo(dwfl_line, nullptr, &line, + nullptr, nullptr, nullptr); + } + } + } + + if (source_file) { + fprintf(stderr, "#%d 0x%lx %s+0x%lx at %s:%d\n", + depth, (long)pc, func_name, (long)offset, + source_file, line); + } else { + fprintf(stderr, "#%d 0x%lx %s+0x%lx\n", + depth, (long)pc, func_name, (long)offset); + } + + if (demangled) + free(demangled); + + ++depth; + } + + if (dwfl) + dwfl_end(dwfl); + } +} /*namespace xo*/ + +/* end backtrace.cpp */