mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-26 16:01:15 -03:00
cleanup: use nix version of getrusage
Change the behavior when `getrusage` fails. Previously, failure was masked by using 0 values for everything. This is misleading. Instead, we now panic on such failures, because they should never occur with our usage of the function. Closes #12502
This commit is contained in:
committed by
Johannes Altmanninger
parent
0223edc639
commit
310eba7156
@@ -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());
|
||||
|
||||
57
src/nix.rs
57
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
src/timer.rs
58
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#"
|
||||
|
||||
Reference in New Issue
Block a user