time_cmd/
lib.rs

1/*!
2Time how long a process takes to run
3
4## Example
5
6```
7use std::{process::Command, time::Duration};
8use time_cmd::*;
9
10match time_cmd(Command::new("ls")) {
11    Err(_) => panic!("IO error - failed to run ls"),
12    Ok((timings, code)) => {
13        if !code.success() {
14            panic!("ls ran but exited non-zero");
15        }
16        if timings.wall_time > Duration::from_secs(1) {
17            panic!("That's a slow ls!");
18        }
19    }
20}
21```
22
23*/
24
25use std::io::Result;
26use std::process::{Command, ExitStatus};
27use std::time::{Duration, Instant};
28
29#[derive(Clone, Copy, PartialEq, Debug)]
30pub struct Timings {
31    pub wall_time: Duration,
32    pub user_time: f64,
33    pub sys_time: f64,
34}
35
36/// Spawns the given command and times how long it takes to exit.
37///
38/// The user must ensure that no other child processes are running at the
39/// same time, or else the times will be added.
40///
41/// On Windows the `user_time` and `sys_time` fields will be NaN.
42pub fn time_cmd(cmd: Command) -> Result<(Timings, ExitStatus)> {
43    #[cfg(unix)]
44    let ret = time_cmd_posix(cmd)?;
45    #[cfg(not(unix))]
46    let ret = time_cmd_fallback(cmd)?;
47    Ok(ret)
48}
49
50#[cfg(not(unix))]
51fn time_cmd_fallback(mut cmd: Command) -> Result<(Timings, ExitStatus)> {
52    let ts = Instant::now();
53    let status = cmd.spawn()?.wait()?;
54    let d = ts.elapsed();
55    Ok((
56        Timings {
57            wall_time: d,
58            user_time: std::f64::NAN,
59            sys_time: std::f64::NAN,
60        },
61        status,
62    ))
63}
64
65#[cfg(unix)]
66fn time_cmd_posix(mut cmd: Command) -> Result<(Timings, ExitStatus)> {
67    // times(2) and sysconf(2) are both POSIX
68    let mut tms_before = libc::tms {
69        tms_utime: 0,
70        tms_stime: 0,
71        tms_cutime: 0,
72        tms_cstime: 0,
73    };
74    let mut tms_after = tms_before;
75
76    unsafe { libc::times(&mut tms_before as *mut libc::tms) };
77    let ts = Instant::now();
78    let status = cmd.spawn()?.wait()?;
79    let d = ts.elapsed();
80    unsafe { libc::times(&mut tms_after as *mut libc::tms) };
81
82    let ticks_per_sec = unsafe { libc::sysconf(libc::_SC_CLK_TCK) } as f64;
83    let utime = (tms_after.tms_cutime - tms_before.tms_cutime) as f64 / ticks_per_sec;
84    let stime = (tms_after.tms_cstime - tms_before.tms_cstime) as f64 / ticks_per_sec;
85
86    Ok((
87        Timings {
88            wall_time: d,
89            user_time: utime,
90            sys_time: stime,
91        },
92        status,
93    ))
94}