xtc_inspect(3)

---

xtc_inspect(3)

live process and loop introspection

XTC_INSPECT(3) Library Functions Manual XTC_INSPECT(3)

xtc_inspect_procs, xtc_inspect_loops, xtc_proc_info, xtc_runtime_infolive process and loop introspection

#include <xtc.h>
#include <xtc_inspect.h>
#include <xtc_runtime.h>

typedef int (*xtc_inspect_proc_fn)(const xtc_proc_info_t *info, void *user);
typedef int (*xtc_inspect_loop_fn)(const xtc_loop_info_t *info, void *user);

int
xtc_inspect_procs(xtc_inspect_proc_fn cb, void *user);

int
xtc_inspect_loops(xtc_inspect_loop_fn cb, void *user);

int
xtc_proc_info(xtc_pid_t pid, xtc_proc_info_t *out);

int
xtc_runtime_info(xtc_runtime_info_t *out);

xtc_inspect is the programmatic form of the debugger's xtc-procs and xtc-loops commands (xtc_proc(3), xtc_loop(3)), callable from inside a running program rather than from a stopped one. It is the libxtc analog of Erlang's () plus the data renders: given the live runtime, enumerate every proc and loop and read each one's vital signs. Its consumers are an admin command (a “SHOW PROCESSES” verb), a metrics scraper, or a terminal observer.

Each call takes a best-effort snapshot under the per-loop slot locks. For the duration of the call the proc set is consistent: no proc is created or reaped out from under the walk. The per-proc mailbox counters are read under the mailbox lock, so they are individually coherent. The scheduler-owned run state is : it reflects an instant during the call and may change immediately after. A snapshot is therefore a faithful point-in-time view, not a stop-the-world freeze.

The enumeration callbacks run all internal locks are released. A callback may freely call back into the proc and loop APIs (including (), xtc_proc_info()), or another xtc_inspect_procs() without risk of self-deadlock or lock-order inversion. A callback returns 0 to continue the walk and any non-zero value to stop early; the enumeration functions then return the number of items visited so far.

() invokes cb once per live proc, across all loops, passing a pointer to a populated xtc_proc_info_t and the caller's user pointer. It returns the number of procs visited, or a negative XTC_E_* code on error.

() invokes cb once per registered loop, passing a xtc_loop_info_t. It returns the loop count, or a negative XTC_E_* code.

() snapshots a single proc, named by pid, into *out. It is the erlang:process_info() analog for one identity.

A proc snapshot is reported in:

typedef struct xtc_proc_info {
        xtc_pid_t pid;
        int       run_state;        /* enum xtc_proc_run_state */
        int       park_reason;      /* enum xtc_proc_park */
        int       alive;
        int       kill_pending;
        size_t    mbox_len;         /* current depth */
        size_t    mbox_peak;        /* high-water mark */
        size_t    mbox_cap;
        size_t    mbox_saved;       /* selective-receive save queue */
        uint64_t  mbox_recv_total;  /* messages ever accepted */
        uint64_t  mbox_drop_total;  /* messages ever rejected */
} xtc_proc_info_t;

The fields are:

pid
The proc identity.
run_state
The sampled scheduler state, one of the enum xtc_proc_run_state values below.
park_reason
Why a XTC_PROC_PARKED proc is parked, one of the enum xtc_proc_park values below; XTC_PARK_NONE when the proc is not parked.
alive
Non-zero while the proc is live.
kill_pending
Non-zero if an asynchronous () has been delivered but not yet acted on.
mbox_len
The live mailbox depth. This is the single most useful health signal in a message-passing system: a large or growing value is the most common pathology, the same vital sign the debugger's xtc-procs ranks on.
mbox_peak
The mailbox high-water mark; a peak far above mbox_len records a burst the proc fell behind on.
mbox_cap
The mailbox capacity bound.
mbox_saved
The selective-receive save-queue depth (xtc_proc(3)).
mbox_recv_total
Lifetime count of messages accepted.
mbox_drop_total
Lifetime count of messages rejected because the mailbox was full or the proc was dead.

enum xtc_proc_run_state mirrors the scheduler task state:

Runnable, waiting for a turn on its loop.
Currently executing on its loop's thread.
Blocked; see park_reason.
Finished, awaiting reap.

enum xtc_proc_park records why a parked proc is waiting:

Not parked.
Waiting on a file descriptor (()).
Waiting on a timer (()) or a timed ().
Waiting for a message (a blocking xtc_recv()).

A loop snapshot is reported in:

typedef struct xtc_loop_info {
        int      loop_id;     /* 0 standalone, exec slot + 1 otherwise */
        int      n_procs;     /* live procs homed on this loop */
        int      n_alive;     /* live tasks (procs + plain tasks) */
        uint64_t tasks_run;
        uint64_t steals;
} xtc_loop_info_t;

tasks_run and steals are the scheduler-utilization view: how much work the loop has run and how much it took by work-stealing.

Link and monitor topology is intentionally NOT exposed by the live API. A proc mutates its own link and monitor lists without a lock, so walking them from another thread would race. To inspect link/monitor topology use the debugger (xtc-proc), which runs against a stopped program; the live API reports only the fields that are safe to read concurrently -- the mailbox counters, read under the mailbox lock, and the sampled run state.

xtc_runtime_info() fills *out with a one-shot snapshot of the running configuration -- loop count, online / performance / efficiency CPU counts, NUMA node count, and the configured vs currently-accounted memory -- so a program need not assemble that itself. It returns XTC_OK on success.

xtc_inspect_procs() returns the number of procs visited; xtc_inspect_loops() returns the number of loops visited. Both return a negative XTC_E_* code on error.

xtc_proc_info() returns XTC_OK on success, XTC_E_NOTFOUND if no live proc has the given pid, or XTC_E_INVAL if out is NULL. See xtc_strerror(3).

Rank live procs by mailbox depth, the way a “SHOW PROCESSES” admin command would:

static int
on_proc(const xtc_proc_info_t *p, void *user)
{
        (void)user;
        if (p->mbox_len > 0)
                printf("pid=%llu mbox=%zu peak=%zu state=%d\n",
                    (unsigned long long)p->pid,
                    p->mbox_len, p->mbox_peak, p->run_state);
        return (0);                 /* 0 = keep going */
}

int n = xtc_inspect_procs(on_proc, NULL);
printf("%d live procs\n", n);

Snapshot one proc by pid:

xtc_proc_info_t info;
if (xtc_proc_info(pid, &info) == XTC_OK && info.mbox_len > info.mbox_cap / 2)
        log_warn("proc %llu mailbox half full", (unsigned long long)pid);

xtc_proc(3), xtc_reg(3), xtc_stats(3)

The xtc project.

June 1, 2026 Debian

View the mdoc source