1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
use std::{time::Duration, process::Child};
/// Resource usage statistics for a process.
#[derive(Debug, Clone)]
pub struct RUsage {
/// User CPU time used.
pub utime: Duration,
/// System CPU time used.
pub stime: Duration,
/// Maximum resident set size.
pub maxrss: usize,
/// Integral shared memory size.
pub ixrss: usize,
/// Integral unshared data size.
pub idrss: usize,
/// Integral unshared stack size.
pub isrss: usize,
/// Page reclaims (soft page faults).
pub minflt: usize,
/// Page faults (hard page faults).
pub majflt: usize,
/// Swaps.
pub nswap: usize,
/// Block input operations.
pub inblock: usize,
/// Block output operations.
pub oublock: usize,
/// IPC messages sent.
pub msgsnd: usize,
/// IPC messages received.
pub msgrcv: usize,
/// Signals received.
pub nsignals: usize,
/// Voluntary context switches.
pub nvcsw: usize,
/// Involuntary context switches.
pub nivcsw: usize,
}
/// Error type for `getrusage` failures.
#[derive(Debug)]
pub enum Error {
/// The process does not exist.
NoSuchProcess,
/// The process exists, but is not a child of the current process.
NotChild,
/// The process exists, but its resource usage statistics are unavailable.
Unavailable,
/// This platform is not supported. There is only support for linux.
UnsupportedPlatform,
}
/// A trait for getting resource usage statistics for a process.
pub trait GetRUsage {
/// Waits for the process to exit and returns its resource usage statistics.
/// Works only on linux with wait4 syscall available.
fn wait_for_rusage(&mut self) -> Result<RUsage, Error>;
}
/// Type wrapper for result.
pub type RUsageResult = std::result::Result<RUsage, Error>;
/// Returns an empty `libc::rusage` struct.
#[cfg(target_os = "linux")]
unsafe fn empty_raw_rusage() -> libc::rusage {
std::mem::zeroed()
}
/// Converts a `libc::timeval` to a `std::time::Duration`.
fn duration_from_timeval(timeval: libc::timeval) -> Duration {
Duration::new(timeval.tv_sec as u64, (timeval.tv_usec * 1000) as u32)
}
impl GetRUsage for Child {
fn wait_for_rusage(&mut self) -> Result<RUsage, Error> {
let pid = self.id() as i32;
let mut status: i32 = 0;
#[cfg(target_os = "linux")]
{
let mut rusage;
unsafe {
rusage = empty_raw_rusage();
libc::wait4(
pid,
&mut status as *mut libc::c_int,
0i32,
&mut rusage as *mut libc::rusage,
);
}
if status == 0 {
Ok(RUsage {
utime: duration_from_timeval(rusage.ru_utime),
stime: duration_from_timeval(rusage.ru_stime),
maxrss: rusage.ru_maxrss as usize,
ixrss: rusage.ru_ixrss as usize,
idrss: rusage.ru_idrss as usize,
isrss: rusage.ru_isrss as usize,
minflt: rusage.ru_minflt as usize,
majflt: rusage.ru_majflt as usize,
nswap: rusage.ru_nswap as usize,
inblock: rusage.ru_inblock as usize,
oublock: rusage.ru_oublock as usize,
msgsnd: rusage.ru_msgsnd as usize,
msgrcv: rusage.ru_msgrcv as usize,
nsignals: rusage.ru_nsignals as usize,
nvcsw: rusage.ru_nvcsw as usize,
nivcsw: rusage.ru_nivcsw as usize,
})
} else if status == libc::ECHILD {
Err(Error::NoSuchProcess)
} else if status == libc::EINTR {
Err(Error::NotChild)
} else {
Err(Error::Unavailable)
}
}
#[cfg(not(target_os = "linux"))]
{
Err(Error::UnsupportedPlatform)
}
}
}
#[cfg(test)]
mod tests {
use std::process::Command;
use super::*;
#[test]
fn count_utime() {
let command = Command::new("tree").arg("/home")
.stdout(std::process::Stdio::null())
.spawn().expect("failed to execute process")
.wait_for_rusage().expect("failed to get rusage");
assert!(command.utime.as_micros() > 0);
}
}