Skip to content

Repository files navigation

libgunwinder

English | 简体中文

libgunwinder is a Linux userspace stack unwinding library for long-running profilers and production diagnostics. It focuses on low-overhead unwinding from caller-provided register and stack snapshots, while reusing ELF, symbol, and CFI metadata across samples.

The project name expands to Global Unwinder.

Contents

Features

  • Lazy ELF, DWARF, CFI, and symbol loading for repeated unwinds.
  • Reusable per-process and per-ELF caches to reduce allocation churn during sampling.
  • DWARF CFI unwinding with a frame-pointer fast path when GU_FLAG_HINT_SET_FP is provided.
  • Build-ID based ELF/debug-file matching and Go symbol handling options.
  • PID lifecycle events for cache cleanup on process exit or module reload.
  • Debug dump read/write helpers and a replay tool for offline investigation.
  • Kernel symbol lookup helpers for diagnostics that need kernel address resolution.

Supported Platforms

  • Linux
  • x86_64
  • aarch64

The Makefile selects architecture-specific headers from include/arch/x86 or include/arch/arm64.

Dependencies

Build-time and link-time dependencies:

  • GNU make
  • A C compiler such as GCC or Clang
  • elfutils libraries: libelf and libdw
  • binutils libraries: libbfd and libiberty
  • OpenSSL libraries: libssl and libcrypto
  • POSIX/Linux system headers

Build

make

The build creates:

  • lib/libgunwinder.a
  • lib/libgunwinder.so
  • bin/bt_debug

Use V=1 to print the full compiler and linker commands:

make V=1

Install

make install DESTDIR=/path/to/stage

Installed files are placed under:

  • /usr/lib
  • /usr/include/gunwinder

Quick Start

#include<gunwinder/unwinder.h>#include<stdio.h>#include<sys/types.h>staticvoidon_frame(conststructgu_frame_record*frame, void*user_ctx)
{
(void)user_ctx;
if (frame->symbol)
printf("%s+0x%lx\n", frame->symbol, frame->offset);
}
voidunwind_sample(pid_tpid, void*regs, uint32_tregs_size, uint8_t*stack, size_tstack_size)
{
structgu_init_cfgcfg= {
.debug_print= false,
.go_not_strip_name= false,
.go_buildid_only= false,
};
structgu_context*ctx=gu_init(&cfg);
if (!ctx)
return;
uint64_tunique_id=gu_preload_pid_debug_info(ctx, pid);
structgu_stack_infoinfo= {
.pid=pid,
.unique_id=unique_id,
.regs=regs,
.regs_size=regs_size,
.stack_data=stack,
.stack_size=stack_size,
};
gu_unwind(ctx, &info, on_frame, NULL);
gu_event_occur(ctx, GU_EVENT_PROCESS_EXIT, &pid);
gu_cleanup(ctx);
}

Callers own the register and stack buffers passed through struct gu_stack_info. libgunwinder reads them during gu_unwind() and does not take ownership.

Architecture

caller snapshot
| regs + stack bytes
v
gu_unwind()
| per-pid executable intervals
| per-ELF symbols, build IDs, debug files
v
DWARF CFI / frame-pointer unwinding
|
v
gu_frame_callback_t

The main public entry points are declared in include/gunwinder/unwinder.h:

  • gu_init() and gu_cleanup() manage the root unwinder context.
  • gu_preload_pid_debug_info() warms per-PID ELF/debug metadata before sampling.
  • gu_unwind() walks a stack snapshot and emits frames through a callback.
  • gu_event_occur() notifies the unwinder about process lifecycle events.
  • gu_debug_dump_sample() and gu_read_stack_dump() support offline debug dumps.

Tools

bin/bt_debug replays stack dump files created by gu_debug_dump_sample():

bin/bt_debug [-v|--verbose] <dump-file-or-directory>

For timing-oriented runs, redirect normal output to a file so terminal printing does not dominate measured unwind time.

Validation

The repository includes self-contained validation tools. They generate synthetic inputs or use caller-provided dump files; no private test data or internal services are required.

make clean all
bin/cfi_stress
bin/test_stable_fp_miss_reload
bin/cfi_bench --frames 100000 --set-size 100 --warmup 1000
  • cfi_stress covers DWARF expression and CFI edge cases.
  • test_stable_fp_miss_reload checks PID map reload throttling behavior.
  • cfi_bench is a synthetic CFI parser/evaluator microbenchmark. Treat its output as a local performance signal rather than a production workload model.

Concurrency

struct gu_context owns mutable caches. Serialize access to a context when calling gu_unwind(), gu_preload_pid_debug_info(), gu_event_occur(), and cache-related accessors. Use separate contexts if independent threads need to unwind without external locking.

Documentation Links

License

Unless a file states otherwise, libgunwinder is licensed under the GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later). See LICENSE, COPYING, and COPYING.LESSER.

Some vendored third-party headers carry their own notices. See NOTICE and ACKNOWLEDGEMENTS.md.

Acknowledgements

libgunwinder acknowledges:

  • uthash for the vendored uthash.h and utlist.h headers.
  • elfutils and its libelf/libdw libraries. Parts of libgunwinder's DWARF and unwinding behavior were cross-checked against elfutils/libdw. libgunwinder keeps its own data structures, parser/cache layout, and stack-snapshot unwind flow; no elfutils source code is copied into this project. The project uses LGPL-3.0-or-later to align with the LGPLv3-or-later option available to elfutils libraries and backends.

See ACKNOWLEDGEMENTS.md for details.

About

High-performance Linux stack unwinding library optimized for whole-machine, multi-workload continuous profiling with low-overhead DWARF CFI unwinding; used by continue-profiling-agent.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages