Skip to main content

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