use std::time::Duration;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub struct ChildUsage {
pub user_cpu: Duration,
pub system_cpu: Duration,
pub max_rss_bytes: Option<u64>,
}
impl ChildUsage {
pub fn cpu(&self) -> Duration {
self.user_cpu.saturating_add(self.system_cpu)
}
#[cfg(unix)]
pub(crate) fn from_rusage(usage: &libc::rusage) -> Self {
let time = |t: libc::timeval| {
Duration::new(
u64::try_from(t.tv_sec).unwrap_or(0),
(u32::try_from(t.tv_usec).unwrap_or(0).min(999_999)) * 1_000,
)
};
let raw = u64::try_from(usage.ru_maxrss).unwrap_or(0);
let max_rss = if cfg!(any(target_os = "macos", target_os = "ios")) {
raw
} else {
raw.saturating_mul(1024)
};
Self {
user_cpu: time(usage.ru_utime),
system_cpu: time(usage.ru_stime),
max_rss_bytes: (max_rss > 0).then_some(max_rss),
}
}
#[cfg(windows)]
pub(crate) fn from_exited_process(handle: &std::os::windows::io::OwnedHandle) -> Option<Self> {
use std::os::windows::io::AsRawHandle;
use windows::Win32::{
Foundation::{FILETIME, HANDLE},
System::{
ProcessStatus::{GetProcessMemoryInfo, PROCESS_MEMORY_COUNTERS},
Threading::GetProcessTimes,
},
};
let process = HANDLE(handle.as_raw_handle() as _);
let (mut created, mut exited, mut kernel, mut user) = (
FILETIME::default(),
FILETIME::default(),
FILETIME::default(),
FILETIME::default(),
);
unsafe { GetProcessTimes(process, &mut created, &mut exited, &mut kernel, &mut user) }
.ok()?;
let time = |t: FILETIME| {
let ticks = (u64::from(t.dwHighDateTime) << 32) | u64::from(t.dwLowDateTime);
Duration::new(ticks / 10_000_000, ((ticks % 10_000_000) * 100) as u32)
};
let mut counters = PROCESS_MEMORY_COUNTERS {
cb: std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
..Default::default()
};
let peak = unsafe {
GetProcessMemoryInfo(
process,
&mut counters,
std::mem::size_of::<PROCESS_MEMORY_COUNTERS>() as u32,
)
}
.ok()
.map(|()| counters.PeakWorkingSetSize as u64);
Some(Self {
user_cpu: time(user),
system_cpu: time(kernel),
max_rss_bytes: peak.filter(|bytes| *bytes > 0),
})
}
}