1use std::collections::BTreeMap;
9use std::ops::AddAssign;
10
11use lgalloc::{FileStats, MapStats};
12use mz_ore::cast::CastFrom;
13use mz_ore::metrics::{MetricsRegistry, raw};
14use paste::paste;
15use prometheus::core::{AtomicU64, GenericGauge};
16
17use crate::MetricsUpdate;
18
19#[derive(Debug, thiserror::Error)]
21#[error(transparent)]
22pub struct Error {
23 #[from]
25 pub kind: ErrorKind,
26}
27
28#[derive(Debug, thiserror::Error)]
30pub enum ErrorKind {
31 #[error("Failed to get file stats: {0}")]
33 FileStatsFailed(String),
34}
35
36impl Error {
37 pub fn new(kind: ErrorKind) -> Error {
39 Error { kind }
40 }
41}
42
43#[derive(Default)]
45struct FileStatsAccum {
46 pub file_size: usize,
48 pub allocated_size: usize,
50}
51
52#[derive(Default)]
54struct MapStatsAccum {
55 pub mapped: usize,
57 pub active: usize,
59 pub dirty: usize,
61}
62
63impl AddAssign<&FileStats> for FileStatsAccum {
64 fn add_assign(&mut self, rhs: &FileStats) {
65 self.file_size += rhs.file_size;
66 self.allocated_size += rhs.allocated_size;
67 }
68}
69
70impl AddAssign<&MapStats> for MapStatsAccum {
71 fn add_assign(&mut self, rhs: &MapStats) {
72 self.mapped += rhs.mapped;
73 self.active += rhs.active;
74 self.dirty += rhs.dirty;
75 }
76}
77
78macro_rules! metrics_size_class {
79 ($namespace:ident
80 @size_class ($(($name:ident, $desc:expr, $metric:expr, $conv:expr)),*)
81 @file ($(($f_name:ident, $f_metric:ident, $f_desc:expr)),*)
82 ) => {
83 metrics_size_class! {
84 @define $namespace
85 @size_class $(($name, $desc, $metric, $conv)),*
86 @file $(($f_name, $f_metric, $f_desc)),*
87 }
88 };
89 (@define $namespace:ident
90 @size_class $(($name:ident, $desc:expr, $metric:expr, $conv:expr)),*
91 @file $(($f_name:ident, $f_metric:ident, $f_desc:expr)),*
92 ) => {
93 paste! {
94 pub(crate) struct LgMetrics {
95 size_class: BTreeMap<usize, LgMetricsSC>,
96 $($metric: raw::UIntGaugeVec,)*
97 $($f_metric: raw::UIntGaugeVec,)*
98 }
99 struct LgMetricsSC {
100 $($metric: GenericGauge<AtomicU64>,)*
101 $($f_metric: GenericGauge<AtomicU64>,)*
102 }
103 impl LgMetrics {
104 fn new(registry: &MetricsRegistry) -> Self {
105 Self {
106 size_class: BTreeMap::default(),
107 $($metric: registry.register(mz_ore::metric!(
108 name: concat!(stringify!($namespace), "_", stringify!($metric)),
109 help: $desc,
110 var_labels: ["size_class"],
111 )),)*
112 $($f_metric: registry.register(mz_ore::metric!(
113 name: concat!(stringify!($namespace), "_", stringify!($f_metric)),
114 help: $f_desc,
115 var_labels: ["size_class"],
116 )),)*
117 }
118 }
119 fn get_size_class(&mut self, size_class: usize) -> &LgMetricsSC {
120 self.size_class.entry(size_class).or_insert_with(|| {
121 let labels: &[&str] = &[&size_class.to_string()];
122 LgMetricsSC {
123 $($metric: self.$metric.with_label_values(labels),)*
124 $($f_metric: self.$f_metric.with_label_values(labels),)*
125 }
126 })
127 }
128 fn update(&mut self) -> Result<(), Error> {
129 let stats = lgalloc::lgalloc_stats();
130 for (size_class, sc) in &stats.size_class {
131 let sc_stats = self.get_size_class(*size_class);
132 $(sc_stats.$metric.set(($conv)(u64::cast_from(sc.$name), *size_class));)*
133 }
134 let mut f_accums = BTreeMap::new();
135 for (size_class, file_stat) in &stats.file {
136 let accum: &mut FileStatsAccum = f_accums.entry(*size_class).or_default();
137 match file_stat {
138 Ok(file_stat) => {
139 accum.add_assign(file_stat);
140 }
141 Err(err) => {
142 return Err(ErrorKind::FileStatsFailed(err.to_string()).into());
143 }
144 }
145 }
146 for (size_class, f_accum) in f_accums {
147 let sc_stats = self.get_size_class(size_class);
148 $(sc_stats.$f_metric.set(u64::cast_from(f_accum.$f_name));)*
149 }
150 Ok(())
151 }
152 }
153 }
154 };
155}
156
157macro_rules! map_metrics {
158 ($namespace:ident
159 @mem ($(($m_name:ident, $m_metric:ident, $m_desc:expr)),*)
160 ) => {
161 map_metrics! {
162 @define $namespace
163 @mem $(($m_name, $m_metric, $m_desc)),*
164 }
165 };
166 (@define $namespace:ident
167 @mem $(($m_name:ident, $m_metric:ident, $m_desc:expr)),*
168 ) => {
169 paste! {
170 pub(crate) struct LgMapMetrics {
171 size_class: BTreeMap<usize, LgMapMetricsSC>,
172 $($m_metric: raw::UIntGaugeVec,)*
173 }
174 struct LgMapMetricsSC {
175 $($m_metric: GenericGauge<AtomicU64>,)*
176 }
177 impl LgMapMetrics {
178 fn new(registry: &MetricsRegistry) -> Self {
179 Self {
180 size_class: BTreeMap::default(),
181 $($m_metric: registry.register(mz_ore::metric!(
182 name: concat!(stringify!($namespace), "_", stringify!($m_metric)),
183 help: $m_desc,
184 var_labels: ["size_class"],
185 )),)*
186 }
187 }
188 fn get_size_class(&mut self, size_class: usize) -> &LgMapMetricsSC {
189 self.size_class.entry(size_class).or_insert_with(|| {
190 let labels: &[&str] = &[&size_class.to_string()];
191 LgMapMetricsSC {
192 $($m_metric: self.$m_metric.with_label_values(labels),)*
193 }
194 })
195 }
196 fn update(&mut self) -> std::io::Result<()> {
197 #[cfg(target_os = "linux")]
198 let stats = lgalloc::lgalloc_stats_with_mapping()?;
199 #[cfg(not(target_os = "linux"))]
203 let stats = lgalloc::lgalloc_stats();
204 let mut m_accums = BTreeMap::new();
205 for (size_class, map_stat) in stats.map.iter().flatten() {
206 let accum: &mut MapStatsAccum = m_accums.entry(*size_class).or_default();
207 accum.add_assign(map_stat);
208 }
209 for (size_class, m_accum) in m_accums {
210 let sc_stats = self.get_size_class(size_class);
211 $(sc_stats.$m_metric.set(u64::cast_from(m_accum.$m_name));)*
212 }
213 Ok(())
214 }
215 }
216 }
217 };
218}
219
220fn normalize_by_size_class(value: u64, size_class: usize) -> u64 {
221 value * u64::cast_from(size_class)
222}
223
224fn id(value: u64, _size_class: usize) -> u64 {
225 value
226}
227
228metrics_size_class! {
229 mz_metrics_lgalloc
230 @size_class (
231 (allocations, "Number of region allocations in size class", allocations_total, id),
232 (area_total_bytes, "Number of bytes in all areas in size class", area_total_bytes, id),
233 (areas, "Number of areas backing size class", areas_total, id),
234 (clean_regions, "Number of clean regions in size class", clean_regions_total, id),
235 (clean_regions, "Number of clean regions in size class", clean_regions_bytes_total, normalize_by_size_class),
236 (deallocations, "Number of region deallocations for size class", deallocations_total, id),
237 (free_regions, "Number of free regions in size class", free_regions_total, id),
238 (free_regions, "Number of free regions in size class", free_regions_bytes_total, normalize_by_size_class),
239 (global_regions, "Number of global regions in size class", global_regions_total, id),
240 (global_regions, "Number of global regions in size class", global_regions_bytes_total, normalize_by_size_class),
241 (refill, "Number of area refills for size class", refill_total, id),
242 (slow_path, "Number of slow path region allocations for size class", slow_path_total, id),
243 (thread_regions, "Number of thread regions in size class", thread_regions_total, id),
244 (thread_regions, "Number of thread regions in size class", thread_regions_bytes_total, normalize_by_size_class),
245 (clear_eager_total, "Number of thread regions in size class", clear_eager_total, id),
246 (clear_eager_total, "Number of thread regions in size class", clear_eager_bytes_total, normalize_by_size_class),
247 (clear_slow_total, "Number of thread regions in size class", clear_slow_total, id),
248 (clear_slow_total, "Number of thread regions in size class", clear_slow_bytes_total, normalize_by_size_class)
249 )
250 @file (
251 (file_size, file_size_bytes, "Sum of file sizes in size class"),
252 (allocated_size, file_allocated_size_bytes, "Sum of allocated sizes in size class")
253 )
254}
255
256map_metrics! {
257 mz_metrics_lgalloc
258 @mem (
259 (mapped, vm_mapped_bytes, "Sum of mapped sizes in size class"),
260 (active, vm_active_bytes, "Sum of active sizes in size class"),
261 (dirty, vm_dirty_bytes, "Sum of dirty sizes in size class")
262 )
263}
264
265pub(crate) fn register_metrics_into(metrics_registry: &MetricsRegistry) -> LgMetrics {
267 LgMetrics::new(metrics_registry)
268}
269
270pub(crate) fn register_map_metrics_into(metrics_registry: &MetricsRegistry) -> LgMapMetrics {
272 LgMapMetrics::new(metrics_registry)
273}
274
275impl MetricsUpdate for LgMetrics {
276 type Error = Error;
277 const NAME: &'static str = "lgalloc";
278 fn update(&mut self) -> Result<(), Self::Error> {
279 self.update()
280 }
281}
282
283impl MetricsUpdate for LgMapMetrics {
284 type Error = std::io::Error;
285 const NAME: &'static str = "lgalloc_map";
286 fn update(&mut self) -> Result<(), Self::Error> {
287 self.update()
288 }
289}