Use wait4 instead of getrusage

This commit is contained in:
David Peter authored and David Peter committed 2026-10-05 15:56:37 +02:00
1 parent 50232feb22
commit ae0035ad56
9 files changed
+143 -98

No files matched your search

+32 -41
View File
@@ -1,5 +1,6 @@
#![cfg(not(windows))]
use std::convert::TryFrom;
use std::io;
use std::mem::MaybeUninit;
use std::os::unix::process::ExitStatusExt;
@@ -7,26 +8,24 @@ use std::process::{Child, ExitStatus};
use anyhow::Result;
use crate::quantity::{byte, kibibyte, microsecond, second, Information, Time};
use crate::util::units::Second;
#[derive(Debug, Copy, Clone)]
struct ResourceUsage {
/// Total amount of time spent executing in user mode
pub time_user: Time,
pub user_usec: Second,
/// Total amount of time spent executing in kernel mode
pub time_system: Time,
pub system_usec: Second,
/// Maximum amount of memory used by the process, in bytes
pub memory_usage: Information,
pub memory_usage_byte: i64,
}
#[allow(clippy::useless_conversion)]
fn convert_timeval(tv: libc::timeval) -> Time {
let sec = tv.tv_sec as f64;
let usec = tv.tv_usec as f64;
Time::new::<second>(sec) + Time::new::<microsecond>(usec)
fn timeval_to_seconds(tv: libc::timeval) -> Second {
const MICROSEC_PER_SEC: i64 = 1000 * 1000;
(i64::from(tv.tv_sec) * MICROSEC_PER_SEC + i64::from(tv.tv_usec)) as f64 * 1e-6
}
#[allow(clippy::useless_conversion)]
@@ -37,37 +36,29 @@ fn wait4(mut child: Child) -> io::Result<(ExitStatus, ResourceUsage)> {
let mut status = 0;
let mut rusage = MaybeUninit::zeroed();
loop {
let result = unsafe { libc::wait4(pid, &mut status, 0, rusage.as_mut_ptr()) };
if result >= 0 {
break;
}
let error = io::Error::last_os_error();
if error.kind() != io::ErrorKind::Interrupted {
return Err(error);
}
}
let result = unsafe { libc::wait4(pid, &mut status, 0, rusage.as_mut_ptr()) };
let rusage = unsafe { rusage.assume_init() };
let memory_usage_byte = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
// Linux and *BSD return the value in KibiBytes, Darwin flavors in bytes
// A f64 can represent integers up to 2^53 exactly, so we can represent
// all values up to 2^53 bytes = 8 PiB exactly. Beyond that, our precision
// is larger than 1 byte, but that's considered acceptable.
Information::new::<byte>(rusage.ru_maxrss as f64)
if result < 0 {
Err(io::Error::last_os_error())
} else {
Information::new::<kibibyte>(rusage.ru_maxrss as f64)
};
let rusage = unsafe { rusage.assume_init() };
Ok((
ExitStatus::from_raw(status),
ResourceUsage {
time_user: convert_timeval(rusage.ru_utime),
time_system: convert_timeval(rusage.ru_stime),
memory_usage: memory_usage_byte.into(),
},
))
let memory_usage_byte = if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
// Linux and *BSD return the value in KibiBytes, Darwin flavors in bytes
rusage.ru_maxrss
} else {
rusage.ru_maxrss * 1024
};
Ok((
ExitStatus::from_raw(status),
ResourceUsage {
user_usec: timeval_to_seconds(rusage.ru_utime),
system_usec: timeval_to_seconds(rusage.ru_stime),
memory_usage_byte: memory_usage_byte.into(),
},
))
}
}
pub struct CPUTimer {}
@@ -77,13 +68,13 @@ impl CPUTimer {
Self {}
}
pub fn stop(&self, child: Child) -> Result<(Time, Time, Information, ExitStatus)> {
pub fn stop(&self, child: Child) -> Result<(ExitStatus, Second, Second, u64)> {
let (status, usage) = wait4(child)?;
Ok((
usage.time_user,
usage.time_system,
usage.memory_usage,
status,
usage.user_usec,
usage.system_usec,
u64::try_from(usage.memory_usage_byte).unwrap_or(0),
))
}
}