libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_stats.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * @stability stable
4 * Use of this source code is governed by the ISC License,
5 * a copy of which is in the file LICENSE in the top-level directory
6 * of this distribution.
7 *
8 * src/inc/xtc_stats.h
9 * Runtime statistics primitives: counters, gauges, histograms.
10 * Designed to be cheap to update on the hot path (single atomic
11 * per-CPU shard for counters; one _Atomic for gauges) and
12 * non-obscuring under perf inspection.
13 *
14 * A counter accumulates events. Per-CPU sharded so that
15 * xtc_counter_inc compiles to one cache-line-local atomic add.
16 * Cross-CPU summing happens only on read, which is the slow path.
17 *
18 * A gauge holds a current value (depth, queue length, etc.).
19 * Single _Atomic int64; reads are wait-free, writes are atomic
20 * store. For low-frequency-update gauges this is fine.
21 *
22 * A histogram tracks a value distribution and answers quantile
23 * queries (p50/p99/p999) and the exact max. Log-linear buckets
24 * from 1 ns to 2^40 ns (~18 min), 16 per octave; a quantile is the
25 * upper bound of its bucket (HdrHistogram's convention), so it is
26 * never below the true value and at most 1/16 above it. Per-CPU
27 * shards so concurrent record() calls don't contend; quantile
28 * queries merge the shards with relaxed loads, no lock.
29 *
30 * A dist is a mergeable online mean/variance aggregator.
31 * Welford's single-pass update runs on the current CPU's shard;
32 * reads merge the per-CPU shards with Chan's parallel formula.
33 * Tracks mean and population variance only (no skew/kurtosis).
34 *
35 * A registry walks every metric for periodic dumps to logs or
36 * a Prometheus-style scrape endpoint.
37 */
38
39#ifndef XTC_STATS_H
40#define XTC_STATS_H
41
42#include "xtc_export.h"
43
44#include <stddef.h>
45#include <stdint.h>
46
47#include "xtc.h"
48
49typedef struct xtc_counter xtc_counter_t;
50typedef struct xtc_gauge xtc_gauge_t;
51typedef struct xtc_hist xtc_hist_t;
52typedef struct xtc_dist xtc_dist_t;
53
54typedef enum xtc_metric_kind {
55 XTC_METRIC_COUNTER = 0,
56 XTC_METRIC_GAUGE = 1,
57 XTC_METRIC_HIST = 2,
58 XTC_METRIC_DIST = 3
59} xtc_metric_kind_t;
60
61typedef int (*xtc_metric_visit_fn)(const char *name,
62 xtc_metric_kind_t kind,
63 const void *handle,
64 void *user);
65
66/*
67 * PUBLIC: int xtc_counter_create __P((const char *, xtc_counter_t **));
68 * PUBLIC: void xtc_counter_destroy __P((xtc_counter_t *));
69 * PUBLIC: void xtc_counter_inc __P((xtc_counter_t *));
70 * PUBLIC: void xtc_counter_add __P((xtc_counter_t *, int64_t));
71 * PUBLIC: uint64_t xtc_counter_read __P((const xtc_counter_t *));
72 *
73 * PUBLIC: int xtc_gauge_create __P((const char *, xtc_gauge_t **));
74 * PUBLIC: void xtc_gauge_destroy __P((xtc_gauge_t *));
75 * PUBLIC: void xtc_gauge_set __P((xtc_gauge_t *, int64_t));
76 * PUBLIC: void xtc_gauge_add __P((xtc_gauge_t *, int64_t));
77 * PUBLIC: int64_t xtc_gauge_read __P((const xtc_gauge_t *));
78 *
79 * PUBLIC: int xtc_hist_create __P((const char *, xtc_hist_t **));
80 * PUBLIC: void xtc_hist_destroy __P((xtc_hist_t *));
81 * PUBLIC: void xtc_hist_record __P((xtc_hist_t *, int64_t));
82 * PUBLIC: int64_t xtc_hist_quantile __P((const xtc_hist_t *, double));
83 * PUBLIC: uint64_t xtc_hist_count __P((const xtc_hist_t *));
84 * PUBLIC: int64_t xtc_hist_max __P((const xtc_hist_t *));
85 *
86 * PUBLIC: int xtc_dist_create __P((const char *, xtc_dist_t **));
87 * PUBLIC: void xtc_dist_destroy __P((xtc_dist_t *));
88 * PUBLIC: void xtc_dist_record __P((xtc_dist_t *, double));
89 * PUBLIC: uint64_t xtc_dist_count __P((const xtc_dist_t *));
90 * PUBLIC: double xtc_dist_mean __P((const xtc_dist_t *));
91 * PUBLIC: double xtc_dist_variance __P((const xtc_dist_t *));
92 * PUBLIC: double xtc_dist_stddev __P((const xtc_dist_t *));
93 *
94 * PUBLIC: int xtc_metrics_iterate __P((xtc_metric_visit_fn, void *));
95 * PUBLIC: int xtc_metrics_dump_prometheus __P((int));
96 *
97 * PUBLIC: void xtc_tuning_check __P((void));
98 */
99
100XTC_API int xtc_counter_create(const char *name, xtc_counter_t **out);
101XTC_API void xtc_counter_destroy(xtc_counter_t *c);
102XTC_API void xtc_counter_inc(xtc_counter_t *c);
103XTC_API void xtc_counter_add(xtc_counter_t *c, int64_t delta);
104XTC_API uint64_t xtc_counter_read(const xtc_counter_t *c);
105
106XTC_API int xtc_gauge_create(const char *name, xtc_gauge_t **out);
107XTC_API void xtc_gauge_destroy(xtc_gauge_t *g);
108XTC_API void xtc_gauge_set(xtc_gauge_t *g, int64_t v);
109XTC_API void xtc_gauge_add(xtc_gauge_t *g, int64_t delta);
110XTC_API int64_t xtc_gauge_read(const xtc_gauge_t *g);
111
112XTC_API int xtc_hist_create(const char *name, xtc_hist_t **out);
113XTC_API void xtc_hist_destroy(xtc_hist_t *h);
114XTC_API void xtc_hist_record(xtc_hist_t *h, int64_t value_ns);
115XTC_API int64_t xtc_hist_quantile(const xtc_hist_t *h, double q);
116XTC_API uint64_t xtc_hist_count(const xtc_hist_t *h);
117XTC_API int64_t xtc_hist_max(const xtc_hist_t *h);
118
119XTC_API int xtc_dist_create(const char *name, xtc_dist_t **out);
120XTC_API void xtc_dist_destroy(xtc_dist_t *d);
121XTC_API void xtc_dist_record(xtc_dist_t *d, double value);
122XTC_API uint64_t xtc_dist_count(const xtc_dist_t *d);
123XTC_API double xtc_dist_mean(const xtc_dist_t *d);
124XTC_API double xtc_dist_variance(const xtc_dist_t *d);
125XTC_API double xtc_dist_stddev(const xtc_dist_t *d);
126
127XTC_API int xtc_metrics_iterate(xtc_metric_visit_fn fn, void *user);
128XTC_API int xtc_metrics_dump_prometheus(int fd);
129
130/*
131 * Run libxtc's host-tuning advisor: a battery of cheap, read-only
132 * probes (CPU governor, intel_pstate, transparent hugepages, vm
133 * swappiness, sched autogroup, io_uring-under-seccomp) that each log
134 * one XTC_LOG_INFO line to the xtc_log_default() sink when the host is
135 * NOT in the recommended state for a low-latency runtime. Silent on a
136 * well-tuned host; Linux-only (a no-op elsewhere); never writes /proc
137 * or /sys.
138 *
139 * This is the ONE diagnostic libxtc itself emits through xtc_log, and
140 * it is the intended way to get those lines into an application's log
141 * sink on the xtc_exec / xtc_loop bring-up path (xtc_app_start already
142 * calls it by default; a consumer that stands up carriers via
143 * xtc_exec_init should call xtc_tuning_check() once at startup, after
144 * installing its xtc_log_set_default sink, to receive the advisories).
145 * Otherwise xtc_log is application-fill: libxtc streams no other
146 * internal log lines today. Safe to call more than once.
147 */
148XTC_API void xtc_tuning_check(void);
149
150#endif /* XTC_STATS_H */