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    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}