1use std::collections::BTreeMap;
9use std::ops::AddAssign;
10
11use lgalloc::{FileStats, SizeClassStats};
12use mz_ore::cast::CastFrom;
13use mz_ore::metrics::{MetricsRegistry, raw};
14use paste::paste;
15use prometheus::core::{AtomicU64, GenericGauge};
16use tracing::error;
17
18use crate::MetricsUpdate;
19
20#[derive(Debug, thiserror::Error)]
22#[error(transparent)]
23pub struct Error {
24 #[from]
26 pub kind: ErrorKind,
27}
28
29#[derive(Debug, thiserror::Error)]
31pub enum ErrorKind {
32 #[error("Failed to get file stats: {0}")]
34 FileStatsFailed(String),
35}
36
37impl Error {
38 pub fn new(kind: ErrorKind) -> Error {
40 Error { kind }
41 }
42}
43
44#[derive(Default)]
46struct FileStatsAccum {
47 pub file_size: usize,
49 pub allocated_size: usize,
51 pub mapped: usize,
53 pub active: usize,
55 pub dirty: usize,
57}
58
59impl AddAssign<&FileStats> for FileStatsAccum {
60 fn add_assign(&mut self, rhs: &FileStats) {
61 self.file_size += rhs.file_size;
62 self.allocated_size += rhs.allocated_size;
63 self.mapped += rhs.mapped;
64 self.active += rhs.active;
65 self.dirty += rhs.dirty;
66 }
67}
68
69macro_rules! metrics_size_class {
70 ($namespace:ident
71 @size_class ($(($name:ident, $desc:expr, $metric:expr, $conv:expr)),*)
72 @file ($(($f_name:ident, $f_metric:ident, $f_desc:expr)),*)
73 ) => {
74 metrics_size_class! {
75 @define $namespace
76 @size_class $(($name, $desc, $metric, $conv)),*
77 @file $(($f_name, $f_metric, $f_desc)),*
78 }
79 };
80 (@define $namespace:ident
81 @size_class $(($name:ident, $desc:expr, $metric:expr, $conv:expr)),*
82 @file $(($f_name:ident, $f_metric:ident, $f_desc:expr)),*
83 ) => {
84 paste! {
85 pub(crate) struct LgMetrics {
86 size_class: BTreeMap<usize, LgMetricsSC>,
87 $($metric: raw::UIntGaugeVec,)*
88 $($f_metric: raw::UIntGaugeVec,)*
89 }
90 struct LgMetricsSC {
91 $($metric: GenericGauge<AtomicU64>,)*
92 $($f_metric: GenericGauge<AtomicU64>,)*
93 }
94 impl LgMetrics {
95 fn new(registry: &MetricsRegistry) -> Self {
96 Self {
97 size_class: BTreeMap::default(),
98 $($metric: registry.register(mz_ore::metric!(
99 name: concat!(stringify!($namespace), "_", stringify!($metric)),
100 help: $desc,
101 var_labels: ["size_class"],
102 )),)*
103 $($f_metric: registry.register(mz_ore::metric!(
104 name: concat!(stringify!($namespace), "_", stringify!($f_metric)),
105 help: $f_desc,
106 var_labels: ["size_class"],
107 )),)*
108 }
109 }
110 fn get_size_class(&mut self, size_class: usize) -> &LgMetricsSC {
111 self.size_class.entry(size_class).or_insert_with(|| {
112 let labels: &[&str] = &[&size_class.to_string()];
113 LgMetricsSC {
114 $($metric: self.$metric.with_label_values(labels),)*
115 $($f_metric: self.$f_metric.with_label_values(labels),)*
116 }
117 })
118 }
119 fn update(&mut self) -> Result<(), Error> {
120 let stats = lgalloc::lgalloc_stats();
121 for sc in &stats.size_class {
122 let sc_stats = self.get_size_class(sc.size_class);
123 $(sc_stats.$metric.set(($conv)(u64::cast_from(sc.$name), sc));)*
124 }
125 let mut accums = BTreeMap::new();
126 match &stats.file_stats {
127 Ok(file_stats) => {
128 for file_stat in file_stats {
129 let accum: &mut FileStatsAccum = accums.entry(file_stat.size_class).or_default();
130 accum.add_assign(file_stat);
131 }
132 }
133 #[cfg(target_os = "linux")]
134 Err(err) => {
135 return Err(Error::new(ErrorKind::FileStatsFailed(err.to_string())));
136 }
137 #[cfg(not(target_os = "linux"))]
138 Err(err) => {
139 if err.kind() != std::io::ErrorKind::NotFound {
140 return Err(Error::new(ErrorKind::FileStatsFailed(err.to_string())));
141 }
142 }
143 }
144 for (size_class, accum) in accums {
145 let sc_stats = self.get_size_class(size_class);
146 $(sc_stats.$f_metric.set(u64::cast_from(accum.$f_name));)*
147 }
148 Ok(())
149 }
150 }
151 }
152 };
153}
154
155fn normalize_by_size_class(value: u64, stats: &SizeClassStats) -> u64 {
156 value * u64::cast_from(stats.size_class)
157}
158
159fn id(value: u64, _stats: &SizeClassStats) -> u64 {
160 value
161}
162
163metrics_size_class! {
164 mz_metrics_lgalloc
165 @size_class (
166 (allocations, "Number of region allocations in size class", allocations_total, id),
167 (area_total_bytes, "Number of bytes in all areas in size class", area_total_bytes, id),
168 (areas, "Number of areas backing size class", areas_total, id),
169 (clean_regions, "Number of clean regions in size class", clean_regions_total, id),
170 (clean_regions, "Number of clean regions in size class", clean_regions_bytes_total, normalize_by_size_class),
171 (deallocations, "Number of region deallocations for size class", deallocations_total, id),
172 (free_regions, "Number of free regions in size class", free_regions_total, id),
173 (free_regions, "Number of free regions in size class", free_regions_bytes_total, normalize_by_size_class),
174 (global_regions, "Number of global regions in size class", global_regions_total, id),
175 (global_regions, "Number of global regions in size class", global_regions_bytes_total, normalize_by_size_class),
176 (refill, "Number of area refills for size class", refill_total, id),
177 (slow_path, "Number of slow path region allocations for size class", slow_path_total, id),
178 (thread_regions, "Number of thread regions in size class", thread_regions_total, id),
179 (thread_regions, "Number of thread regions in size class", thread_regions_bytes_total, normalize_by_size_class),
180 (clear_eager_total, "Number of thread regions in size class", clear_eager_total, id),
181 (clear_eager_total, "Number of thread regions in size class", clear_eager_bytes_total, normalize_by_size_class),
182 (clear_slow_total, "Number of thread regions in size class", clear_slow_total, id),
183 (clear_slow_total, "Number of thread regions in size class", clear_slow_bytes_total, normalize_by_size_class)
184 )
185 @file (
186 (file_size, file_size_bytes, "Sum of file sizes in size class"),
187 (allocated_size, file_allocated_size_bytes, "Sum of allocated sizes in size class"),
188 (mapped, vm_mapped_bytes, "Sum of mapped sizes in size class"),
189 (active, vm_active_bytes, "Sum of active sizes in size class"),
190 (dirty, vm_dirty_bytes, "Sum of dirty sizes in size class")
191 )
192}
193
194pub(crate) fn register_metrics_into(metrics_registry: &MetricsRegistry) -> LgMetrics {
196 LgMetrics::new(metrics_registry)
197}
198
199impl MetricsUpdate for LgMetrics {
200 type Error = Error;
201 const NAME: &'static str = "lgalloc";
202 fn update(&mut self) -> Result<(), Self::Error> {
203 self.update()
204 }
205}