#
cargo/util/
cpu.rs

    1use std::io;
    2
    3pub struct State(imp::State);
    4
    5impl State {
    6    /// Captures the current state of all CPUs on the system.
    7    ///
    8    /// The `State` returned here isn't too meaningful in terms of
    9    /// interpretation across platforms, but it can be compared to previous
    10    /// states to get a meaningful cross-platform number.
    11    pub fn current() -> io::Result<State> {
    12        imp::current().map(State)
    13    }
    14
    15    /// Returns the percentage of time CPUs were idle from the current state
    16    /// relative to the previous state, as a percentage from 0.0 to 100.0.
    17    ///
    18    /// This function will return, as a percentage, the amount of time that the
    19    /// entire system was idle between the `previous` state and this own state.
    20    /// This can be useful to compare two snapshots in time of CPU usage to see
    21    /// how the CPU usage compares between the two.
    22    pub fn idle_since(&self, previous: &State) -> f64 {
    23        imp::pct_idle(&previous.0, &self.0)
    24    }
    25}
    26
    27#[cfg(target_os = "linux")]
    28mod imp {
    29    use std::{fs, io};
    30
    31    pub struct State {
    32        user: u64,
    33        nice: u64,
    34        system: u64,
    35        idle: u64,
    36        iowait: u64,
    37        irq: u64,
    38        softirq: u64,
    39        steal: u64,
    40        guest: u64,
    41        guest_nice: u64,
    42    }
    43
    44    pub fn current() -> io::Result<State> {
    45        let state = fs::read_to_string("/proc/stat")?;
    46
    47        (|| {
    48            let mut parts = state.lines().next()?.split_whitespace();
    49            if parts.next()? != "cpu" {
    50                return None;
    51            }
    52            Some(State {
    53                user: parts.next()?.parse::<u64>().ok()?,
    54                nice: parts.next()?.parse::<u64>().ok()?,
    55                system: parts.next()?.parse::<u64>().ok()?,
    56                idle: parts.next()?.parse::<u64>().ok()?,
    57                iowait: parts.next()?.parse::<u64>().ok()?,
    58                irq: parts.next()?.parse::<u64>().ok()?,
    59                softirq: parts.next()?.parse::<u64>().ok()?,
    60                steal: parts.next()?.parse::<u64>().ok()?,
    61                guest: parts.next()?.parse::<u64>().ok()?,
    62                guest_nice: parts.next()?.parse::<u64>().ok()?,
    63            })
    64        })()
    65        .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "first line of /proc/stat malformed"))
    66    }
    67
    68    pub fn pct_idle(prev: &State, next: &State) -> f64 {
    69        let user = next.user - prev.user;
    70        let nice = next.nice - prev.nice;
    71        let system = next.system - prev.system;
    72        let idle = next.idle - prev.idle;
    73        let iowait = next.iowait.saturating_sub(prev.iowait);
    74        let irq = next.irq - prev.irq;
    75        let softirq = next.softirq - prev.softirq;
    76        let steal = next.steal - prev.steal;
    77        let guest = next.guest - prev.guest;
    78        let guest_nice = next.guest_nice - prev.guest_nice;
    79        let total =
    80            user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice;
    81
    82        (idle as f64) / (total as f64) * 100.0
    83    }
    84}
    85
    86#[cfg(target_os = "macos")]
    87#[allow(bad_style)]
    88mod imp {
    89    use std::io;
    90    use std::ptr;
    91
    92    type host_t = u32;
    93    type mach_port_t = u32;
    94    type vm_map_t = mach_port_t;
    95    type vm_offset_t = usize;
    96    type vm_size_t = usize;
    97    type vm_address_t = vm_offset_t;
    98    type processor_flavor_t = i32;
    99    type natural_t = u32;
    100    type processor_info_array_t = *mut i32;
    101    type mach_msg_type_number_t = i32;
    102    type kern_return_t = i32;
    103
    104    const PROESSOR_CPU_LOAD_INFO: processor_flavor_t = 2;
    105    const CPU_STATE_USER: usize = 0;
    106    const CPU_STATE_SYSTEM: usize = 1;
    107    const CPU_STATE_IDLE: usize = 2;
    108    const CPU_STATE_NICE: usize = 3;
    109    const CPU_STATE_MAX: usize = 4;
    110
    111    unsafe extern "C" {
    112        static mut mach_task_self_: mach_port_t;
    113
    114        fn mach_host_self() -> mach_port_t;
    115        fn host_processor_info(
    116            host: host_t,
    117            flavor: processor_flavor_t,
    118            out_processor_count: *mut natural_t,
    119            out_processor_info: *mut processor_info_array_t,
    120            out_processor_infoCnt: *mut mach_msg_type_number_t,
    121        ) -> kern_return_t;
    122        fn vm_deallocate(
    123            target_task: vm_map_t,
    124            address: vm_address_t,
    125            size: vm_size_t,
    126        ) -> kern_return_t;
    127    }
    128
    129    pub struct State {
    130        user: u64,
    131        system: u64,
    132        idle: u64,
    133        nice: u64,
    134    }
    135
    136    #[repr(C)]
    137    struct processor_cpu_load_info_data_t {
    138        cpu_ticks: [u32; CPU_STATE_MAX],
    139    }
    140
    141    pub fn current() -> io::Result<State> {
    142        // There's scant little documentation on `host_processor_info`
    143        // throughout the internet, so this is just modeled after what everyone
    144        // else is doing. For now this is modeled largely after libuv.
    145
    146        unsafe {
    147            let mut num_cpus_u = 0;
    148            let mut cpu_info = ptr::null_mut();
    149            let mut msg_type = 0;
    150            let err = host_processor_info(
    151                mach_host_self(),
    152                PROESSOR_CPU_LOAD_INFO,
    153                &mut num_cpus_u,
    154                &mut cpu_info,
    155                &mut msg_type,
    156            );
    157            if err != 0 {
    158                return Err(io::Error::last_os_error());
    159            }
    160            let mut ret = State {
    161                user: 0,
    162                system: 0,
    163                idle: 0,
    164                nice: 0,
    165            };
    166            let mut current = cpu_info as *const processor_cpu_load_info_data_t;
    167            for _ in 0..num_cpus_u {
    168                ret.user += (*current).cpu_ticks[CPU_STATE_USER] as u64;
    169                ret.system += (*current).cpu_ticks[CPU_STATE_SYSTEM] as u64;
    170                ret.idle += (*current).cpu_ticks[CPU_STATE_IDLE] as u64;
    171                ret.nice += (*current).cpu_ticks[CPU_STATE_NICE] as u64;
    172                current = current.offset(1);
    173            }
    174            vm_deallocate(mach_task_self_, cpu_info as vm_address_t, msg_type as usize);
    175            Ok(ret)
    176        }
    177    }
    178
    179    pub fn pct_idle(prev: &State, next: &State) -> f64 {
    180        let user = next.user - prev.user;
    181        let system = next.system - prev.system;
    182        let idle = next.idle - prev.idle;
    183        let nice = next.nice - prev.nice;
    184        let total = user + system + idle + nice;
    185        (idle as f64) / (total as f64) * 100.0
    186    }
    187}
    188
    189#[cfg(windows)]
    190mod imp {
    191    use std::io;
    192    use std::mem;
    193
    194    use windows_sys::Win32::Foundation::FILETIME;
    195    use windows_sys::Win32::System::Threading::GetSystemTimes;
    196
    197    pub struct State {
    198        idle: FILETIME,
    199        kernel: FILETIME,
    200        user: FILETIME,
    201    }
    202
    203    pub fn current() -> io::Result<State> {
    204        unsafe {
    205            let mut ret = mem::zeroed::<State>();
    206            let r = GetSystemTimes(&mut ret.idle, &mut ret.kernel, &mut ret.user);
    207            if r != 0 {
    208                Ok(ret)
    209            } else {
    210                Err(io::Error::last_os_error())
    211            }
    212        }
    213    }
    214
    215    pub fn pct_idle(prev: &State, next: &State) -> f64 {
    216        fn to_u64(a: &FILETIME) -> u64 {
    217            ((a.dwHighDateTime as u64) << 32) | (a.dwLowDateTime as u64)
    218        }
    219
    220        let idle = to_u64(&next.idle) - to_u64(&prev.idle);
    221        let kernel = to_u64(&next.kernel) - to_u64(&prev.kernel);
    222        let user = to_u64(&next.user) - to_u64(&prev.user);
    223        let total = user + kernel;
    224        (idle as f64) / (total as f64) * 100.0
    225    }
    226}
    227
    228#[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
    229mod imp {
    230    use std::io;
    231
    232    pub struct State;
    233
    234    pub fn current() -> io::Result<State> {
    235        Err(io::Error::new(
    236            io::ErrorKind::Other,
    237            "unsupported platform to learn CPU state",
    238        ))
    239    }
    240
    241    pub fn pct_idle(_prev: &State, _next: &State) -> f64 {
    242        unimplemented!()
    243    }
    244}