xtc_dump(3)

---

xtc_dump(3)

crash diagnostics: runtime-state dump, panic, fatal-signal handler

XTC_DUMP(3) Library Functions Manual XTC_DUMP(3)

xtc_dump, xtc_panic, xtc_crash_handler_installcrash diagnostics: runtime-state dump, panic, fatal-signal handler

#include <xtc.h>
#include <xtc_dump.h>

void
xtc_dump(int fd);

void
xtc_panic(const char *file, int line, const char *fmt, ...);

int
xtc_crash_handler_install(void);

XTC_PANIC(fmt, ...);

XTC_ASSERT(cond);

XTC_ASSERT_F(cond, fmt, ...);

These routines turn the live runtime into a human-readable diagnostic on the way down, without a debugger or a core file. The dump is the in-process counterpart of the debugger scripts (xtc_proc(3), xtc_inspect(3)): the same proc, loop, and mailbox view an session shows, written to a file descriptor.

() writes a best-effort runtime-state dump to fd (commonly STDERR_FILENO):

  1. the C backtrace of the OS thread, when a backtrace backend is compiled in (see PORTABILITY);
  2. every scheduler loop with its run-queue and work-steal counters;
  3. every live proc with its run state, park reason, and mailbox depth, peak, capacity, and lifetime receive/drop counts.
The field labels match the debugger scripts, so an in-process dump and a gdb(1) session read alike. xtc_dump() never allocates and never aborts; it writes through a fixed stack buffer with raw write(2) calls, so it can run at any time and does not perturb the heap it may be diagnosing. The caller decides what to do next.

() emits fmt/... as a panic banner, then xtc_dump(STDERR_FILENO), then calls abort(3). It does not return. Call it through the () macro, which supplies file and line:

XTC_PANIC("bad frame %ld: pin=%d", (long)idx, pin);

() panics with the failed expression when cond is false; () does the same with a caller-supplied message. Unlike the standard assert(3), both emit the full runtime dump before aborting, and both are always compiled in (they are diagnostics, not debug-only checks).

XTC_ASSERT(frame->pin > 0);
XTC_ASSERT_F(n <= cap, "overflow: n=%zu cap=%zu", n, cap);

() installs a handler for the fatal signals SIGSEGV, SIGBUS, SIGABRT, SIGFPE, and SIGILL that emits a backtrace and a best-effort runtime dump to standard error, then re-raises the signal with its default disposition so a core is still produced. It is idempotent and installs handlers only for faults -- never for SIGCHLD, SIGTERM, or other signals an embedder may manage. It returns XTC_OK, or XTC_E_NOSYS on a platform without POSIX signals.

A fiber's own C stack is not unwound: it lives in a saved coroutine context, not on a live OS thread. Like an Erlang crash dump or a Go panic, the dump reports each proc's state and mailbox -- which proc is parked, on what, with how deep a mailbox -- not N reconstructed C stacks. Only the calling (faulting) thread's C stack is shown. For deep per-fiber stacks attach a debugger to a core; the dump and the debugger share field names so the two views line up.

This makes the facility strongest for the failure mode it was built for: a hung or wedged message-passing system, where the question is “which proc is waiting on whom, and why did it not wake.” For domain data-structure invariants, keep using () on those structures directly.

xtc_dump() takes the per-loop inspection locks (via xtc_inspect(3)), so it is fully reliable from an explicit () or XTC_ASSERT() in normal context. From the fatal-signal handler the banner and backtrace are always emitted -- they use only async-signal-safe primitives -- but the proc/loop walk is attempted best-effort and may block if the fault happened while a loop lock was held. A hung handler still leaves the banner and backtrace, and the re-raised signal still yields a core.

The C backtrace uses the () seam, selected at configure time in priority order: the <execinfo.h> () backend on glibc, macOS, and the BSDs (symbolized frames); a libunwind(3) backend where execinfo is absent (notably musl), symbolized best-effort with dladdr(3) or addresses-only when dladdr is unavailable; a Windows DbgHelp backend ((), ()), compiled and reviewed but not yet runtime-verified on a Windows host; and otherwise a no-op stub. Where no backend is present the dump omits the C stack but still reports the full proc/loop/mailbox state. xtc_crash_handler_install() returns XTC_E_NOSYS on Windows (SEH is future work).

xtc_dump() returns nothing. xtc_panic() does not return. xtc_crash_handler_install() returns XTC_OK, or XTC_E_NOSYS where unsupported.

Install the crash handler at startup, then assert an invariant:

xtc_crash_handler_install();
...
XTC_ASSERT_F(pin > 0, "unfix underflow: frame=%ld", (long)idx);

xtc_inspect(3), xtc_proc(3), xtc_loop(3), xtc_slab(3)

The xtc project.

June 4, 2026 Debian

View the mdoc source