diff --git a/Cargo.toml b/Cargo.toml index 9645199..7354282 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,11 @@ indicatif = "0.9" statistical = "0.1" atty = "0.2.2" -[target.'cfg(not(target_os = "windows"))'.dependencies] +[target.'cfg(not(windows))'.dependencies] libc = "0.2" -[target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3.4", features = [ "processthreadsapi", "minwindef"] } +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3.4", features = ["processthreadsapi", "minwindef", "winnt"] } [dependencies.clap] version = "2" diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index a136a66..d2fe47f 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -1,4 +1,5 @@ use std::io; +use std::process::ExitStatus; use colored::*; use statistical::{mean, standard_deviation}; @@ -10,9 +11,10 @@ use hyperfine::format::{format_duration, format_duration_unit}; use hyperfine::outlier_detection::{modified_zscores, OUTLIER_THRESHOLD}; use hyperfine::timer::Timer; use hyperfine::timer::wallclocktimer::WallClockTimer; -use hyperfine::timer::cputimer::CPUTimer; +use hyperfine::timer::cputimer::get_process_times; use hyperfine::shell::run_shell_command; + /// Results from timing a single shell command #[derive(Debug, Copy, Clone)] pub struct TimingResult { @@ -42,12 +44,13 @@ pub fn time_shell_command( shell_spawning_time: Option, ) -> io::Result<(TimingResult, bool)> { let wallclock_timer = WallClockTimer::start(); - let cpu_timer = CPUTimer::start(); + // let cpu_timer = CPUTimer::start(); - let status = run_shell_command(shell_cmd)?; + let (mut time_user, mut time_system, status) = execute_and_time(shell_cmd)?; + // let status = run_shell_command(shell_cmd)?; let mut time_real = wallclock_timer.stop(); - let (mut time_user, mut time_system) = cpu_timer.stop(); + // let (mut time_user, mut time_system) = cpu_timer.stop(); if failure_action == CmdFailureAction::RaiseError && !status.success() { return Err(io::Error::new( @@ -291,3 +294,34 @@ pub fn run_benchmark( Ok(()) } + +#[cfg(windows)] +fn execute_and_time(command: &str) -> io::Result<(f64, f64, ExitStatus)> { + use std::os::windows::io::AsRawHandle; + + let mut child = run_shell_command(command)?; + + let result = child.wait()?; + let handle = child.as_raw_handle(); + + let (user_usec, system_usec) = get_process_times(handle); + + // Temp, bypassing timer and the stop method + let user = user_usec as f64 * 1e-6; + let system = system_usec as f64 * 1e-6; + + Ok((user, system, result)) +} + +#[cfg(not(windows))] +fn execute_and_time(command: &str) -> io::Result<(f64, f64, ExitStatus)> { + use hyperfine::timer::cputimer::CPUTimer; + + let cpu_timer = CPUTimer::start(); + + let status = run_shell_command(command)?; + + let (mut time_user, mut time_system) = cpu_timer.stop(); + + Ok((time_user, time_system, status)) +} diff --git a/src/hyperfine/shell.rs b/src/hyperfine/shell.rs index 4545e72..9e18d15 100644 --- a/src/hyperfine/shell.rs +++ b/src/hyperfine/shell.rs @@ -1,8 +1,14 @@ use std::io; -use std::process::{Command, ExitStatus, Stdio}; +use std::process::{Command, Stdio}; + +#[cfg(not(windows))] +use std::process::ExitStatus; + +#[cfg(windows)] +use std::process::Child; /// Run a standard shell command -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] pub fn run_shell_command(command: &str) -> io::Result { Command::new("sh") .arg("-c") @@ -14,13 +20,13 @@ pub fn run_shell_command(command: &str) -> io::Result { } /// Run a Windows shell command using cmd.exe -#[cfg(target_os = "windows")] -pub fn run_shell_command(command: &str) -> io::Result { +#[cfg(windows)] +pub fn run_shell_command(command: &str) -> io::Result { Command::new("cmd") .arg("/C") .arg(command) .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) - .status() + .spawn() } diff --git a/src/hyperfine/timer/cputimer.rs b/src/hyperfine/timer/cputimer.rs index 7f5086c..851d286 100644 --- a/src/hyperfine/timer/cputimer.rs +++ b/src/hyperfine/timer/cputimer.rs @@ -1,6 +1,9 @@ use hyperfine::internal::Second; use hyperfine::timer::Timer; +#[cfg(windows)] +use std::os::windows::io::RawHandle; + #[derive(Debug, Copy, Clone)] struct CPUTimes { /// Total amount of time spent executing in user mode @@ -20,7 +23,7 @@ struct CPUInterval { } /// Read CPU execution times ('user' and 'system') -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] fn get_cpu_times() -> CPUTimes { use libc::{getrusage, rusage, RUSAGE_CHILDREN}; use std::mem; @@ -45,8 +48,17 @@ fn get_cpu_times() -> CPUTimes { /// Read CPU execution times (dummy for now) #[cfg(windows)] fn get_cpu_times() -> CPUTimes { - use winapi::um::processthreadsapi::{GetCurrentProcess, GetProcessTimes}; + CPUTimes { + user_usec : 0, + system_usec: 0, + } +} + +#[cfg(windows)] +pub fn get_process_times(handle: RawHandle) -> (i64, i64) { + use winapi::um::processthreadsapi::GetProcessTimes; use winapi::shared::minwindef::FILETIME; + use winapi::um::winnt::HANDLE; // Winapi reports times as per 100 nanosecond const HUNDRED_NS_PER_MS: i64 = 10; @@ -69,9 +81,8 @@ fn get_cpu_times() -> CPUTimes { }; let (user_usec, system_usec) = unsafe { - let handle = GetCurrentProcess(); let res = GetProcessTimes( - handle, + handle as HANDLE, &mut _ctime, &mut _etime, &mut kernel_time, @@ -81,6 +92,7 @@ fn get_cpu_times() -> CPUTimes { // GetProcessTimes will exit with non-zero if success as per: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683223(v=vs.85).aspx if res != 0 { // Extract times as laid out here: https://support.microsoft.com/en-us/help/188768/info-working-with-the-filetime-structure + // Both user_time and kernel_time are spans that the proces spent in either. let user: i64 = (((user_time.dwHighDateTime as i64) << 32) + user_time.dwLowDateTime as i64) / HUNDRED_NS_PER_MS; let kernel: i64 = (((kernel_time.dwHighDateTime as i64) << 32) + kernel_time.dwLowDateTime as i64) / HUNDRED_NS_PER_MS; (user, kernel) @@ -89,10 +101,7 @@ fn get_cpu_times() -> CPUTimes { } }; - CPUTimes { - user_usec, - system_usec, - } + (user_usec,system_usec) } /// Compute the time intervals in between two `CPUTimes` snapshots diff --git a/src/main.rs b/src/main.rs index 65e3b43..27e2542 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,10 +5,10 @@ extern crate colored; extern crate indicatif; extern crate statistical; -#[cfg(not(target_os = "windows"))] +#[cfg(not(windows))] extern crate libc; -#[cfg(target_os = "windows")] +#[cfg(windows)] extern crate winapi; #[cfg(test)] @@ -149,7 +149,7 @@ fn main() { OutputStyleOption::Basic }, }; - + if options.output_style == OutputStyleOption::Basic { colored::control::set_override(false); }