| use std::io; | |
| pub struct State(imp::State); | |
| impl State { | |
| /// Captures the current state of all CPUs on the system. | |
| /// | |
| /// The `State` returned here isn't too meaningful in terms of | |
| /// interpretation across platforms, but it can be compared to previous | |
| /// states to get a meaningful cross-platform number. | |
| pub fn current() -> io::Result<State> { | |
| imp::current().map(State) | |
| } | |
| /// Returns the percentage of time CPUs were idle from the current state | |
| /// relative to the previous state, as a percentage from 0.0 to 100.0. | |
| /// | |
| /// This function will return, as a percentage, the amount of time that the | |
| /// entire system was idle between the `previous` state and this own state. | |
| /// This can be useful to compare two snapshots in time of CPU usage to see | |
| /// how the CPU usage compares between the two. | |
| pub fn idle_since(&self, previous: &State) -> f64 { | |
| imp::pct_idle(&previous.0, &self.0) | |
| } | |
| } | |
| #[cfg(target_os = "linux")] | |
| mod imp { | |
| use std::{fs, io}; | |
| pub struct State { | |
| user: u64, | |
| nice: u64, | |
| system: u64, | |
| idle: u64, | |
| iowait: u64, | |
| irq: u64, | |
| softirq: u64, | |
| steal: u64, | |
| guest: u64, | |
| guest_nice: u64, | |
| } | |
| pub fn current() -> io::Result<State> { | |
| let state = fs::read_to_string("/proc/stat")?; | |
| (|| { | |
| let mut parts = state.lines().next()?.split_whitespace(); | |
| if parts.next()? != "cpu" { | |
| return None; | |
| } | |
| Some(State { | |
| user: parts.next()?.parse::<u64>().ok()?, | |
| nice: parts.next()?.parse::<u64>().ok()?, | |
| system: parts.next()?.parse::<u64>().ok()?, | |
| idle: parts.next()?.parse::<u64>().ok()?, | |
| iowait: parts.next()?.parse::<u64>().ok()?, | |
| irq: parts.next()?.parse::<u64>().ok()?, | |
| softirq: parts.next()?.parse::<u64>().ok()?, | |
| steal: parts.next()?.parse::<u64>().ok()?, | |
| guest: parts.next()?.parse::<u64>().ok()?, | |
| guest_nice: parts.next()?.parse::<u64>().ok()?, | |
| }) | |
| })() | |
| .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "first line of /proc/stat malformed")) | |
| } | |
| pub fn pct_idle(prev: &State, next: &State) -> f64 { | |
| let user = next.user - prev.user; | |
| let nice = next.nice - prev.nice; | |
| let system = next.system - prev.system; | |
| let idle = next.idle - prev.idle; | |
| let iowait = next.iowait.saturating_sub(prev.iowait); | |
| let irq = next.irq - prev.irq; | |
| let softirq = next.softirq - prev.softirq; | |
| let steal = next.steal - prev.steal; | |
| let guest = next.guest - prev.guest; | |
| let guest_nice = next.guest_nice - prev.guest_nice; | |
| let total = | |
| user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice; | |
| (idle as f64) / (total as f64) * 100.0 | |
| } | |
| } | |
| #[cfg(target_os = "macos")] | |
| #[allow(bad_style)] | |
| mod imp { | |
| use std::io; | |
| use std::ptr; | |
| type host_t = u32; | |
| type mach_port_t = u32; | |
| type vm_map_t = mach_port_t; | |
| type vm_offset_t = usize; | |
| type vm_size_t = usize; | |
| type vm_address_t = vm_offset_t; | |
| type processor_flavor_t = i32; | |
| type natural_t = u32; | |
| type processor_info_array_t = *mut i32; | |
| type mach_msg_type_number_t = i32; | |
| type kern_return_t = i32; | |
| const PROCESSOR_CPU_LOAD_INFO: processor_flavor_t = 2; | |
| const CPU_STATE_USER: usize = 0; | |
| const CPU_STATE_SYSTEM: usize = 1; | |
| const CPU_STATE_IDLE: usize = 2; | |
| const CPU_STATE_NICE: usize = 3; | |
| const CPU_STATE_MAX: usize = 4; | |
| unsafe extern "C" { | |
| static mut mach_task_self_: mach_port_t; | |
| fn mach_host_self() -> mach_port_t; | |
| fn host_processor_info( | |
| host: host_t, | |
| flavor: processor_flavor_t, | |
| out_processor_count: *mut natural_t, | |
| out_processor_info: *mut processor_info_array_t, | |
| out_processor_infoCnt: *mut mach_msg_type_number_t, | |
| ) -> kern_return_t; | |
| fn vm_deallocate( | |
| target_task: vm_map_t, | |
| address: vm_address_t, | |
| size: vm_size_t, | |
| ) -> kern_return_t; | |
| } | |
| pub struct State { | |
| user: u64, | |
| system: u64, | |
| idle: u64, | |
| nice: u64, | |
| } | |
| #[repr(C)] | |
| struct processor_cpu_load_info_data_t { | |
| cpu_ticks: [u32; CPU_STATE_MAX], | |
| } | |
| pub fn current() -> io::Result<State> { | |
| // There's scant little documentation on `host_processor_info` | |
| // throughout the internet, so this is just modeled after what everyone | |
| // else is doing. For now this is modeled largely after libuv. | |
| unsafe { | |
| let mut num_cpus_u = 0; | |
| let mut cpu_info = ptr::null_mut(); | |
| let mut msg_type = 0; | |
| let err = host_processor_info( | |
| mach_host_self(), | |
| PROCESSOR_CPU_LOAD_INFO, | |
| &mut num_cpus_u, | |
| &mut cpu_info, | |
| &mut msg_type, | |
| ); | |
| if err != 0 { | |
| return Err(io::Error::last_os_error()); | |
| } | |
| let mut ret = State { | |
| user: 0, | |
| system: 0, | |
| idle: 0, | |
| nice: 0, | |
| }; | |
| let mut current = cpu_info as *const processor_cpu_load_info_data_t; | |
| for _ in 0..num_cpus_u { | |
| ret.user += (*current).cpu_ticks[CPU_STATE_USER] as u64; | |
| ret.system += (*current).cpu_ticks[CPU_STATE_SYSTEM] as u64; | |
| ret.idle += (*current).cpu_ticks[CPU_STATE_IDLE] as u64; | |
| ret.nice += (*current).cpu_ticks[CPU_STATE_NICE] as u64; | |
| current = current.offset(1); | |
| } | |
| vm_deallocate(mach_task_self_, cpu_info as vm_address_t, msg_type as usize); | |
| Ok(ret) | |
| } | |
| } | |
| pub fn pct_idle(prev: &State, next: &State) -> f64 { | |
| let user = next.user - prev.user; | |
| let system = next.system - prev.system; | |
| let idle = next.idle - prev.idle; | |
| let nice = next.nice - prev.nice; | |
| let total = user + system + idle + nice; | |
| (idle as f64) / (total as f64) * 100.0 | |
| } | |
| } | |
| #[cfg(windows)] | |
| mod imp { | |
| use std::io; | |
| use std::mem; | |
| use windows_sys::Win32::Foundation::FILETIME; | |
| use windows_sys::Win32::System::Threading::GetSystemTimes; | |
| pub struct State { | |
| idle: FILETIME, | |
| kernel: FILETIME, | |
| user: FILETIME, | |
| } | |
| pub fn current() -> io::Result<State> { | |
| unsafe { | |
| let mut ret = mem::zeroed::<State>(); | |
| let r = GetSystemTimes(&mut ret.idle, &mut ret.kernel, &mut ret.user); | |
| if r != 0 { | |
| Ok(ret) | |
| } else { | |
| Err(io::Error::last_os_error()) | |
| } | |
| } | |
| } | |
| pub fn pct_idle(prev: &State, next: &State) -> f64 { | |
| fn to_u64(a: &FILETIME) -> u64 { | |
| ((a.dwHighDateTime as u64) << 32) | (a.dwLowDateTime as u64) | |
| } | |
| let idle = to_u64(&next.idle) - to_u64(&prev.idle); | |
| let kernel = to_u64(&next.kernel) - to_u64(&prev.kernel); | |
| let user = to_u64(&next.user) - to_u64(&prev.user); | |
| let total = user + kernel; | |
| (idle as f64) / (total as f64) * 100.0 | |
| } | |
| } | |
| #[cfg(not(any(target_os = "linux", target_os = "macos", windows)))] | |
| mod imp { | |
| use std::io; | |
| pub struct State; | |
| pub fn current() -> io::Result<State> { | |
| Err(io::Error::new( | |
| io::ErrorKind::Other, | |
| "unsupported platform to learn CPU state", | |
| )) | |
| } | |
| pub fn pct_idle(_prev: &State, _next: &State) -> f64 { | |
| unimplemented!() | |
| } | |
| } |