diff --git a/src/bin/fish.rs b/src/bin/fish.rs index ac5369db2..daa156bfa 100644 --- a/src/bin/fish.rs +++ b/src/bin/fish.rs @@ -42,7 +42,7 @@ history::{self, start_private_mode}, io::IoChain, locale::set_libc_locales, - nix::{RUsage, getrusage, isatty}, + nix::isatty, panic::panic_handler, parse_constants::{ParseErrorList, ParseTreeFlags}, parse_tree::ParsedSource, @@ -63,7 +63,10 @@ }; use fish_wcstringutil::wcs2bytes; use libc::STDIN_FILENO; -use nix::unistd::{AccessFlags, getpid}; +use nix::{ + sys::resource::{UsageWho, getrusage}, + unistd::{AccessFlags, getpid}, +}; use std::ffi::{OsStr, OsString}; use std::fs::File; use std::os::unix::prelude::*; @@ -102,28 +105,30 @@ struct FishCmdOpts { /// Return a timeval converted to milliseconds. #[allow(clippy::unnecessary_cast)] -fn tv_to_msec(tv: &libc::timeval) -> i64 { +fn nix_tv_to_ms(tv: nix::sys::time::TimeVal) -> i64 { // milliseconds per second - let mut msec = tv.tv_sec as i64 * 1000; + let mut ms = tv.tv_sec() as i64 * 1000; // microseconds per millisecond - msec += tv.tv_usec as i64 / 1000; - msec + ms += tv.tv_usec() as i64 / 1000; + ms } fn print_rusage_self() { - let rs = getrusage(RUsage::RSelf); + // `getrusage` should never fail with this usage. + // If it does, it suggests a non-POSIX-compliant OS. + let usage = getrusage(UsageWho::RUSAGE_SELF).unwrap(); let rss_kb = if cfg!(apple) { - // mac use bytes. - rs.ru_maxrss / 1024 + // Macs use bytes, + // Source: commit b6555a0dc462f669e1b5a370c3efae0f5948a1ef + // The docs at https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html say otherwise. + usage.max_rss() / 1024 } else { - // Everyone else uses KB. - rs.ru_maxrss + usage.max_rss() }; - - let user_time = tv_to_msec(&rs.ru_utime); - let sys_time = tv_to_msec(&rs.ru_stime); + let user_time = nix_tv_to_ms(usage.user_time()); + let sys_time = nix_tv_to_ms(usage.system_time()); let total_time = user_time + sys_time; - let signals = rs.ru_nsignals; + let signals = usage.signals(); eprintf!(" rusage self:\n"); eprintf!(" user time: %s ms\n", sys_time.to_string()); diff --git a/src/nix.rs b/src/nix.rs index 8268e855c..f3957aa9f 100644 --- a/src/nix.rs +++ b/src/nix.rs @@ -1,64 +1,7 @@ //! Safe wrappers around various libc functions that we might want to reuse across modules. -use fish_util::perror; -use std::time::Duration; - -#[allow(clippy::unnecessary_cast)] -pub const fn timeval_to_duration(val: &libc::timeval) -> Duration { - let micros = val.tv_sec as i64 * (1E6 as i64) + val.tv_usec as i64; - Duration::from_micros(micros as u64) -} - -pub trait TimevalExt { - fn as_micros(&self) -> i64; - fn as_duration(&self) -> Duration; -} - -impl TimevalExt for libc::timeval { - fn as_micros(&self) -> i64 { - timeval_to_duration(self).as_micros() as i64 - } - - fn as_duration(&self) -> Duration { - timeval_to_duration(self) - } -} - pub fn isatty(fd: i32) -> bool { // This returns false if the fd is valid but not a tty, or is invalid. // No place we currently call it really cares about the difference. (unsafe { libc::isatty(fd) }) == 1 } - -/// An enumeration of supported libc rusage types used by [`getrusage()`]. -/// NB: RUSAGE_THREAD is not supported on macOS. -pub enum RUsage { - RSelf, // "Self" is a reserved keyword - RChildren, -} - -/// A safe wrapper around `libc::getrusage()`. -pub fn getrusage(resource: RUsage) -> libc::rusage { - let mut rusage = std::mem::MaybeUninit::uninit(); - let result = unsafe { - libc::getrusage( - match resource { - RUsage::RSelf => libc::RUSAGE_SELF, - RUsage::RChildren => libc::RUSAGE_CHILDREN, - }, - rusage.as_mut_ptr(), - ) - }; - - // getrusage(2) says the syscall can only fail if the dest address is invalid (EFAULT) or if the - // requested resource type is invalid. Since we're in control of both, we can assume it won't - // fail. In case it does anyway (e.g. OS where the syscall isn't implemented), we can just - // return an empty value. - match result { - 0 => unsafe { rusage.assume_init() }, - _ => { - perror("getrusage"); - unsafe { std::mem::zeroed() } - } - } -} diff --git a/src/timer.rs b/src/timer.rs index dcb87cebb..7236a64a8 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -14,12 +14,12 @@ //! but it's still the best we can do because we don't know how long of a time might elapse between //! `TimerSnapshot` instances and need to avoid rollover. +use nix::sys::resource::{Usage, UsageWho, getrusage}; +use nix::sys::time::TimeVal; use std::fmt::Write as _; use std::io::Write as _; use std::time::{Duration, Instant}; -use crate::nix::{RUsage, getrusage}; - enum Unit { Minutes, Seconds, @@ -29,8 +29,8 @@ enum Unit { struct TimerSnapshot { wall_time: Instant, - cpu_fish: libc::rusage, - cpu_children: libc::rusage, + cpu_fish: Usage, + cpu_children: Usage, } /// Create a `TimerSnapshot` and return a `PrintElapsedOnDrop` object that will print upon @@ -44,8 +44,11 @@ pub fn push_timer() -> PrintElapsedOnDrop { impl TimerSnapshot { pub fn take() -> TimerSnapshot { TimerSnapshot { - cpu_fish: getrusage(RUsage::RSelf), - cpu_children: getrusage(RUsage::RChildren), + // getrusage should never fail. + // POSIX rusage getrusage only fails if the who value is invalid. Both `RUSAGE_SELF` and + // `RUSAGE_CHILDREN` are valid, so this should never fail. + cpu_fish: getrusage(UsageWho::RUSAGE_SELF).unwrap(), + cpu_children: getrusage(UsageWho::RUSAGE_CHILDREN).unwrap(), wall_time: Instant::now(), } } @@ -54,12 +57,17 @@ pub fn take() -> TimerSnapshot { /// instances. The returned string can take one of two formats, depending on the value of the /// `verbose` parameter. pub fn get_delta(t1: &TimerSnapshot, t2: &TimerSnapshot, verbose: bool) -> String { - use crate::nix::timeval_to_duration as from; + #[allow(clippy::unnecessary_cast)] + const fn from(val: TimeVal) -> Duration { + let micros = val.tv_sec() as i64 * 1_000_000 + val.tv_usec() as i64; + Duration::from_micros(micros as u64) + } - let mut fish_sys = from(&t2.cpu_fish.ru_stime) - from(&t1.cpu_fish.ru_stime); - let mut fish_usr = from(&t2.cpu_fish.ru_utime) - from(&t1.cpu_fish.ru_utime); - let mut child_sys = from(&t2.cpu_children.ru_stime) - from(&t1.cpu_children.ru_stime); - let mut child_usr = from(&t2.cpu_children.ru_utime) - from(&t1.cpu_children.ru_utime); + let mut fish_sys = from(t2.cpu_fish.system_time()) - from(t1.cpu_fish.system_time()); + let mut fish_usr = from(t2.cpu_fish.user_time()) - from(t1.cpu_fish.user_time()); + let mut child_sys = + from(t2.cpu_children.system_time()) - from(t1.cpu_children.system_time()); + let mut child_usr = from(t2.cpu_children.user_time()) - from(t1.cpu_children.user_time()); // The result from getrusage is not necessarily realtime, it may be cached from a few // microseconds ago. In the event that execution completes extremely quickly or there is @@ -192,16 +200,28 @@ mod tests { #[test] fn timer_format_and_alignment() { let mut t1 = TimerSnapshot::take(); - t1.cpu_fish.ru_utime.tv_usec = 0; - t1.cpu_fish.ru_stime.tv_usec = 0; - t1.cpu_children.ru_utime.tv_usec = 0; - t1.cpu_children.ru_stime.tv_usec = 0; + { + let t1_fish = t1.cpu_fish.as_mut(); + t1_fish.ru_utime.tv_usec = 0; + t1_fish.ru_stime.tv_usec = 0; + } + { + let t1_children = t1.cpu_children.as_mut(); + t1_children.ru_utime.tv_usec = 0; + t1_children.ru_stime.tv_usec = 0; + } let mut t2 = TimerSnapshot::take(); - t2.cpu_fish.ru_utime.tv_usec = 999995; - t2.cpu_fish.ru_stime.tv_usec = 999994; - t2.cpu_children.ru_utime.tv_usec = 1000; - t2.cpu_children.ru_stime.tv_usec = 500; + { + let t2_fish = t2.cpu_fish.as_mut(); + t2_fish.ru_utime.tv_usec = 999995; + t2_fish.ru_stime.tv_usec = 999994; + } + { + let t2_children = t2.cpu_children.as_mut(); + t2_children.ru_utime.tv_usec = 1000; + t2_children.ru_stime.tv_usec = 500; + } t2.wall_time = t1.wall_time + Duration::from_micros(500); let expected = r#"