xtc_stats(3)

---

xtc_stats(3)

runtime statistics: counters, gauges, histograms

XTC_STATS(3) Library Functions Manual XTC_STATS(3)

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

#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);

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 : () 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); () hits one shard's atomic counter. () merges shards under no lock (relaxed atomic loads).
  • Dists are per-CPU sharded. Each shard holds a Welford triple (count, mean, M2); () 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. (), (), (), and () 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 variance (M2 / count); it is 0.0 for a count below 2, and xtc_dist_mean() is 0.0 for an empty dist. xtc_dist is consumer-facing telemetry and is not simulation-reachable.

: 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.

(), (), () register the metric in a global registry keyed by name (up to 64 chars). The matching (), (), and () unregister and free a metric. The () and () pair register and release a dist the same way. The registry supports walking via () or dumping in Prometheus exposition format via () to a file descriptor.

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);

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.

() 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 () 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 () for per-carrier internals).

xtc_log(3), xtc_cfg(3), xtc(7), perf-stat(1)

Appeared in xtc 0.1.

May 28, 2026 Debian

View the mdoc source