#[derive(Clone, Copy, Debug)]
pub(super) struct ProcessSnapshot {
minor_faults: u64,
major_faults: u64,
read_bytes: u64,
rss_kib: u64,
peak_rss_kib: u64,
}
#[derive(Clone, Copy, Debug)]
pub(super) struct ProcessDelta {
pub(super) minor_faults: u64,
pub(super) major_faults: u64,
pub(super) read_bytes: u64,
pub(super) rss_kib_start: u64,
pub(super) rss_kib_end: u64,
pub(super) peak_rss_kib: u64,
}
impl ProcessSnapshot {
#[cfg(target_os = "linux")]
pub(super) fn capture() -> Option<Self> {
let stat = std::fs::read_to_string("/proc/self/stat").ok()?;
let io = std::fs::read_to_string("/proc/self/io").ok()?;
let status = std::fs::read_to_string("/proc/self/status").ok()?;
let (minor_faults, major_faults) = parse_stat(&stat)?;
Some(Self {
minor_faults,
major_faults,
read_bytes: parse_named_value(&io, "read_bytes:")?,
rss_kib: parse_named_kib(&status, "VmRSS:")?,
peak_rss_kib: parse_named_kib(&status, "VmHWM:")?,
})
}
#[cfg(not(target_os = "linux"))]
pub(super) const fn capture() -> Option<Self> {
None
}
pub(super) fn delta(start: Option<Self>, end: Option<Self>) -> Option<ProcessDelta> {
let (start, end) = (start?, end?);
Some(ProcessDelta {
minor_faults: end.minor_faults.saturating_sub(start.minor_faults),
major_faults: end.major_faults.saturating_sub(start.major_faults),
read_bytes: end.read_bytes.saturating_sub(start.read_bytes),
rss_kib_start: start.rss_kib,
rss_kib_end: end.rss_kib,
peak_rss_kib: end.peak_rss_kib,
})
}
}
#[cfg(target_os = "linux")]
fn parse_stat(input: &str) -> Option<(u64, u64)> {
let (_, fields) = input.rsplit_once(')')?;
let mut fields = fields.split_whitespace();
let minor_faults = fields.nth(7)?.parse().ok()?;
let major_faults = fields.nth(1)?.parse().ok()?;
Some((minor_faults, major_faults))
}
#[cfg(target_os = "linux")]
fn parse_named_value(input: &str, name: &str) -> Option<u64> {
input.lines().find_map(|line| {
line.strip_prefix(name)?
.split_whitespace()
.next()?
.parse()
.ok()
})
}
#[cfg(target_os = "linux")]
fn parse_named_kib(input: &str, name: &str) -> Option<u64> {
let line = input.lines().find_map(|line| line.strip_prefix(name))?;
let (value, unit) = line.trim().split_once(char::is_whitespace)?;
if unit.trim() != "kB" {
return None;
}
value.parse().ok()
}