use std::mem::MaybeUninit;
use std::{fs, io};
use witchcraft_metrics::MetricRegistry;
pub fn register_metrics(metrics: &MetricRegistry) {
metrics.gauge("process.threads", || num_threads().unwrap_or(0));
metrics.gauge("process.filedescriptor", || filedescriptor().unwrap_or(0.));
}
fn num_threads() -> Option<i64> {
let stat = fs::read_to_string("/proc/self/stat").ok()?;
parse_num_threads(&stat)
}
fn parse_num_threads(stat: &str) -> Option<i64> {
stat.rsplit(')').next()?.split(' ').nth(18)?.parse().ok()
}
fn filedescriptor() -> io::Result<f32> {
let mut files = 0;
for r in fs::read_dir("/proc/self/fd")? {
r?;
files += 1;
}
let max_files = Rlimit::nofile()?.cur();
Ok(files as f32 / max_files as f32)
}
struct Rlimit(libc::rlimit);
impl Rlimit {
pub fn nofile() -> io::Result<Self> {
unsafe {
let mut limit = MaybeUninit::uninit();
if libc::getrlimit(libc::RLIMIT_NOFILE, limit.as_mut_ptr()) != 0 {
return Err(io::Error::last_os_error());
}
Ok(Rlimit(limit.assume_init()))
}
}
#[allow(clippy::unnecessary_cast)]
pub fn cur(&self) -> u64 {
self.0.rlim_cur as u64
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn num_threads() {
let stat = "9 (cat) R 1 9 1 34816 9 4194304 80 0 0 0 0 0 0 0 20 0 1 0 4977616 2138112 113 \
18446744073709551615 187650934964224 187650934994048 281474646050032 0 0 0 0 0 0 0 0 0 \
17 7 0 0 0 0 0 187650935060992 187650935062800 187651580477440 281474646051178 \
281474646051198 281474646051198 281474646052843 0";
assert_eq!(parse_num_threads(stat), Some(1));
}
}