-
-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
141: Implement `time` r=GrayJack a=envp This PR will track building `time` Partially fixes #53 Co-authored-by: Vaibhav Yenamandra <3663231+envp@users.noreply.github.com>
- Loading branch information
Showing
20 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
# "time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ members = [ | |
"sort", | ||
"tail", | ||
"tee", | ||
"time", | ||
"touch", | ||
"true", | ||
"tty", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
//! 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::{c_int, rusage, RUSAGE_CHILDREN, RUSAGE_SELF}; | ||
|
||
/// 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 { | ||
pub timing: Timing, | ||
pub mem: MemoryUsage, | ||
pub 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 From<rusage> for 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() }; | ||
|
||
#[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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "time" | ||
version = "0.1.0" | ||
authors = ["Vaibhav Yenamandra <v@calloc.net>"] | ||
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"] } | ||
coreutils_core = { path = "../coreutils_core" } | ||
|
||
[build-dependencies] | ||
clap = { version = "^2.33.0" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use clap::{ | ||
crate_authors, crate_description, crate_name, crate_version, App, AppSettings::ColoredHelp, Arg, | ||
}; | ||
|
||
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]); | ||
|
||
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") | ||
.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 <COMMAND>.").multiple(true); | ||
|
||
app.args(&[posix_fmt, command, arguments]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
//! Command line options that are supported by `time` | ||
|
||
use crate::{cli::create_app, 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<String>, | ||
} | ||
|
||
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::{create_app, OutputFormatter, TimeOpts}; | ||
|
||
#[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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
mod cli; | ||
mod flags; | ||
mod output; | ||
mod subprocess; | ||
|
||
use coreutils_core::os::resource::{get_rusage, ResourceConsumer}; | ||
|
||
fn main() { | ||
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), | ||
}; | ||
|
||
let usage = get_rusage(ResourceConsumer::Children); | ||
|
||
eprintln!("{}", opts.printer.format_stats(&usage, &duration)); | ||
std::process::exit(exit_status.code().unwrap_or(1)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
//! 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(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) | ||
}, | ||
} | ||
} | ||
} | ||
|
||
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) | ||
} |
Oops, something went wrong.