1use std::{time::Duration, process::Child};
2
3#[derive(Debug, Clone)]
5pub struct RUsage {
6 pub utime: Duration,
8 pub stime: Duration,
10 pub maxrss: usize,
12 pub ixrss: usize,
14 pub idrss: usize,
16 pub isrss: usize,
18 pub minflt: usize,
20 pub majflt: usize,
22 pub nswap: usize,
24 pub inblock: usize,
26 pub oublock: usize,
28 pub msgsnd: usize,
30 pub msgrcv: usize,
32 pub nsignals: usize,
34 pub nvcsw: usize,
36 pub nivcsw: usize,
38}
39
40#[derive(Debug)]
42pub enum Error {
43 NoSuchProcess,
45 NotChild,
47 Unavailable,
49 UnsupportedPlatform,
51}
52
53pub trait GetRUsage {
55 fn wait_for_rusage(&mut self) -> Result<RUsage, Error>;
58}
59
60pub type RUsageResult = std::result::Result<RUsage, Error>;
62
63#[cfg(target_os = "linux")]
65unsafe fn empty_raw_rusage() -> libc::rusage {
66 std::mem::zeroed()
67}
68
69fn duration_from_timeval(timeval: libc::timeval) -> Duration {
71 Duration::new(timeval.tv_sec as u64, (timeval.tv_usec * 1000) as u32)
72}
73
74impl GetRUsage for Child {
75 fn wait_for_rusage(&mut self) -> Result<RUsage, Error> {
76 let pid = self.id() as i32;
77 let mut status: i32 = 0;
78 #[cfg(target_os = "linux")]
79 {
80 let mut rusage;
81 unsafe {
82 rusage = empty_raw_rusage();
83 libc::wait4(
84 pid,
85 &mut status as *mut libc::c_int,
86 0i32,
87 &mut rusage as *mut libc::rusage,
88 );
89 }
90
91 if status == 0 {
92 Ok(RUsage {
93 utime: duration_from_timeval(rusage.ru_utime),
94 stime: duration_from_timeval(rusage.ru_stime),
95 maxrss: rusage.ru_maxrss as usize,
96 ixrss: rusage.ru_ixrss as usize,
97 idrss: rusage.ru_idrss as usize,
98 isrss: rusage.ru_isrss as usize,
99 minflt: rusage.ru_minflt as usize,
100 majflt: rusage.ru_majflt as usize,
101 nswap: rusage.ru_nswap as usize,
102 inblock: rusage.ru_inblock as usize,
103 oublock: rusage.ru_oublock as usize,
104 msgsnd: rusage.ru_msgsnd as usize,
105 msgrcv: rusage.ru_msgrcv as usize,
106 nsignals: rusage.ru_nsignals as usize,
107 nvcsw: rusage.ru_nvcsw as usize,
108 nivcsw: rusage.ru_nivcsw as usize,
109 })
110 } else if status == libc::ECHILD {
111 Err(Error::NoSuchProcess)
112 } else if status == libc::EINTR {
113 Err(Error::NotChild)
114 } else {
115 Err(Error::Unavailable)
116 }
117 }
118 #[cfg(not(target_os = "linux"))]
119 {
120 Err(Error::UnsupportedPlatform)
121 }
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use std::process::Command;
128
129 use super::*;
130
131 #[test]
132 fn count_utime() {
133 let command = Command::new("tree").arg("/home")
134 .stdout(std::process::Stdio::null())
135 .spawn().expect("failed to execute process")
136 .wait_for_rusage().expect("failed to get rusage");
137 assert!(command.utime.as_micros() > 0);
138 }
139}