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