| 1 | /* @title: Stat series */ |
| 2 | #pragma once |
| 3 | #include <stdatomic.h> |
| 4 | #include <stddef.h> |
| 5 | #include <stdint.h> |
| 6 | #include <string.h> |
| 7 | #include <sync/spinlock.h> |
| 8 | |
| 9 | /* |
| 10 | * Generic time-bucketed statistics series. |
| 11 | * |
| 12 | * Subsystems can embed one or more struct stat_series to track |
| 13 | * short-term rates (e.g., allocations/sec, frees/sec, requests/sec, etc). |
| 14 | * |
| 15 | * Each bucket represents a fixed-duration window |
| 16 | * The current bucket advances as time passes |
| 17 | */ |
| 18 | |
| 19 | struct stat_bucket { |
| 20 | struct stat_series *parent; |
| 21 | atomic_size_t count; /* count for this bucket */ |
| 22 | atomic_size_t sum; /* optional aggregate */ |
| 23 | void *private; /* private, per subsystem */ |
| 24 | }; |
| 25 | typedef size_t (*stat_series_callback)(struct stat_bucket *bucket); |
| 26 | |
| 27 | struct stat_series { |
| 28 | stat_series_callback bucket_reset; /* to call upon bucket reset */ |
| 29 | struct stat_bucket *buckets; /* ringbuffer */ |
| 30 | uint32_t nbuckets; /* how many buckets */ |
| 31 | _Atomic uint32_t current; /* current bucket idx */ |
| 32 | time_t bucket_us; /* duration for each bucket */ |
| 33 | _Atomic uint64_t last_update_us; /* last time we advanced */ |
| 34 | void *private; /* private, per subsystem */ |
| 35 | struct spinlock lock; |
| 36 | }; |
| 37 | |
| 38 | struct stat_series *stat_series_create(uint32_t nbuckets, time_t bucket_us, |
| 39 | stat_series_callback bucket_reset, |
| 40 | void *private); |
| 41 | |
| 42 | void stat_series_init(struct stat_series *s, struct stat_bucket *buckets, |
| 43 | uint32_t nbuckets, time_t bucket_us, |
| 44 | stat_series_callback bucket_reset, void *private); |
| 45 | void stat_series_reset(struct stat_series *s); |
| 46 | void stat_series_record(struct stat_series *s, size_t value, |
| 47 | stat_series_callback callback); |
| 48 | |
| 49 | void stat_series_advance(struct stat_series *s, time_t now_us); |
| 50 | |
| 51 | #define stat_series_for_each(series, iter) \ |
| 52 | for (uint32_t __i = 0; \ |
| 53 | (iter = &((series)->buckets[__i]), __i < (series)->nbuckets); __i++) |
| 54 | |
| 55 | #define STAT_SERIES_DEFINE(name, n, bucket_us) \ |
| 56 | static struct stat_bucket name##_buckets[(n)] = {0}; \ |
| 57 | static struct stat_series name = { \ |
| 58 | .buckets = name##_buckets, \ |
| 59 | .nbuckets = (n), \ |
| 60 | .bucket_us = (bucket_us), \ |
| 61 | }; |
| 62 | |
| 63 | #define STAT_SERIES_CUR_BUCKET(s) (&(s)->buckets[(s)->current]) |
| 64 | |