1use std::time::Duration;
9
10use mz_ore::cast::CastFrom;
11use mz_ore::metrics::MetricsRegistry;
12use prometheus::{Gauge, IntGauge};
13
14use crate::MetricsUpdate;
15
16pub(crate) const SOURCE: &str = file!();
17
18macro_rules! metrics {
19 ($namespace:ident $(($name:ident, $desc:expr, $suffix:expr, $type:ident)),*) => {
20 metrics! { @define $namespace $(($name, $desc, $suffix, $type)),*}
21 };
22 (@define $namespace:ident $(($name:ident, $desc:expr, $suffix:expr, $type:ident)),*) => {
23 pub(crate) struct RuMetrics {
24 $($name: <$type as Unit>::Gauge,)*
25 }
26 impl RuMetrics {
27 fn new(registry: &MetricsRegistry) -> Self {
28 Self {
29 $($name: registry.register(mz_ore::metric!(
30 name: concat!(stringify!($namespace), "_", stringify!($name), $suffix),
31 help: $desc,
32 )),)*
33 }
34 }
35 fn update_internal(&self) -> Result<(), std::io::Error> {
36 let rusage = unsafe {
37 let mut rusage = std::mem::zeroed();
38 let ret = libc::getrusage(libc::RUSAGE_SELF, &mut rusage);
39 if ret < 0 {
40 return Err(std::io::Error::last_os_error());
41 }
42 rusage
43 };
44 $(self.$name.set(<$type as Unit>::from(rusage.$name));)*
45 Ok(())
46 }
47 pub(crate) fn descs(&self) -> Vec<(String, String)> {
53 use prometheus::core::Collector;
54 let mut descs = Vec::new();
55 $(for d in self.$name.desc() {
56 descs.push((d.fq_name.clone(), d.help.clone()));
57 })*
58 descs
59 }
60 }
61 };
62}
63
64trait Unit {
66 type Gauge;
68 type From;
70 type To;
72 fn from(value: Self::From) -> Self::To;
74}
75
76struct Timeval;
78impl Unit for Timeval {
79 type Gauge = Gauge;
80 type From = libc::timeval;
81 type To = f64;
82 fn from(Self::From { tv_sec, tv_usec }: Self::From) -> Self::To {
83 (Duration::from_secs(u64::cast_from(tv_sec.abs_diff(0)))
85 + Duration::from_micros(u64::cast_from(tv_usec.abs_diff(0))))
86 .as_secs_f64()
87 }
88}
89
90struct Unitless;
92impl Unit for Unitless {
93 type Gauge = IntGauge;
94 type From = libc::c_long;
95 type To = i64;
96 fn from(value: Self::From) -> Self::To {
97 value
98 }
99}
100
101struct MaxrssToBytes;
107impl Unit for MaxrssToBytes {
108 type Gauge = IntGauge;
109 type From = libc::c_long;
110 type To = i64;
111
112 #[cfg(not(target_os = "macos"))]
113 fn from(value: Self::From) -> Self::To {
114 value.saturating_mul(1024)
115 }
116
117 #[cfg(target_os = "macos")]
118 fn from(value: Self::From) -> Self::To {
119 value
120 }
121}
122
123metrics! {
124 mz_metrics_libc
125 (ru_utime, "user CPU time used", "_seconds_total", Timeval),
126 (ru_stime, "system CPU time used", "_seconds_total", Timeval),
127 (ru_maxrss, "maximum resident set size", "_bytes", MaxrssToBytes),
128 (ru_minflt, "page reclaims (soft page faults)", "_total", Unitless),
129 (ru_majflt, "page faults (hard page faults)", "_total", Unitless),
130 (ru_inblock, "block input operations", "_total", Unitless),
131 (ru_oublock, "block output operations", "_total", Unitless),
132 (ru_nvcsw, "voluntary context switches", "_total", Unitless),
133 (ru_nivcsw, "involuntary context switches", "_total", Unitless)
134}
135
136pub(crate) fn register_metrics_into(metrics_registry: &MetricsRegistry) -> RuMetrics {
138 RuMetrics::new(metrics_registry)
139}
140
141impl MetricsUpdate for RuMetrics {
142 type Error = std::io::Error;
143 const NAME: &'static str = "rusage";
144 fn update(&mut self) -> Result<(), Self::Error> {
145 self.update_internal()
146 }
147}