xtc_stats(3)
---xtc_stats(3)
runtime statistics: counters, gauges, histograms
| XTC_STATS(3) | Library Functions Manual | XTC_STATS(3) |
NAME
xtc_counter_create,
xtc_counter_inc,
xtc_counter_add,
xtc_counter_read,
xtc_counter_destroy,
xtc_gauge_create,
xtc_gauge_set,
xtc_gauge_add,
xtc_gauge_read,
xtc_gauge_destroy,
xtc_hist_create,
xtc_hist_record,
xtc_hist_quantile,
xtc_hist_count,
xtc_hist_destroy,
xtc_dist_create,
xtc_dist_record,
xtc_dist_count,
xtc_dist_mean,
xtc_dist_variance,
xtc_dist_stddev,
xtc_dist_destroy,
xtc_metrics_iterate,
xtc_metrics_dump_prometheus,
xtc_tuning_check — runtime
statistics: counters, gauges, histograms
SYNOPSIS
#include <xtc.h>
#include <xtc_stats.h>
int
xtc_counter_create(const
char *name, xtc_counter_t
**out);
void
xtc_counter_inc(xtc_counter_t
*c);
void
xtc_counter_add(xtc_counter_t
*c, int64_t
delta);
uint64_t
xtc_counter_read(const
xtc_counter_t *c);
void
xtc_counter_destroy(xtc_counter_t
*c);
int
xtc_gauge_create(const
char *name, xtc_gauge_t
**out);
void
xtc_gauge_set(xtc_gauge_t
*g, int64_t v);
int64_t
xtc_gauge_read(const
xtc_gauge_t *g);
void
xtc_gauge_destroy(xtc_gauge_t
*g);
int
xtc_hist_create(const
char *name, xtc_hist_t
**out);
void
xtc_hist_record(xtc_hist_t
*h, int64_t
value_ns);
int64_t
xtc_hist_quantile(const
xtc_hist_t *h, double
q);
void
xtc_hist_destroy(xtc_hist_t
*h);
int
xtc_dist_create(const
char *name, xtc_dist_t
**out);
void
xtc_dist_record(xtc_dist_t
*d, double
value);
uint64_t
xtc_dist_count(const
xtc_dist_t *d);
double
xtc_dist_mean(const
xtc_dist_t *d);
double
xtc_dist_variance(const
xtc_dist_t *d);
double
xtc_dist_stddev(const
xtc_dist_t *d);
void
xtc_dist_destroy(xtc_dist_t
*d);
int
xtc_metrics_iterate(xtc_metric_visit_fn
fn, void
*user);
int
xtc_metrics_dump_prometheus(int
fd);
void
xtc_tuning_check(void);
DESCRIPTION
xtc_stats is the runtime telemetry layer.
Three metric kinds:
- counter
- Monotonic non-negative count (requests served, errors observed).
- gauge
- Current value, can move up or down (active connections, queue depth).
- histogram
- Distribution; supports quantile queries (p50, p99, p999). For latency tracking.
- dist
- Online mergeable mean and population variance of a stream of double samples. For a running mean/variance where the full quantile machinery of a histogram is not needed.
- Counters are
per-CPU
sharded:
xtc_counter_inc() hits one cache-line-isolated atomic on the calling CPU. No cross-CPU coherence traffic on the hot path. Reads sum all shards. - Gauges are a single _Atomic int64_t. For low-frequency-update gauges this is fine; high-frequency gauges should be modeled as a counter pair instead.
- Histograms are per-CPU sharded (one log-linear bucket
array per CPU);
xtc_hist_record() hits one shard's atomic counter.xtc_hist_quantile() merges shards under no lock (relaxed atomic loads). - Dists are per-CPU sharded. Each shard holds a Welford
triple (count, mean, M2);
xtc_dist_record() does Welford's single-pass update on the calling CPU's shard. The Welford update is a read-modify-write of three fields, not a single atomic add, and two threads can migrate onto the same shard, so each shard carries a brief per-shard spinlock held only for the handful of arithmetic ops:xtc_dist_record() is therefore not lock-free. It reads no clock and allocates nothing.xtc_dist_mean(),xtc_dist_variance(),xtc_dist_count(), andxtc_dist_stddev() snapshot every shard under its lock and merge the triples with Chan's parallel formula (M2 = M2a + M2b + delta^2 * na*nb/(na+nb)). The reported variance is the population variance (M2 / count); it is 0.0 for a count below 2, andxtc_dist_mean() is 0.0 for an empty dist.xtc_distis consumer-facing telemetry and is not simulation-reachable.
Observability discipline: the implementation is designed not to obscure observability tools. Updating a counter compiles to one CPU-shard sched_getcpu (cached in TLS) plus one atomic_fetch_add; under perf-stat(1) this shows up as the program's CPU pattern, not as overhead in the telemetry layer.
xtc_counter_create(),
xtc_gauge_create(),
xtc_hist_create()
register the metric in a global registry keyed by name
(up to 64 chars). The matching
xtc_counter_destroy(),
xtc_gauge_destroy(),
and
xtc_hist_destroy()
unregister and free a metric. The
xtc_dist_create()
and
xtc_dist_destroy()
pair register and release a dist the same way. The registry supports walking
via
xtc_metrics_iterate()
or dumping in Prometheus exposition format via
xtc_metrics_dump_prometheus()
to a file descriptor.
EXAMPLES
xtc_counter_t *requests;
xtc_hist_t *latency;
xtc_counter_create("rexis.requests", &requests);
xtc_hist_create ("rexis.req_latency_ns", &latency);
/* Hot path: */
int64_t t0 = now_ns();
handle_request();
xtc_counter_inc(requests);
xtc_hist_record(latency, now_ns() - t0);
/* Periodic dump (e.g. via xtc_timer): */
xtc_metrics_dump_prometheus(scrape_fd);
PROMETHEUS OUTPUT
xtc_metrics_dump_prometheus() emits, for
each metric:
# TYPE rexis.requests counter rexis.requests 12345 # TYPE rexis.queue_depth gauge rexis.queue_depth 42 # TYPE rexis.req_latency_ns histogram rexis.req_latency_ns_count 12345 rexis.req_latency_ns_p50 1500 rexis.req_latency_ns_p99 9800 rexis.req_latency_ns_p999 24000 # TYPE rexis.batch_size summary rexis.batch_size_count 12345 rexis.batch_size_mean 37.5 rexis.batch_size_variance 12.25 rexis.batch_size_stddev 3.5
A trivial scrape server reads fd and forwards to HTTP. See examples/05_rexis/metrics.c for an example dump-on-timer pattern.
xtc_tuning_check()
runs libxtc's host-tuning advisor: cheap, read-only probes (CPU governor,
intel_pstate, transparent hugepages, vm swappiness, sched autogroup,
io_uring-under-seccomp) that each log one
XTC_LOG_INFO line to the
xtc_log(3) default sink when the host is not in the
recommended state for a low-latency runtime. It is silent on a well-tuned
host, Linux-only (a no-op elsewhere), and never writes
/proc or /sys. This is the
one diagnostic libxtc itself emits through xtc_log(3):
xtc_app_start(3) calls it by default, but a consumer that
brings carriers up directly via xtc_exec(3) should call
xtc_tuning_check() once at startup, after installing
its
xtc_log_set_default()
sink, to receive the advisories. Otherwise xtc_log(3) is
application-fill -- libxtc streams no other internal log lines -- and
xtc_metrics is likewise a registry the application
populates; libxtc registers no internal metrics (use
xtc_exec(3)'s
xtc_exec_loop_stats()
for per-carrier internals).
SEE ALSO
HISTORY
Appeared in xtc 0.1.
| May 28, 2026 | Debian |