cargo/util/
cpu.rs
1use std::io;
2
3pub struct State(imp::State);
4
5impl State {
6 pub fn current() -> io::Result<State> {
12 imp::current().map(State)
13 }
14
15 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 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}