From b27496be956c6fba79b6aa50175404c24c7baf05 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Fri, 26 Feb 2021 23:20:05 -0500 Subject: [PATCH 01/22] General: Add `time` binary to toml files of OSes --- Cargo.toml | 1 + FreeBSD.toml | 1 + Fuchsia.toml | 1 + Haiku.toml | 1 + Illumos.toml | 1 + Linux.toml | 1 + MacOS.toml | 1 + NetBSD.toml | 1 + OpenBSD.toml | 1 + Solaris.toml | 1 + Unix.toml | 1 + 11 files changed, 11 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c69438a0..bf508390 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/FreeBSD.toml b/FreeBSD.toml index 7099abe5..3da75087 100644 --- a/FreeBSD.toml +++ b/FreeBSD.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Fuchsia.toml b/Fuchsia.toml index 6f770662..3ef963a9 100644 --- a/Fuchsia.toml +++ b/Fuchsia.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Haiku.toml b/Haiku.toml index 6f770662..3ef963a9 100644 --- a/Haiku.toml +++ b/Haiku.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Illumos.toml b/Illumos.toml index 7099abe5..3da75087 100644 --- a/Illumos.toml +++ b/Illumos.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Linux.toml b/Linux.toml index 863d08bb..dc41b834 100644 --- a/Linux.toml +++ b/Linux.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/MacOS.toml b/MacOS.toml index 7099abe5..3da75087 100644 --- a/MacOS.toml +++ b/MacOS.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/NetBSD.toml b/NetBSD.toml index 7099abe5..3da75087 100644 --- a/NetBSD.toml +++ b/NetBSD.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/OpenBSD.toml b/OpenBSD.toml index 7099abe5..3da75087 100644 --- a/OpenBSD.toml +++ b/OpenBSD.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Solaris.toml b/Solaris.toml index 7099abe5..3da75087 100644 --- a/Solaris.toml +++ b/Solaris.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", diff --git a/Unix.toml b/Unix.toml index 7099abe5..3da75087 100644 --- a/Unix.toml +++ b/Unix.toml @@ -41,6 +41,7 @@ members = [ "sort", "tail", "tee", + "time", "touch", "true", "tty", From 471414b99f6114485e734b635a39fbd2bafb411d Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 27 Feb 2021 21:22:31 -0500 Subject: [PATCH 02/22] Time: Scaffold code from template - Initial structure taken from .template with tool specific info filled in --- time/Cargo.toml | 14 ++++++++++++++ time/build.rs | 24 ++++++++++++++++++++++++ time/src/cli.rs | 15 +++++++++++++++ time/src/main.rs | 5 +++++ 4 files changed, 58 insertions(+) create mode 100644 time/Cargo.toml create mode 100644 time/build.rs create mode 100644 time/src/cli.rs create mode 100644 time/src/main.rs diff --git a/time/Cargo.toml b/time/Cargo.toml new file mode 100644 index 00000000..d221a99f --- /dev/null +++ b/time/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "time" +version = "0.1.0" +authors = ["Vaibhav Yenamandra "] +license = "MPL-2.0-no-copyleft-exception" +build = "build.rs" +edition = "2018" +description = "time a simple command" + +[dependencies] +clap = { version = "^2.33.0", features = ["wrap_help"] } + +[build-dependencies] +clap = { version = "^2.33.0" } diff --git a/time/build.rs b/time/build.rs new file mode 100644 index 00000000..8e0a5883 --- /dev/null +++ b/time/build.rs @@ -0,0 +1,24 @@ +use std::env; + +use clap::Shell; + +#[path = "src/cli.rs"] +mod cli; + +fn main() { + let mut app = cli::create_app(); + + let out_dir = match env::var("OUT_DIR") { + Ok(dir) => dir, + Err(err) => { + eprintln!("No OUT_DIR: {}", err); + return; + }, + }; + + app.gen_completions("template", Shell::Zsh, out_dir.clone()); + app.gen_completions("template", Shell::Fish, out_dir.clone()); + app.gen_completions("template", Shell::Bash, out_dir.clone()); + app.gen_completions("template", Shell::PowerShell, out_dir.clone()); + app.gen_completions("template", Shell::Elvish, out_dir); +} diff --git a/time/src/cli.rs b/time/src/cli.rs new file mode 100644 index 00000000..b97ff419 --- /dev/null +++ b/time/src/cli.rs @@ -0,0 +1,15 @@ +use clap::{ + crate_authors, crate_description, crate_name, crate_version, App, AppSettings::ColoredHelp, +}; + +pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { + let app = App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .help_message("Display help information.") + .version_message("Display version information.") + .help_short("?") + .settings(&[ColoredHelp]); + app +} diff --git a/time/src/main.rs b/time/src/main.rs new file mode 100644 index 00000000..7230cd47 --- /dev/null +++ b/time/src/main.rs @@ -0,0 +1,5 @@ +mod cli; + +fn main() { + let _ = cli::create_app().get_matches(); +} From b9ad3d59145816fcd01a6c1c67766846b0e72fc6 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 27 Feb 2021 21:25:16 -0500 Subject: [PATCH 03/22] Time: cli: Add -p flag, and accept a command to run --- time/src/cli.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index b97ff419..3c98aa27 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -1,5 +1,5 @@ use clap::{ - crate_authors, crate_description, crate_name, crate_version, App, AppSettings::ColoredHelp, + crate_authors, crate_description, crate_name, crate_version, App, AppSettings::ColoredHelp, Arg, }; pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { @@ -11,5 +11,57 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { .version_message("Display version information.") .help_short("?") .settings(&[ColoredHelp]); - app + + let posix_fmt = Arg::with_name("posix") + .help( + "Display time output in POSIX specified format as:\n\treal %f\n\tuser %f\n\tsys \ + %f\nTimer accuracy is arbitrary, but will always be counted in seconds.", + ) + .short("p") + .long("--posix") + .takes_value(false); + + let command = Arg::with_name("COMMAND").help("Command or utility to run.").required(true); + + let arguments = Arg::with_name("ARGUMENT") + .help("Optional arguments to pass to .") + .multiple(true) + .number_of_values(1) + .empty_values(false); + + app.arg(posix_fmt).arg(command).arg(arguments) +} + +#[derive(Debug)] +pub struct Args { + pub output_fmt: OutputFormat, + pub executable: String, + pub exec_args: Vec, +} + +impl Args { + pub fn new() -> Args { + let args = create_app().get_matches(); + let command = + args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); + + Args { + output_fmt: if args.is_present("posix") { + OutputFormat::Posix + } else { + OutputFormat::Default + }, + executable: command.to_owned(), + exec_args: match args.values_of("arguments") { + Some(vs) => vs.into_iter().map(|item| item.to_owned()).collect(), + None => vec![], + }, + } + } +} + +#[derive(Debug)] +pub enum OutputFormat { + Default, + Posix, } From a621450cdf6a9fbc4f0f16763dd916c0b9b01570 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 28 Feb 2021 15:46:12 -0500 Subject: [PATCH 04/22] Time: cli: Rename struct s/Args/TimeOpts --- time/src/cli.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 3c98aa27..36c8d2a6 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -33,19 +33,19 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { } #[derive(Debug)] -pub struct Args { +pub struct TimeOpts { pub output_fmt: OutputFormat, pub executable: String, pub exec_args: Vec, } -impl Args { - pub fn new() -> Args { +impl TimeOpts { + pub fn new() -> TimeOpts { let args = create_app().get_matches(); let command = args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); - Args { + TimeOpts { output_fmt: if args.is_present("posix") { OutputFormat::Posix } else { From 6229d6a341c13c0d2d52a4613f0e76d654e6c9ca Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 28 Feb 2021 19:24:15 -0500 Subject: [PATCH 05/22] Core: Create, export module resource - Wraps getrusage(2) --- coreutils_core/src/os.rs | 1 + coreutils_core/src/os/resource.rs | 104 ++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 coreutils_core/src/os/resource.rs diff --git a/coreutils_core/src/os.rs b/coreutils_core/src/os.rs index aec92616..ae33263e 100644 --- a/coreutils_core/src/os.rs +++ b/coreutils_core/src/os.rs @@ -6,6 +6,7 @@ pub mod process; pub mod time; pub mod tty; pub mod utsname; +pub mod resource; // Specific Modules #[cfg(not(any(target_os = "fuchsia", target_os = "haiku")))] diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs new file mode 100644 index 00000000..33a345d7 --- /dev/null +++ b/coreutils_core/src/os/resource.rs @@ -0,0 +1,104 @@ +//! Module abstracting interactions with getrusage(2) +//! +//! Also holds utility functions for summarizing the data returned by getrusage(2) +use libc::{getrusage, rusage, c_int, RUSAGE_SELF, RUSAGE_CHILDREN}; +use super::TimeVal; + +/// Interface for `RUSAGE_*` constants from libc. +/// +/// TODO This is an incomplete set of constants. It is currently missing +/// `libc::RUSAGE_THREAD` which requires the `_GNU_SOURCE` macro to be defined +/// at build time. +pub enum ResourceConsumer { + Caller = RUSAGE_SELF as isize, + Children = RUSAGE_CHILDREN as isize, +} + +#[derive(Debug)] +pub struct RUsage { + timing: Timing, + mem: MemoryUsage, + io: IOUsage +} + +#[derive(Debug)] +pub struct Timing { + /// User CPU time used + pub user_time: TimeVal, + /// System CPU time used + pub sys_time: TimeVal, +} + +#[derive(Debug)] +pub struct MemoryUsage { + /// Maximum resident set size + pub max_rss: u64, + /// Number of page reclaims (soft page faults) + pub num_minor_page_flt: u64, + /// Number of page faults (hard page faults) + pub num_major_page_flt: u64, + /// Number of voluntary context switches + pub num_vol_ctx_switch: u64, + /// Number of involuntary context switches + pub num_invol_ctx_switch: u64, + /// Unmaintained on linux: Integral shared memory size + pub shared_mem_size: u64, + /// Unmaintained on linux: Integral unshared data size + pub unshared_data_size: u64, + /// Unmaintained on linux: Integral unshared stack size + pub unshared_stack_size: u64, + /// Unmaintained on linux: Number of swaps + pub num_swaps: u64, +} + +#[derive(Debug)] +pub struct IOUsage { + /// Number of block input operations + pub num_block_in: u64, + /// Number of block output operations + pub num_block_out: u64, + /// Unmaintained on linux: Number of IPC messages recieved + pub num_sock_recv: u64, + /// Unmaintained on linux: Number of IPC messages sent + pub num_sock_send: u64, + /// Unmaintained: Number of signals recieved + pub num_signals: u64, +} + +impl RUsage { + fn from(ru: rusage) -> Self { + RUsage { + timing: Timing { + user_time: ru.ru_utime, + sys_time: ru.ru_stime, + }, + mem: MemoryUsage { + max_rss: ru.ru_maxrss as u64, + num_minor_page_flt: ru.ru_minflt as u64, + num_major_page_flt: ru.ru_majflt as u64, + num_vol_ctx_switch: ru.ru_nvcsw as u64, + num_invol_ctx_switch: ru.ru_nivcsw as u64, + shared_mem_size: ru.ru_ixrss as u64, + unshared_data_size: ru.ru_idrss as u64, + unshared_stack_size: ru.ru_isrss as u64, + num_swaps: ru.ru_nswap as u64, + }, + io: IOUsage { + num_block_in: ru.ru_inblock as u64, + num_block_out: ru.ru_oublock as u64, + num_sock_recv: ru.ru_msgrcv as u64, + num_sock_send: ru.ru_msgsnd as u64, + num_signals: ru.ru_nsignals as u64, + } + } + } +} + +/// Safely wrap `libc::getrusage` +pub fn get_rusage(target: ResourceConsumer) -> RUsage { + let mut usage: rusage = unsafe { std::mem::zeroed() }; + unsafe { + getrusage(target as c_int, &mut usage); + } + RUsage::from(usage) +} From 2ffb334b5c091bf56196521b44399319f1267e4f Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 28 Feb 2021 23:43:46 -0500 Subject: [PATCH 06/22] Time: cli: Combine exec_args and executable into a single field --- time/src/cli.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 36c8d2a6..4f5a2d66 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -35,8 +35,7 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { #[derive(Debug)] pub struct TimeOpts { pub output_fmt: OutputFormat, - pub executable: String, - pub exec_args: Vec, + pub command: Vec, } impl TimeOpts { @@ -51,10 +50,13 @@ impl TimeOpts { } else { OutputFormat::Default }, - executable: command.to_owned(), - exec_args: match args.values_of("arguments") { - Some(vs) => vs.into_iter().map(|item| item.to_owned()).collect(), - None => vec![], + command: match args.values_of("arguments") { + Some(vs) => { + let mut cmd = vec![command.to_owned()]; + cmd.extend(vs.into_iter().map(|item| item.to_owned())); + cmd + }, + None => vec![command.to_owned()], }, } } From cd93133451d325c514a73a105c8831b0e79af6f0 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 28 Feb 2021 23:48:26 -0500 Subject: [PATCH 07/22] Time: subprocess: Create subprocess module --- time/src/subprocess.rs | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 time/src/subprocess.rs diff --git a/time/src/subprocess.rs b/time/src/subprocess.rs new file mode 100644 index 00000000..f4a7819b --- /dev/null +++ b/time/src/subprocess.rs @@ -0,0 +1,56 @@ +/// Module for creating, and interacting with child processes +use std::process::{exit, Command, ExitStatus, Stdio}; +use std::{ + io, + time::{Duration, Instant}, +}; + +type SubprocessTiming = (ExitStatus, Duration); + +/// Wrapper around `std::process::exit` that prints the error's +/// message to stderr before quitting. +/// +/// Will try to propagate the error code set in the err if available +pub fn exit_with_msg(err: std::io::Error) -> ! { + eprintln!("{}", err); + exit(err.raw_os_error().unwrap_or(1)) +} + +/// Wrapper for creating, spawning and waiting on `std::process::Command` +/// Returns the `std::process::ExitStatus` of the `std::process::Command` +/// that was run +pub fn timed_run(cmd_vec: &Vec) -> io::Result { + let mut cmd = Command::new(&cmd_vec[0]); + cmd.args(&cmd_vec[1..]); + cmd.stdin(Stdio::inherit()).stdout(Stdio::inherit()).stderr(Stdio::inherit()); + + let start_time = Instant::now(); + let status = cmd.spawn()?.wait()?; + Ok((status, start_time.elapsed())) +} + +#[cfg(test)] +mod tests { + use super::{timed_run, Duration}; + + #[test] + fn invalid_command_returns_errno_when_set() { + if let Err(err) = timed_run(&vec!["does-not-exist".to_string()]) { + assert!(err.raw_os_error() == Some(2)) + } else { + assert!(false, "Subprocess did not fail as expected") + } + } + + #[test] + fn correct_duration_when_sleeping() { + if let Ok((status, duration)) = timed_run(&vec!["sleep".to_string(), "0.1".to_string()]) { + assert!(status.code() == Some(0)); + assert!(duration > Duration::from_millis(100)); + // TODO Is this a good tolerance for the duration? + assert!(duration < Duration::from_millis(200)); + } else { + assert!(false, "Failed to run command"); + } + } +} From 4e6468f11831c7ff106b035318097d14e783e9af Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Mon, 1 Mar 2021 00:00:38 -0500 Subject: [PATCH 08/22] Time: Parse CLI opts, run subprocess and extract resource usage --- time/Cargo.toml | 1 + time/src/main.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/time/Cargo.toml b/time/Cargo.toml index d221a99f..aa4854df 100644 --- a/time/Cargo.toml +++ b/time/Cargo.toml @@ -9,6 +9,7 @@ description = "time a simple command" [dependencies] clap = { version = "^2.33.0", features = ["wrap_help"] } +coreutils_core = { path = "../coreutils_core" } [build-dependencies] clap = { version = "^2.33.0" } diff --git a/time/src/main.rs b/time/src/main.rs index 7230cd47..c1a50bf0 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -1,5 +1,14 @@ mod cli; +mod subprocess; + +use coreutils_core::os::resource::{get_rusage, ResourceConsumer}; fn main() { - let _ = cli::create_app().get_matches(); + let opts = cli::TimeOpts::new(); + let (exit_status, duration) = match subprocess::timed_run(&opts.command) { + Ok(rv) => rv, + Err(err) => subprocess::exit_with_msg(err), + }; + + let usage = get_rusage(ResourceConsumer::Children); } From 8caf5c92fbc6b37abc542f7f2421df4e377716f8 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Fri, 5 Mar 2021 21:55:02 -0500 Subject: [PATCH 09/22] Time: cli: Fix bug in argument specification No longer use number_of_values(true) as that implies an option Also use "ARGUMENT" to match from clap --- time/src/cli.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 4f5a2d66..147090af 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -25,11 +25,9 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { let arguments = Arg::with_name("ARGUMENT") .help("Optional arguments to pass to .") - .multiple(true) - .number_of_values(1) - .empty_values(false); + .multiple(true); - app.arg(posix_fmt).arg(command).arg(arguments) + app.args(&[posix_fmt, command, arguments]) } #[derive(Debug)] @@ -50,7 +48,7 @@ impl TimeOpts { } else { OutputFormat::Default }, - command: match args.values_of("arguments") { + command: match args.values_of("ARGUMENT") { Some(vs) => { let mut cmd = vec![command.to_owned()]; cmd.extend(vs.into_iter().map(|item| item.to_owned())); From f0b97fd93de9104096336627ef188e5f534b5ae6 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Fri, 5 Mar 2021 22:08:01 -0500 Subject: [PATCH 10/22] Time: cli: Refactor and add test cases - Refactor enum declaration to use `Self` - Add test cases for `TimeOpts` creation - Rename 'OutputFormat' to 'OutputFormatter' --- time/src/cli.rs | 39 ++++++++++++++++++++++++++++++++------- time/src/main.rs | 2 +- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 147090af..d71093e4 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -32,21 +32,23 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { #[derive(Debug)] pub struct TimeOpts { - pub output_fmt: OutputFormat, + pub output_fmt: OutputFormatter, pub command: Vec, } impl TimeOpts { - pub fn new() -> TimeOpts { - let args = create_app().get_matches(); + pub fn from_matches() -> Self { + Self::new(create_app().get_matches()) + } + pub fn new(args: clap::ArgMatches) -> Self { let command = args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); TimeOpts { output_fmt: if args.is_present("posix") { - OutputFormat::Posix + OutputFormatter::Posix } else { - OutputFormat::Default + OutputFormatter::Default }, command: match args.values_of("ARGUMENT") { Some(vs) => { @@ -60,8 +62,31 @@ impl TimeOpts { } } -#[derive(Debug)] -pub enum OutputFormat { +#[derive(Debug, PartialEq)] +pub enum OutputFormatter { Default, Posix, } + +#[cfg(test)] +mod tests { + use super::{TimeOpts, OutputFormatter, create_app}; + + #[test] + fn parsing_valid_command_with_args() { + let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3"]; + let opts = TimeOpts::new(create_app().get_matches_from(args)); + + assert_eq!(4, opts.command.len()); + assert_eq!(vec!["cmd-to-run", "arg1", "arg2", "arg3"], opts.command); + assert_eq!(OutputFormatter::Default, opts.output_fmt); + } + + #[test] + fn parse_valid_command_with_posix_spec() { + let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3", "-p"]; + let opts = TimeOpts::new(create_app().get_matches_from(args)); + + assert_eq!(OutputFormatter::Posix, opts.output_fmt); + } +} diff --git a/time/src/main.rs b/time/src/main.rs index c1a50bf0..6df8c49d 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -4,7 +4,7 @@ mod subprocess; use coreutils_core::os::resource::{get_rusage, ResourceConsumer}; fn main() { - let opts = cli::TimeOpts::new(); + let opts = cli::TimeOpts::from_matches(); let (exit_status, duration) = match subprocess::timed_run(&opts.command) { Ok(rv) => rv, Err(err) => subprocess::exit_with_msg(err), From 6f3dd318880cae1048ef313a5439b5b907259c11 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Fri, 5 Mar 2021 23:07:09 -0500 Subject: [PATCH 11/22] Time: Break up CLI into flags, output modules --- time/src/cli.rs | 61 --------------------------------------------- time/src/flags.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++ time/src/main.rs | 4 ++- time/src/output.rs | 11 ++++++++ 4 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 time/src/flags.rs create mode 100644 time/src/output.rs diff --git a/time/src/cli.rs b/time/src/cli.rs index d71093e4..181bec80 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -29,64 +29,3 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { app.args(&[posix_fmt, command, arguments]) } - -#[derive(Debug)] -pub struct TimeOpts { - pub output_fmt: OutputFormatter, - pub command: Vec, -} - -impl TimeOpts { - pub fn from_matches() -> Self { - Self::new(create_app().get_matches()) - } - pub fn new(args: clap::ArgMatches) -> Self { - let command = - args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); - - TimeOpts { - output_fmt: if args.is_present("posix") { - OutputFormatter::Posix - } else { - OutputFormatter::Default - }, - command: match args.values_of("ARGUMENT") { - Some(vs) => { - let mut cmd = vec![command.to_owned()]; - cmd.extend(vs.into_iter().map(|item| item.to_owned())); - cmd - }, - None => vec![command.to_owned()], - }, - } - } -} - -#[derive(Debug, PartialEq)] -pub enum OutputFormatter { - Default, - Posix, -} - -#[cfg(test)] -mod tests { - use super::{TimeOpts, OutputFormatter, create_app}; - - #[test] - fn parsing_valid_command_with_args() { - let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3"]; - let opts = TimeOpts::new(create_app().get_matches_from(args)); - - assert_eq!(4, opts.command.len()); - assert_eq!(vec!["cmd-to-run", "arg1", "arg2", "arg3"], opts.command); - assert_eq!(OutputFormatter::Default, opts.output_fmt); - } - - #[test] - fn parse_valid_command_with_posix_spec() { - let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3", "-p"]; - let opts = TimeOpts::new(create_app().get_matches_from(args)); - - assert_eq!(OutputFormatter::Posix, opts.output_fmt); - } -} diff --git a/time/src/flags.rs b/time/src/flags.rs new file mode 100644 index 00000000..11f72bed --- /dev/null +++ b/time/src/flags.rs @@ -0,0 +1,62 @@ +//! Command line options that are supported by `time` + +use crate::cli::create_app; +use crate::output::OutputFormatter; + +// Condense CLI args as a struct +#[derive(Debug)] +pub struct TimeOpts { + /// Formatter to use when printing stats back to CLI + pub printer: OutputFormatter, + /// Command as seen on the CLI + pub command: Vec, +} + +impl TimeOpts { + pub fn from_matches() -> Self { + Self::new(create_app().get_matches()) + } + pub fn new(args: clap::ArgMatches) -> Self { + let command = + args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); + + TimeOpts { + printer: if args.is_present("posix") { + OutputFormatter::Posix + } else { + OutputFormatter::Default + }, + command: match args.values_of("ARGUMENT") { + Some(vs) => { + let mut cmd = vec![command.to_owned()]; + cmd.extend(vs.into_iter().map(|item| item.to_owned())); + cmd + }, + None => vec![command.to_owned()], + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::{TimeOpts, OutputFormatter, create_app}; + + #[test] + fn parsing_valid_command_with_args() { + let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3"]; + let opts = TimeOpts::new(create_app().get_matches_from(args)); + + assert_eq!(4, opts.command.len()); + assert_eq!(vec!["cmd-to-run", "arg1", "arg2", "arg3"], opts.command); + assert_eq!(OutputFormatter::Default, opts.printer); + } + + #[test] + fn parse_valid_command_with_posix_spec() { + let args = vec!["test-time", "cmd-to-run", "arg1", "arg2", "arg3", "-p"]; + let opts = TimeOpts::new(create_app().get_matches_from(args)); + + assert_eq!(OutputFormatter::Posix, opts.printer); + } +} diff --git a/time/src/main.rs b/time/src/main.rs index 6df8c49d..cb38070b 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -1,10 +1,12 @@ mod cli; +mod output; +mod flags; mod subprocess; use coreutils_core::os::resource::{get_rusage, ResourceConsumer}; fn main() { - let opts = cli::TimeOpts::from_matches(); + let opts = flags::TimeOpts::from_matches(); let (exit_status, duration) = match subprocess::timed_run(&opts.command) { Ok(rv) => rv, Err(err) => subprocess::exit_with_msg(err), diff --git a/time/src/output.rs b/time/src/output.rs new file mode 100644 index 00000000..99278ff6 --- /dev/null +++ b/time/src/output.rs @@ -0,0 +1,11 @@ +//! Output interface for `time` + +#[derive(Debug, PartialEq)] +pub enum OutputFormatter { + Default, + Posix, +} + +impl OutputFormatter { + pub fn format_stats(rusage: &Rusage, duration: &std::time::Duration) {} +} From 003f0121c0facabb9ddfb78c1b82027aeb0d030c Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 12:12:03 -0500 Subject: [PATCH 12/22] Time: Implement `POSIX.2` output formatting and macos (FreeBSD) default --- coreutils_core/src/os/resource.rs | 6 +++--- time/src/main.rs | 2 ++ time/src/output.rs | 22 +++++++++++++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs index 33a345d7..6e442af4 100644 --- a/coreutils_core/src/os/resource.rs +++ b/coreutils_core/src/os/resource.rs @@ -16,9 +16,9 @@ pub enum ResourceConsumer { #[derive(Debug)] pub struct RUsage { - timing: Timing, - mem: MemoryUsage, - io: IOUsage + pub timing: Timing, + pub mem: MemoryUsage, + pub io: IOUsage } #[derive(Debug)] diff --git a/time/src/main.rs b/time/src/main.rs index cb38070b..e0cb4178 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -13,4 +13,6 @@ fn main() { }; let usage = get_rusage(ResourceConsumer::Children); + + eprintln!("{}", opts.printer.format_stats(&usage, &duration)); } diff --git a/time/src/output.rs b/time/src/output.rs index 99278ff6..6350381d 100644 --- a/time/src/output.rs +++ b/time/src/output.rs @@ -1,11 +1,31 @@ //! Output interface for `time` +use coreutils_core::os::{resource::RUsage, TimeVal}; + #[derive(Debug, PartialEq)] pub enum OutputFormatter { Default, Posix, } +/// Express `coreutils_core::os::TimeVal` into `f64` seconds +fn as_secs_f64(tv: TimeVal) -> f64 { tv.tv_sec as f64 + (tv.tv_usec as f64) / 1_000_000.0 } + impl OutputFormatter { - pub fn format_stats(rusage: &Rusage, duration: &std::time::Duration) {} + pub fn format_stats(self, rusage: &RUsage, duration: &std::time::Duration) -> String { + let wall_time = duration.as_secs_f64(); + let user_time = as_secs_f64(rusage.timing.user_time); + let sys_time = as_secs_f64(rusage.timing.sys_time); + match self { + OutputFormatter::Default => default_formatter(rusage, wall_time, user_time, sys_time), + OutputFormatter::Posix => { + format!("real {:.2}\nuser {:.2}\nsys {:.2}", wall_time, user_time, sys_time) + }, + } + } +} + +#[cfg(target_os = "macos")] +fn default_formatter(_rusage: &RUsage, wall_time: f64, user_time: f64, sys_time: f64) -> String { + format!(" {:.2} real {:.2} user {:.2} sys", wall_time, user_time, sys_time) } From 0bcb1f2f327c02fb3cf207311a3706f143b87d9c Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 12:13:35 -0500 Subject: [PATCH 13/22] Time: Format code --- time/src/cli.rs | 5 ++--- time/src/flags.rs | 12 +++++------- time/src/main.rs | 2 +- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 181bec80..3602dc7c 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -23,9 +23,8 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { let command = Arg::with_name("COMMAND").help("Command or utility to run.").required(true); - let arguments = Arg::with_name("ARGUMENT") - .help("Optional arguments to pass to .") - .multiple(true); + let arguments = + Arg::with_name("ARGUMENT").help("Optional arguments to pass to .").multiple(true); app.args(&[posix_fmt, command, arguments]) } diff --git a/time/src/flags.rs b/time/src/flags.rs index 11f72bed..565b86e9 100644 --- a/time/src/flags.rs +++ b/time/src/flags.rs @@ -1,7 +1,6 @@ //! Command line options that are supported by `time` -use crate::cli::create_app; -use crate::output::OutputFormatter; +use crate::{cli::create_app, output::OutputFormatter}; // Condense CLI args as a struct #[derive(Debug)] @@ -13,9 +12,8 @@ pub struct TimeOpts { } impl TimeOpts { - pub fn from_matches() -> Self { - Self::new(create_app().get_matches()) - } + pub fn from_matches() -> Self { Self::new(create_app().get_matches()) } + pub fn new(args: clap::ArgMatches) -> Self { let command = args.value_of("COMMAND").expect("`COMMAND` value cannot be `None`, it is required."); @@ -26,7 +24,7 @@ impl TimeOpts { } else { OutputFormatter::Default }, - command: match args.values_of("ARGUMENT") { + command: match args.values_of("ARGUMENT") { Some(vs) => { let mut cmd = vec![command.to_owned()]; cmd.extend(vs.into_iter().map(|item| item.to_owned())); @@ -40,7 +38,7 @@ impl TimeOpts { #[cfg(test)] mod tests { - use super::{TimeOpts, OutputFormatter, create_app}; + use super::{create_app, OutputFormatter, TimeOpts}; #[test] fn parsing_valid_command_with_args() { diff --git a/time/src/main.rs b/time/src/main.rs index e0cb4178..89bd8f25 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -1,6 +1,6 @@ mod cli; -mod output; mod flags; +mod output; mod subprocess; use coreutils_core::os::resource::{get_rusage, ResourceConsumer}; From 54223a0e41301e233e1ee7dd0cc843d1a93077be Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 12:34:47 -0500 Subject: [PATCH 14/22] Time: output: Move platform specific details into their own modules --- time/src/output.rs | 10 +++++----- time/src/output/macos.rs | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 time/src/output/macos.rs diff --git a/time/src/output.rs b/time/src/output.rs index 6350381d..0ae38e65 100644 --- a/time/src/output.rs +++ b/time/src/output.rs @@ -2,6 +2,11 @@ use coreutils_core::os::{resource::RUsage, TimeVal}; +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "macos")] +pub use macos::default_formatter; + #[derive(Debug, PartialEq)] pub enum OutputFormatter { Default, @@ -24,8 +29,3 @@ impl OutputFormatter { } } } - -#[cfg(target_os = "macos")] -fn default_formatter(_rusage: &RUsage, wall_time: f64, user_time: f64, sys_time: f64) -> String { - format!(" {:.2} real {:.2} user {:.2} sys", wall_time, user_time, sys_time) -} diff --git a/time/src/output/macos.rs b/time/src/output/macos.rs new file mode 100644 index 00000000..026f46d3 --- /dev/null +++ b/time/src/output/macos.rs @@ -0,0 +1,7 @@ +//! Output interface for `time` + +use coreutils_core::os::resource::RUsage; + +pub fn default_formatter(_rusage: &RUsage, wall_time: f64, user_time: f64, sys_time: f64) -> String { + format!(" {:.2} real {:.2} user {:.2} sys", wall_time, user_time, sys_time) +} From 1f25d16d562a381da9d69cffe83322b31aa8fea4 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 12:55:07 -0500 Subject: [PATCH 15/22] Time: subprocess: Follow `POSIX.2` spec for error codes Unspecified errors are arbitrarily mapped to the 1-125 range --- time/src/main.rs | 1 + time/src/subprocess.rs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/time/src/main.rs b/time/src/main.rs index 89bd8f25..b7a3d5ed 100644 --- a/time/src/main.rs +++ b/time/src/main.rs @@ -15,4 +15,5 @@ fn main() { let usage = get_rusage(ResourceConsumer::Children); eprintln!("{}", opts.printer.format_stats(&usage, &duration)); + std::process::exit(exit_status.code().unwrap_or(1)); } diff --git a/time/src/subprocess.rs b/time/src/subprocess.rs index f4a7819b..e4afb1f2 100644 --- a/time/src/subprocess.rs +++ b/time/src/subprocess.rs @@ -13,7 +13,19 @@ type SubprocessTiming = (ExitStatus, Duration); /// Will try to propagate the error code set in the err if available pub fn exit_with_msg(err: std::io::Error) -> ! { eprintln!("{}", err); - exit(err.raw_os_error().unwrap_or(1)) + + // Translate the exit code according to POSIX spec + // 1-125: for errors internal to `time` + // 126 : Command was found but could not be invoked (PermissionError) + // 127 : Command was not found + exit(match err.kind() { + io::ErrorKind::PermissionDenied => 126, + io::ErrorKind::NotFound => 127, + // Translate other error code to 0-124 and shift right by 1 + // Internal exit codes are typically arbitrary enough that they be + // considered limited to developer use-only + _ => 1 + (err.raw_os_error().unwrap_or(0) % 125), + }) } /// Wrapper for creating, spawning and waiting on `std::process::Command` From d8eba37014fca8b14edaf94e1ee835d1f2972a62 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 13:57:45 -0500 Subject: [PATCH 16/22] Time: cli: Drop non-standard `--posix` --- time/src/cli.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/time/src/cli.rs b/time/src/cli.rs index 3602dc7c..f0b528b2 100644 --- a/time/src/cli.rs +++ b/time/src/cli.rs @@ -18,7 +18,6 @@ pub(crate) fn create_app<'a, 'b>() -> App<'a, 'b> { %f\nTimer accuracy is arbitrary, but will always be counted in seconds.", ) .short("p") - .long("--posix") .takes_value(false); let command = Arg::with_name("COMMAND").help("Command or utility to run.").required(true); From 31264e0c2d35ad2940267a4f4ddef4d4ad29ef91 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 14:27:20 -0500 Subject: [PATCH 17/22] Time: drop macos specific code --- time/src/output.rs | 9 ++++----- time/src/output/macos.rs | 7 ------- 2 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 time/src/output/macos.rs diff --git a/time/src/output.rs b/time/src/output.rs index 0ae38e65..07f6aa93 100644 --- a/time/src/output.rs +++ b/time/src/output.rs @@ -2,11 +2,6 @@ use coreutils_core::os::{resource::RUsage, TimeVal}; -#[cfg(target_os = "macos")] -mod macos; -#[cfg(target_os = "macos")] -pub use macos::default_formatter; - #[derive(Debug, PartialEq)] pub enum OutputFormatter { Default, @@ -29,3 +24,7 @@ impl OutputFormatter { } } } + +pub fn default_formatter(_: &RUsage, wall_time: f64, user_time: f64, sys_time: f64) -> String { + format!("{:.2} real {:.2} user {:.2} sys", wall_time, user_time, sys_time) +} diff --git a/time/src/output/macos.rs b/time/src/output/macos.rs deleted file mode 100644 index 026f46d3..00000000 --- a/time/src/output/macos.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Output interface for `time` - -use coreutils_core::os::resource::RUsage; - -pub fn default_formatter(_rusage: &RUsage, wall_time: f64, user_time: f64, sys_time: f64) -> String { - format!(" {:.2} real {:.2} user {:.2} sys", wall_time, user_time, sys_time) -} From c5b4b55f811721d00a65bb99142b83b9a5bc05f2 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 14:41:00 -0500 Subject: [PATCH 18/22] Time: Custom get_rusage() for FuchsiaOS Returns a zeroed RUsage struct --- Fuchsia.toml | 2 +- coreutils_core/src/os/resource.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Fuchsia.toml b/Fuchsia.toml index 3ef963a9..912318d2 100644 --- a/Fuchsia.toml +++ b/Fuchsia.toml @@ -41,7 +41,7 @@ members = [ "sort", "tail", "tee", - "time", + # "time", "touch", "true", "tty", diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs index 6e442af4..455fdcc2 100644 --- a/coreutils_core/src/os/resource.rs +++ b/coreutils_core/src/os/resource.rs @@ -1,7 +1,9 @@ //! Module abstracting interactions with getrusage(2) //! //! Also holds utility functions for summarizing the data returned by getrusage(2) -use libc::{getrusage, rusage, c_int, RUSAGE_SELF, RUSAGE_CHILDREN}; +#[cfg(not(target_os = "fuchsia"))] +use libc::getrusage; +use libc::{rusage, c_int, RUSAGE_SELF, RUSAGE_CHILDREN}; use super::TimeVal; /// Interface for `RUSAGE_*` constants from libc. @@ -97,8 +99,14 @@ impl RUsage { /// Safely wrap `libc::getrusage` pub fn get_rusage(target: ResourceConsumer) -> RUsage { let mut usage: rusage = unsafe { std::mem::zeroed() }; + + #[cfg(not(target_os = "fuchsia"))] + // Fuchsia doesn't have a getrusage syscall, but provides the rusage struct. + // The default is to abort with an error message so that callers don't end + // up with invalid data. unsafe { getrusage(target as c_int, &mut usage); } + RUsage::from(usage) } From cfb73c7f664df814c87c6344303e208f17c91590 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sat, 6 Mar 2021 16:28:11 -0500 Subject: [PATCH 19/22] Time: Format source code --- coreutils_core/src/os.rs | 2 +- coreutils_core/src/os/resource.rs | 29 +++++++++++++---------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/coreutils_core/src/os.rs b/coreutils_core/src/os.rs index ae33263e..0650ad20 100644 --- a/coreutils_core/src/os.rs +++ b/coreutils_core/src/os.rs @@ -3,10 +3,10 @@ pub mod group; pub mod login_name; pub mod passwd; pub mod process; +pub mod resource; pub mod time; pub mod tty; pub mod utsname; -pub mod resource; // Specific Modules #[cfg(not(any(target_os = "fuchsia", target_os = "haiku")))] diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs index 455fdcc2..ff947b3f 100644 --- a/coreutils_core/src/os/resource.rs +++ b/coreutils_core/src/os/resource.rs @@ -1,10 +1,10 @@ //! Module abstracting interactions with getrusage(2) //! //! Also holds utility functions for summarizing the data returned by getrusage(2) +use super::TimeVal; #[cfg(not(target_os = "fuchsia"))] use libc::getrusage; -use libc::{rusage, c_int, RUSAGE_SELF, RUSAGE_CHILDREN}; -use super::TimeVal; +use libc::{c_int, rusage, RUSAGE_CHILDREN, RUSAGE_SELF}; /// Interface for `RUSAGE_*` constants from libc. /// @@ -19,8 +19,8 @@ pub enum ResourceConsumer { #[derive(Debug)] pub struct RUsage { pub timing: Timing, - pub mem: MemoryUsage, - pub io: IOUsage + pub mem: MemoryUsage, + pub io: IOUsage, } #[derive(Debug)] @@ -28,7 +28,7 @@ pub struct Timing { /// User CPU time used pub user_time: TimeVal, /// System CPU time used - pub sys_time: TimeVal, + pub sys_time: TimeVal, } #[derive(Debug)] @@ -56,7 +56,7 @@ pub struct MemoryUsage { #[derive(Debug)] pub struct IOUsage { /// Number of block input operations - pub num_block_in: u64, + pub num_block_in: u64, /// Number of block output operations pub num_block_out: u64, /// Unmaintained on linux: Number of IPC messages recieved @@ -64,17 +64,14 @@ pub struct IOUsage { /// Unmaintained on linux: Number of IPC messages sent pub num_sock_send: u64, /// Unmaintained: Number of signals recieved - pub num_signals: u64, + pub num_signals: u64, } impl RUsage { fn from(ru: rusage) -> Self { RUsage { - timing: Timing { - user_time: ru.ru_utime, - sys_time: ru.ru_stime, - }, - mem: MemoryUsage { + timing: Timing { user_time: ru.ru_utime, sys_time: ru.ru_stime }, + mem: MemoryUsage { max_rss: ru.ru_maxrss as u64, num_minor_page_flt: ru.ru_minflt as u64, num_major_page_flt: ru.ru_majflt as u64, @@ -85,13 +82,13 @@ impl RUsage { unshared_stack_size: ru.ru_isrss as u64, num_swaps: ru.ru_nswap as u64, }, - io: IOUsage { - num_block_in: ru.ru_inblock as u64, + io: IOUsage { + num_block_in: ru.ru_inblock as u64, num_block_out: ru.ru_oublock as u64, num_sock_recv: ru.ru_msgrcv as u64, num_sock_send: ru.ru_msgsnd as u64, - num_signals: ru.ru_nsignals as u64, - } + num_signals: ru.ru_nsignals as u64, + }, } } } From 3776a7c8506ba31e3199b421213bc88dd0f6c196 Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 7 Mar 2021 12:35:56 -0500 Subject: [PATCH 20/22] Core: resource: Implement Conversion trait for rusage -> RUsage --- coreutils_core/src/os/resource.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs index ff947b3f..02fa223e 100644 --- a/coreutils_core/src/os/resource.rs +++ b/coreutils_core/src/os/resource.rs @@ -6,6 +6,8 @@ use super::TimeVal; use libc::getrusage; use libc::{c_int, rusage, RUSAGE_CHILDREN, RUSAGE_SELF}; +use std::convert::From; + /// Interface for `RUSAGE_*` constants from libc. /// /// TODO This is an incomplete set of constants. It is currently missing @@ -67,7 +69,7 @@ pub struct IOUsage { pub num_signals: u64, } -impl RUsage { +impl From for RUsage { fn from(ru: rusage) -> Self { RUsage { timing: Timing { user_time: ru.ru_utime, sys_time: ru.ru_stime }, From aca20a8e62d72af8621af0eac6067b0306d102ab Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 7 Mar 2021 19:51:29 -0500 Subject: [PATCH 21/22] Time: subprocess: Drop benchmark like test This test is truly arbitrary and more of a benchmark than a test --- time/src/subprocess.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/time/src/subprocess.rs b/time/src/subprocess.rs index e4afb1f2..13786f47 100644 --- a/time/src/subprocess.rs +++ b/time/src/subprocess.rs @@ -43,7 +43,7 @@ pub fn timed_run(cmd_vec: &Vec) -> io::Result { #[cfg(test)] mod tests { - use super::{timed_run, Duration}; + use super::timed_run; #[test] fn invalid_command_returns_errno_when_set() { @@ -53,16 +53,4 @@ mod tests { assert!(false, "Subprocess did not fail as expected") } } - - #[test] - fn correct_duration_when_sleeping() { - if let Ok((status, duration)) = timed_run(&vec!["sleep".to_string(), "0.1".to_string()]) { - assert!(status.code() == Some(0)); - assert!(duration > Duration::from_millis(100)); - // TODO Is this a good tolerance for the duration? - assert!(duration < Duration::from_millis(200)); - } else { - assert!(false, "Failed to run command"); - } - } } From 46529e0f93a8dcc2c2229810c026ecc4fbd52e4c Mon Sep 17 00:00:00 2001 From: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com> Date: Sun, 7 Mar 2021 19:53:37 -0500 Subject: [PATCH 22/22] Core: resource: Drop import as std::convert::From is in the Prelude --- coreutils_core/src/os/resource.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/coreutils_core/src/os/resource.rs b/coreutils_core/src/os/resource.rs index 02fa223e..7db63a9f 100644 --- a/coreutils_core/src/os/resource.rs +++ b/coreutils_core/src/os/resource.rs @@ -6,8 +6,6 @@ use super::TimeVal; use libc::getrusage; use libc::{c_int, rusage, RUSAGE_CHILDREN, RUSAGE_SELF}; -use std::convert::From; - /// Interface for `RUSAGE_*` constants from libc. /// /// TODO This is an incomplete set of constants. It is currently missing