libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_trace.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * Use of this source code is governed by the ISC License.
4 *
5 * src/inc/xtc_trace.h
6 * Causal message tracing -- libxtc's seq_trace. An opt-in,
7 * bounded ring of trace events (message sends and receives,
8 * process spawns and exits), each stamped with a hybrid logical
9 * clock (HLC) so the true happens-before of a request can be
10 * reconstructed across procs and cores even when wall-clock
11 * arrival order lies.
12 *
13 * The HLC is a 64-bit stamp: the high 48 bits are a monotonic
14 * physical-time component (microseconds) and the low 16 bits a
15 * logical counter. Every traced event ticks one global clock, so
16 * a send's stamp is always less than the stamp of the receive it
17 * causes; a RECV record also carries the originating SEND's stamp
18 * in `cause`, so the causal edge is explicit. (A per-shard HLC for
19 * the MVCC data path is a separate, later
20 * use of the same idea.)
21 *
22 * Tracing is OFF by default and costs a single relaxed atomic load
23 * on the message hot path when disabled. When enabled it serializes
24 * ring writes under a lock; enable it to debug, as with recon/dbg
25 * in the BEAM, not as an always-on production tax.
26 *
27 * See docs/guide/debugging.md.
28 */
29
30#ifndef XTC_TRACE_H
31#define XTC_TRACE_H
32
33#include "xtc_export.h"
34
35#include <stddef.h>
36#include <stdint.h>
37
38#include "xtc.h"
39#include "xtc_proc.h"
40
41enum xtc_trace_kind {
42 XTC_TRACE_SEND = 0, /* self sent a message to peer */
43 XTC_TRACE_RECV = 1, /* self received a message from peer */
44 XTC_TRACE_SPAWN = 2, /* self (parent) spawned peer (child) */
45 XTC_TRACE_EXIT = 3 /* self exited; detail = reason */
46};
47
48typedef struct xtc_trace_rec {
49 uint64_t hlc; /* this event's HLC stamp */
50 uint64_t cause; /* RECV: the originating send's HLC; else 0 */
51 int kind; /* enum xtc_trace_kind */
52 xtc_pid_t self; /* the proc the event happened in */
53 xtc_pid_t peer; /* the other proc (dest / source / child) */
54 uint32_t detail; /* SEND/RECV: payload bytes; EXIT: reason */
56
57/* Visit callback: return 0 to continue, nonzero to stop early. */
58typedef int (*xtc_trace_fn)(const xtc_trace_rec_t *rec, void *user);
59
60/*
61 * PUBLIC: int xtc_trace_enable __P((int));
62 * PUBLIC: int xtc_trace_reset __P((void));
63 * PUBLIC: int xtc_trace_dump __P((xtc_trace_fn, void *));
64 * PUBLIC: uint64_t xtc_hlc_now __P((void));
65 */
66
67/* Turn tracing on (on != 0) or off. Returns the previous state. */
68XTC_API int xtc_trace_enable(int on);
69
70/* Drop all buffered trace records. Returns XTC_OK. */
71XTC_API int xtc_trace_reset(void);
72
73/* Visit buffered records in causal (HLC-ascending) order. Returns the
74 * number visited, or a negative XTC_E_* on error. */
75XTC_API int xtc_trace_dump(xtc_trace_fn cb, void *user);
76
77/* The current global HLC value (for tests and display). */
78XTC_API uint64_t xtc_hlc_now(void);
79
80/* ---- A3: async causal trace (per-fiber suspend/resume ring) ----
81 *
82 * Cats Effect keeps a small per-fiber ring of the await/resume call
83 * sites and splices that causal chain onto a fault or fiber dump, so a
84 * dump answers not just WHAT a fiber's state is now but HOW it got here.
85 * This is libxtc's C analog. Where xtc_trace above records the causal
86 * chain of MESSAGES between procs (seq_trace), the causal trace here
87 * records the suspend/resume chain WITHIN one proc: the ordered sites at
88 * which the fiber parked and resumed (mailbox recv, timer sleep, fd
89 * wait, ...). xtc_dump splices each proc's recent chain onto its state
90 * line when the trace is enabled.
91 *
92 * It is OFF by default and ZERO-COST when off: the per-proc ring is
93 * written only when the trace is enabled, so a disabled trace is a
94 * single relaxed-atomic load + branch on the suspend/resume boundary and
95 * touches no memory. The ring is per-proc and core-private -- each
96 * write is one index bump and a store on the owning fiber, no lock, no
97 * atomic on the record, no allocation after spawn. Enable it to debug a
98 * stuck or mis-scheduled fiber, not as an always-on tax.
99 *
100 * See docs/guide/debugging.md.
101 */
102
103/* The kind of suspend/resume boundary a causal record marks. A PARK_*
104 * value names WHY the fiber suspended; RESUME marks the matching wake. */
105enum xtc_causal_kind {
106 XTC_CAUSAL_PARK_MAILBOX = 0, /* parked in xtc_recv* (await a message) */
107 XTC_CAUSAL_PARK_TIMER = 1, /* parked in xtc_proc_sleep (a delay) */
108 XTC_CAUSAL_PARK_FD = 2, /* parked in xtc_proc_wait_fd (I/O) */
109 XTC_CAUSAL_RESUME = 3 /* the fiber resumed after a park */
110};
111
112/* One per-fiber causal record: the boundary kind + a static site label
113 * (the __func__ of the park site, or a caller-supplied string literal --
114 * always a static string, so the ring stores the pointer, never a copy). */
115typedef struct xtc_causal_rec {
116 int kind; /* enum xtc_causal_kind */
117 const char *site; /* static label (e.g. __func__); never freed */
119
120/* Visit callback: return 0 to continue, nonzero to stop early. */
121typedef int (*xtc_causal_fn)(const xtc_causal_rec_t *rec, void *user);
122
123/*
124 * PUBLIC: int xtc_trace_causal_enable __P((int));
125 * PUBLIC: int xtc_trace_causal_dump __P((xtc_pid_t, xtc_causal_fn, void *));
126 */
127
128/* Turn the per-fiber causal trace on (on != 0) or off. Returns the
129 * previous state (1 on, 0 off). Off by default; when off the
130 * suspend/resume boundary pays one relaxed load + branch. */
131XTC_API int xtc_trace_causal_enable(int on);
132
133/* Visit the causal records of proc `pid` oldest-first (the fiber's
134 * recent park/resume chain). Returns the number visited, XTC_E_NOTFOUND
135 * if no live proc has that pid, or XTC_E_INVAL on a NULL callback. When
136 * the trace is disabled a live proc simply has an empty ring (0). */
137XTC_API int xtc_trace_causal_dump(xtc_pid_t pid, xtc_causal_fn cb, void *user);
138
139#endif /* XTC_TRACE_H */