reovim_kernel/debug/
metrics.rs1use std::{
9 collections::HashMap,
10 sync::{
11 OnceLock,
12 atomic::{AtomicU64, Ordering},
13 },
14};
15
16use reovim_arch::sync::RwLock;
17
18pub struct Counter {
35 value: AtomicU64,
36}
37
38impl Counter {
39 #[must_use]
41 pub const fn new() -> Self {
42 Self {
43 value: AtomicU64::new(0),
44 }
45 }
46
47 pub fn increment(&self) {
49 self.value.fetch_add(1, Ordering::Relaxed);
50 }
51
52 pub fn add(&self, n: u64) {
54 self.value.fetch_add(n, Ordering::Relaxed);
55 }
56
57 #[must_use]
59 pub fn get(&self) -> u64 {
60 self.value.load(Ordering::Relaxed)
61 }
62
63 pub fn reset(&self) {
65 self.value.store(0, Ordering::Relaxed);
66 }
67}
68
69impl Default for Counter {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75pub struct Histogram {
94 buckets: [AtomicU64; 16],
96 sum: AtomicU64,
98 count: AtomicU64,
100}
101
102impl Histogram {
103 #[must_use]
105 pub const fn new() -> Self {
106 Self {
107 buckets: [
108 AtomicU64::new(0),
109 AtomicU64::new(0),
110 AtomicU64::new(0),
111 AtomicU64::new(0),
112 AtomicU64::new(0),
113 AtomicU64::new(0),
114 AtomicU64::new(0),
115 AtomicU64::new(0),
116 AtomicU64::new(0),
117 AtomicU64::new(0),
118 AtomicU64::new(0),
119 AtomicU64::new(0),
120 AtomicU64::new(0),
121 AtomicU64::new(0),
122 AtomicU64::new(0),
123 AtomicU64::new(0),
124 ],
125 sum: AtomicU64::new(0),
126 count: AtomicU64::new(0),
127 }
128 }
129
130 pub fn record(&self, value: u64) {
137 let bucket = if value == 0 {
143 0
144 } else {
145 (64 - value.leading_zeros()).min(15) as usize
146 };
147
148 self.buckets[bucket].fetch_add(1, Ordering::Relaxed);
149 self.sum.fetch_add(value, Ordering::Relaxed);
150 self.count.fetch_add(1, Ordering::Relaxed);
151 }
152
153 #[must_use]
155 #[allow(clippy::cast_precision_loss)] pub fn mean(&self) -> f64 {
157 let count = self.count.load(Ordering::Relaxed);
158 if count == 0 {
159 0.0
160 } else {
161 self.sum.load(Ordering::Relaxed) as f64 / count as f64
162 }
163 }
164
165 #[must_use]
167 pub fn count(&self) -> u64 {
168 self.count.load(Ordering::Relaxed)
169 }
170
171 #[must_use]
173 pub fn sum(&self) -> u64 {
174 self.sum.load(Ordering::Relaxed)
175 }
176
177 #[must_use]
179 pub fn buckets(&self) -> [u64; 16] {
180 let mut result = [0u64; 16];
181 for (i, bucket) in self.buckets.iter().enumerate() {
182 result[i] = bucket.load(Ordering::Relaxed);
183 }
184 result
185 }
186
187 pub fn reset(&self) {
189 for bucket in &self.buckets {
190 bucket.store(0, Ordering::Relaxed);
191 }
192 self.sum.store(0, Ordering::Relaxed);
193 self.count.store(0, Ordering::Relaxed);
194 }
195}
196
197impl Default for Histogram {
198 fn default() -> Self {
199 Self::new()
200 }
201}
202
203pub struct MetricsRegistry {
208 counters: RwLock<HashMap<&'static str, std::sync::Arc<Counter>>>,
209 histograms: RwLock<HashMap<&'static str, std::sync::Arc<Histogram>>>,
210}
211
212impl MetricsRegistry {
213 #[must_use]
215 pub fn new() -> Self {
216 Self {
217 counters: RwLock::new(HashMap::new()),
218 histograms: RwLock::new(HashMap::new()),
219 }
220 }
221
222 #[must_use]
226 pub fn counter(&self, name: &'static str) -> std::sync::Arc<Counter> {
227 {
229 let counters = self.counters.read();
230 if let Some(counter) = counters.get(name) {
231 return counter.clone();
232 }
233 }
234
235 let mut counters = self.counters.write();
237 counters
238 .entry(name)
239 .or_insert_with(|| std::sync::Arc::new(Counter::new()))
240 .clone()
241 }
242
243 #[must_use]
245 pub fn histogram(&self, name: &'static str) -> std::sync::Arc<Histogram> {
246 {
247 let histograms = self.histograms.read();
248 if let Some(histogram) = histograms.get(name) {
249 return histogram.clone();
250 }
251 }
252
253 let mut histograms = self.histograms.write();
254 histograms
255 .entry(name)
256 .or_insert_with(|| std::sync::Arc::new(Histogram::new()))
257 .clone()
258 }
259
260 #[must_use]
262 pub fn snapshot(&self) -> MetricsSnapshot {
263 let counters = self.counters.read();
264 let histograms = self.histograms.read();
265
266 MetricsSnapshot {
267 counters: counters.iter().map(|(k, v)| (*k, v.get())).collect(),
268 histograms: histograms
269 .iter()
270 .map(|(k, v)| (*k, (v.count(), v.mean())))
271 .collect(),
272 }
273 }
274}
275
276impl Default for MetricsRegistry {
277 fn default() -> Self {
278 Self::new()
279 }
280}
281
282#[derive(Debug)]
284pub struct MetricsSnapshot {
285 pub counters: HashMap<&'static str, u64>,
287 pub histograms: HashMap<&'static str, (u64, f64)>,
289}
290
291static METRICS: OnceLock<MetricsRegistry> = OnceLock::new();
293
294#[must_use]
296pub fn metrics() -> &'static MetricsRegistry {
297 METRICS.get_or_init(MetricsRegistry::new)
298}