libxtc
0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_tail.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_tail.h
6
* A runtime microscope: cheap, high-volume recording of what the
7
* scheduler and runtime are actually doing -- "tail -f a system" --
8
* so hard async bugs (lost/late wakeups, long polls, mailbox backups,
9
* scheduler imbalance) become obvious after the fact. Inspired by
10
* dial9 ("a microscope for Tokio").
11
*
12
* Unlike xtc_stats (aggregate counters) and xtc_trace (the causal
13
* message trace), xtc_tail records every INDIVIDUAL runtime event
14
* tied to a precise instant, so a degraded window can be diffed
15
* against a normal one. It is OFF by default and one branch when
16
* disabled -- observability must not tax production.
17
*
18
* Phase 1 (this): the SCHED source (proc spawn / exit / wake / run)
19
* over a bounded per-process ring, an in-process read callback, and a
20
* versioned binary dump to an fd. The IO/OS sources, on-disk spill
21
* with rotation, and the offline viewer are staged follow-ons (see
22
* the roadmap); the record format and API are designed to accept them
23
* without a break.
24
*/
25
26
#ifndef XTC_TAIL_H
27
#define XTC_TAIL_H
28
29
#include <stddef.h>
30
#include <stdint.h>
31
32
#include "xtc.h"
33
#include "xtc_proc.h"
34
35
/* Event sources, an enable mask (dial9-style: pay only for what you turn
36
* on). Phase 1 implements SCHED; the others are reserved so the mask is
37
* stable across the staged build-out. */
38
#define XTC_TAIL_SCHED (1u << 0)
/* proc spawn/exit/wake/run */
39
#define XTC_TAIL_MSG (1u << 1)
/* reserved: send/recv/mailbox depth */
40
#define XTC_TAIL_IO (1u << 2)
/* reserved: fd reg/del/completion */
41
#define XTC_TAIL_OS (1u << 3)
/* reserved: per-loop CPU/RSS sampling */
42
#define XTC_TAIL_ALL (XTC_TAIL_SCHED | XTC_TAIL_MSG | XTC_TAIL_IO | XTC_TAIL_OS)
43
44
/* Event kinds recorded by the SCHED source. */
45
enum
xtc_tail_kind {
46
XTC_TAIL_SPAWN = 0,
/* a proc was spawned */
47
XTC_TAIL_EXIT = 1,
/* a proc exited (detail = reason) */
48
XTC_TAIL_WAKE = 2,
/* a parked proc was woken (armed) */
49
XTC_TAIL_RUN = 3,
/* a proc began running after a wake
50
* (detail = wake-to-run latency, ns) */
51
XTC_TAIL_PARK = 4,
/* a proc parked (blocked on recv/timer/fd) */
52
/* MSG source: */
53
XTC_TAIL_SEND = 5,
/* pid sent a message (detail = payload bytes) */
54
XTC_TAIL_RECV = 6,
/* pid received a message (detail = bytes) */
55
XTC_TAIL_MBOX_HWM = 7
/* pid's mailbox depth reached a new high-water
56
* (detail = the new peak depth) */
57
};
58
59
/* One recorded event. Fixed layout; the binary dump writes it verbatim
60
* behind a versioned header, so a reader across the wire/disk decodes it
61
* without guessing. */
62
typedef
struct
xtc_tail_rec {
63
uint64_t ts_ns;
/* monotonic timestamp (ns) */
64
uint32_t source;
/* which XTC_TAIL_* source produced it */
65
uint32_t kind;
/* enum xtc_tail_kind */
66
xtc_pid_t
pid;
/* the proc the event concerns */
67
uint64_t detail;
/* kind-specific (EXIT reason / RUN latency ns) */
68
}
xtc_tail_rec_t
;
69
70
/* Visit callback for xtc_tail_read: return 0 to continue, nonzero stops. */
71
typedef
int (*xtc_tail_fn)(
const
xtc_tail_rec_t
*rec,
void
*user);
72
73
/*
74
* PUBLIC: unsigned xtc_tail_enable __P((unsigned));
75
* PUBLIC: void xtc_tail_disable __P((void));
76
* PUBLIC: int xtc_tail_reset __P((void));
77
* PUBLIC: int xtc_tail_read __P((xtc_tail_fn, void *));
78
* PUBLIC: int xtc_tail_dump __P((int));
79
* PUBLIC: size_t xtc_tail_count __P((void));
80
*/
81
82
/* Enable the named sources (a bitwise-OR of XTC_TAIL_*). Returns the
83
* previously enabled mask. Enabling is idempotent; call with the full
84
* mask you want each time (it replaces, not ORs). */
85
unsigned
xtc_tail_enable(
unsigned
source_mask);
86
87
/* Disable all recording (equivalent to xtc_tail_enable(0)). */
88
void
xtc_tail_disable(
void
);
89
90
/* Drop all buffered records. Returns XTC_OK. */
91
int
xtc_tail_reset(
void
);
92
93
/* Visit every buffered record oldest-first (a stable snapshot). */
94
int
xtc_tail_read(xtc_tail_fn cb,
void
*user);
95
96
/* Write the buffered records to `fd` as a versioned binary trace:
97
* a small header (magic, version, record count, record size) followed
98
* by the records verbatim. A separate offline tool renders it. */
99
int
xtc_tail_dump(
int
fd);
100
101
/* Number of records currently buffered. */
102
size_t
xtc_tail_count(
void
);
103
104
/* Internal: record one event from a runtime hook point (proc spawn/exit,
105
* etc.). Not part of the public API -- callers use xtc_tail_enable to
106
* turn recording on and the read/dump/count functions to consume it.
107
* A no-op fast path (one branch) when `source` is disabled. */
108
void
__xtc_tail_emit(
unsigned
source,
unsigned
kind,
xtc_pid_t
pid,
109
uint64_t detail);
110
111
/* Internal: 1 if `source` is currently enabled. A hook point uses this
112
* to guard extra work (e.g. a clock read) so a disabled tail costs one
113
* branch and has no side effects. */
114
int
__xtc_tail_on(
unsigned
source);
115
116
/* On-disk binary dump header (also used by the offline reader).
117
*
118
* Format v2 is COMPACT and PORTABLE (dial9-style): all header fields are
119
* written as explicit little-endian bytes (no struct memcpy, so it is
120
* byte-identical across endianness and padding), and each event is
121
* varint/delta encoded rather than a fixed 32-byte record:
122
*
123
* header: magic[4]="XTCL" version(LE u32)=2 flags(LE u32)
124
* count(LE u32) base_ts_ns(LE u64)
125
* flags bit0 = 1 -> little-endian canonical stream (always set today)
126
* per event (oldest first):
127
* kind : 1 byte
128
* source : 1 byte
129
* ts_delta: LEB128 varint, ns since the previous event (base for the
130
* first) -- monotonic timestamps make this 1-2 bytes
131
* loop_id : LEB128 varint (pid.loop_id)
132
* local_id: LEB128 varint (pid.local_id)
133
* gen : LEB128 varint (pid.gen)
134
* detail : LEB128 varint (EXIT reason / RUN latency ns)
135
*
136
* Typical ~6-12 bytes/event vs 32 for the raw struct, and portable.
137
* xtc_tail_read (in-process) still hands back the fixed xtc_tail_rec_t. */
138
#define XTC_TAIL_MAGIC 0x5854434Cu
/* "XTCL" */
139
#define XTC_TAIL_VERSION 2u
140
#define XTC_TAIL_FLAG_LE 1u
/* little-endian canonical stream */
141
typedef
struct
xtc_tail_hdr {
142
uint32_t magic;
/* XTC_TAIL_MAGIC */
143
uint32_t version;
/* XTC_TAIL_VERSION */
144
uint32_t flags;
/* XTC_TAIL_FLAG_* (endianness marker) */
145
uint32_t count;
/* number of events that follow */
146
uint64_t base_ts_ns;
/* timestamp of the first event (deltas from here) */
147
}
xtc_tail_hdr_t
;
148
149
#endif
/* XTC_TAIL_H */
xtc_pid_t
Definition
xtc_proc.h:33
xtc_tail_hdr_t
Definition
xtc_tail.h:141
xtc_tail_rec_t
Definition
xtc_tail.h:62
src
inc
xtc_tail.h
Generated by
1.9.8