linux_info/
system.rs

1//! get system information (uptime, hostname, os release, load average, usernames, groups).
2
3use crate::util::read_to_string_mut;
4
5use std::{fs, io};
6use std::path::Path;
7use std::time::Duration;
8use std::ops::Sub;
9
10/// Read uptime information from /proc/uptime.
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct Uptime {
13	raw: String
14}
15
16impl Uptime {
17	fn path() -> &'static Path {
18		Path::new("/proc/uptime")
19	}
20
21	#[cfg(test)]
22	fn from_string(raw: String) -> Self {
23		Self {raw}
24	}
25
26	/// Reads uptime from /proc/uptime.
27	pub fn read() -> io::Result<Self> {
28		Ok(Self {
29			raw: fs::read_to_string(Self::path())?
30		})
31	}
32
33	/// Reloads information without allocating.
34	pub fn reload(&mut self) -> io::Result<()> {
35		read_to_string_mut(Self::path(), &mut self.raw)
36	}
37
38	/// Main method to get uptime values. Returns every entry.
39	pub fn all_infos<'a>(&'a self) -> impl Iterator<Item=Duration> + 'a {
40		self.raw.split(' ')
41			.filter_map(|v| v.trim().parse().ok())
42			.map(Duration::from_secs_f64)
43	}
44
45	/// Get the system uptime.
46	pub fn uptime(&self) -> Option<Duration> {
47		self.all_infos().next()
48	}
49
50	/// Get the sum of how much time each core has spent idle.  
51	/// Should be idletime / cores to get the real idle time.
52	pub fn idletime(&self) -> Option<Duration> {
53		self.all_infos().nth(1)
54	}
55}
56
57/// Read the hostname from /proc/sys/kernel/hostname.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct Hostname {
60	raw: String
61}
62
63impl Hostname {
64	fn path() -> &'static Path {
65		Path::new("/proc/sys/kernel/hostname")
66	}
67
68	#[cfg(test)]
69	fn from_string(raw: String) -> Self {
70		Self {raw}
71	}
72
73	/// Reads hostname from /proc/sys/kernel/hostname.
74	pub fn read() -> io::Result<Self> {
75		Ok(Self {
76			raw: fs::read_to_string(Self::path())?
77		})
78	}
79
80	/// Reloads information without allocating.
81	pub fn reload(&mut self) -> io::Result<()> {
82		read_to_string_mut(Self::path(), &mut self.raw)
83	}
84
85	/// Get hostname as str.
86	pub fn hostname(&self) -> &str {
87		self.raw.trim()
88	}
89
90	/// Get hostname as raw String (may contain whitespace).
91	pub fn into_string(self) -> String {
92		self.raw
93	}
94}
95
96/// Read the hostname from /proc/sys/kernel/osrelease.
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct OsRelease {
99	raw: String
100}
101
102impl OsRelease {
103	fn path() -> &'static Path {
104		Path::new("/proc/sys/kernel/osrelease")
105	}
106
107	#[cfg(test)]
108	fn from_string(raw: String) -> Self {
109		Self {raw}
110	}
111
112	/// Reads hostname from /proc/sys/kernel/osrelease.
113	pub fn read() -> io::Result<Self> {
114		Ok(Self {
115			raw: fs::read_to_string(Self::path())?
116		})
117	}
118
119	/// Reloads information without allocating.
120	pub fn reload(&mut self) -> io::Result<()> {
121		read_to_string_mut(Self::path(), &mut self.raw)
122	}
123
124	/// Get os release as str.
125	pub fn full_str(&self) -> &str {
126		self.raw.trim()
127	}
128
129	/// Get os release as raw String (may contain whitespace).
130	pub fn into_string(self) -> String {
131		self.raw
132	}
133}
134
135/// Read the load average from /proc/loadavg.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct LoadAvg {
138	raw: String
139}
140
141impl LoadAvg {
142	fn path() -> &'static Path {
143		Path::new("/proc/loadavg")
144	}
145
146	#[cfg(test)]
147	fn from_string(raw: String) -> Self {
148		Self {raw}
149	}
150
151	/// Read load average from /proc/loadavg.
152	pub fn read() -> io::Result<Self> {
153		Ok(Self {
154			raw: fs::read_to_string(Self::path())?
155		})
156	}
157
158	/// Reloads information without allocating.
159	pub fn reload(&mut self) -> io::Result<()> {
160		read_to_string_mut(Self::path(), &mut self.raw)
161	}
162
163	/// Get all key and values.
164	pub fn values<'a>(&'a self) -> impl Iterator<Item=&'a str> {
165		self.raw.split(' ')
166			.map(str::trim)
167	}
168
169	/// Get the average of jobs in the queue or waiting for disk I/O.  
170	/// The values are averaged over (1 min, 5 min, 15 min).
171	pub fn average(&self) -> Option<(f32, f32, f32)> {
172		let mut vals = self.values()
173			.take(3)
174			.map(|v| v.parse().ok());
175		Some((vals.next()??, vals.next()??, vals.next()??))
176	}
177
178	/// Returns two values (runnable threads, running threads).
179	pub fn threads(&self) -> Option<(usize, usize)> {
180		let mut vals = self.values()
181			.nth(3)?
182			.split('/')
183			.map(|v| v.parse().ok());
184		Some((vals.next()??, vals.next()??))
185	}
186
187	/// Returns the PID of the most recent process.
188	pub fn newest_pid(&self) -> Option<u32> {
189		self.values().last()?
190			.parse().ok()
191	}
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
195pub struct CpuStat {
196	/// user: normal processes executing in user mode
197	pub user: usize,
198	/// nice: niced processes executing in user mode
199	pub nice: usize,
200	/// system: processes executing in kernel mode
201	pub system: usize,
202	/// idle: twiddling thumbs
203	pub idle: usize,
204	/// iowait: waiting for I/O to complete
205	pub iowait: usize,
206	/// irq: servicing interrupts
207	pub irq: usize,
208	/// softirq: servicing softirqs
209	pub softirq: usize
210}
211
212impl CpuStat {
213	// Calculate total time
214	pub fn total_time(&self) -> usize {
215		self.user + self.nice + self.system + self.idle + self.iowait +
216		self.irq + self.softirq
217	}
218
219	// Calculate total active time (excluding idle and iowait)
220	pub fn active_time(&self) -> usize {
221		self.user + self.nice + self.system + self.irq + self.softirq
222	}
223
224	// Calculate CPU usage 0-1
225	//
226	// previous needs to be older
227	pub fn usage(&self, previous: &Self) -> f64 {
228		let diff = *self - *previous;
229
230		if diff.total_time() == 0 {
231			return 0.0;
232		}
233
234		diff.active_time() as f64 / diff.total_time() as f64
235	}
236}
237
238impl Sub for CpuStat {
239	type Output = Self;
240
241	fn sub(self, other: Self) -> Self {
242		Self {
243			user: self.user - other.user,
244			nice: self.nice - other.nice,
245			system: self.system - other.system,
246			idle: self.idle - other.idle,
247			iowait: self.iowait - other.iowait,
248			irq: self.irq - other.irq,
249			softirq: self.softirq - other.softirq,
250		}
251	}
252}
253
254impl FromIterator<usize> for CpuStat {
255	fn from_iter<T>(iter: T) -> Self
256	where T: IntoIterator<Item = usize> {
257		let mut iter = iter.into_iter();
258
259		Self {
260			user: iter.next().unwrap_or(0),
261			nice: iter.next().unwrap_or(0),
262			system: iter.next().unwrap_or(0),
263			idle: iter.next().unwrap_or(0),
264			iowait: iter.next().unwrap_or(0),
265			irq: iter.next().unwrap_or(0),
266			softirq: iter.next().unwrap_or(0)
267		}
268	}
269}
270
271/// Read the load average from /proc/loadavg.
272#[derive(Debug, Clone, PartialEq, Eq)]
273pub struct Stat {
274	raw: String
275}
276
277impl Stat {
278	fn path() -> &'static Path {
279		Path::new("/proc/loadavg")
280	}
281
282	#[cfg(test)]
283	fn from_string(raw: String) -> Self {
284		Self {raw}
285	}
286
287	/// Read load average from /proc/loadavg.
288	pub fn read() -> io::Result<Self> {
289		Ok(Self {
290			raw: fs::read_to_string(Self::path())?
291		})
292	}
293
294	/// Reloads information without allocating.
295	pub fn reload(&mut self) -> io::Result<()> {
296		read_to_string_mut(Self::path(), &mut self.raw)
297	}
298
299	/// Get all key and values.
300	pub fn values<'a>(&'a self) -> impl Iterator<Item=(
301		&'a str,
302		impl Iterator<Item=usize> + '_
303	)> {
304		self.raw.trim().lines()
305			.map(str::trim)
306			.filter_map(|s| {
307				let (key, rest) = s.split_once(' ')?;
308
309				Some((key, rest.split(' ').filter_map(|v| v.parse().ok())))
310			})
311	}
312
313	pub fn cpu(&self) -> Option<CpuStat> {
314		self.values().find(|(k, _)| *k == "cpu")
315			.map(|(_, v)| v.collect())
316	}
317
318	pub fn cpu_nth(&self, nth: usize) -> Option<CpuStat> {
319		let nk = format!("cpu{}", nth);
320		self.values().find(|(k, _)| *k == nk)
321			.map(|(_, v)| v.collect())
322	}
323}
324
325
326// TODO add https://www.idnt.net/en-US/kb/941772
327// /proc/stat
328
329
330#[cfg(test)]
331mod tests {
332	use super::*;
333
334	fn uptime() -> Uptime {
335		Uptime::from_string("220420.83 5275548.45\n".into())
336	}
337
338	#[test]
339	fn uptime_methods() {
340		// uptime
341		assert_eq!(uptime().uptime().unwrap().as_secs(), 220420);
342		// idle time
343		assert_eq!(uptime().idletime().unwrap().as_secs(), 5275548);
344	}
345
346	#[test]
347	fn hostname() {
348		// a useless test
349		let name = Hostname::from_string("test-hostname\n".into());
350		assert_eq!(name.hostname(), "test-hostname");
351	}
352
353	#[test]
354	fn os_release() {
355		// a useless test
356		let name = OsRelease::from_string("test-hostname\n".into());
357		assert_eq!(name.full_str(), "test-hostname");
358	}
359
360	#[test]
361	fn load_avg() {
362		let s = LoadAvg::from_string("13.37 15.82 16.64 14/1444 436826\n".into());
363		assert_eq!(s.average().unwrap(), (13.37, 15.82, 16.64));
364		assert_eq!(s.threads().unwrap(), (14, 1444));
365		assert_eq!(s.newest_pid().unwrap(), 436826);
366	}
367
368	#[test]
369	fn stat() {
370		let first = Stat::from_string("\
371cpu  47500 2396 21138 741776 6759 0 516 0 0 0
372cpu0 1657 25 649 31631 152 0 40 0 0 0
373cpu1 1895 140 624 31335 197 0 9 0 0 0
374cpu2 2155 69 696 31185 101 0 2 0 0 0
375cpu3 2830 72 723 30280 259 0 15 0 0 0
376cpu4 2378 11 776 30813 247 0 1 0 0 0
377cpu5 2402 326 724 30541 193 0 0 0 0 0
378cpu6 1488 13 1217 31159 76 0 1 0 0 0
379cpu7 1537 50 861 31563 111 0 12 0 0 0
380cpu8 2164 22 1279 30611 120 0 0 0 0 0
381cpu9 2760 24 682 30418 292 0 6 0 0 0
382cpu10 2454 440 676 30409 206 0 0 0 0 0
383cpu11 1944 10 709 31251 284 0 0 0 0 0
384cpu12 2050 75 957 30479 634 0 0 0 0 0
385cpu13 1751 180 583 31385 303 0 6 0 0 0
386cpu14 1684 77 753 30998 414 0 162 0 0 0
387cpu15 1922 53 561 31603 73 0 0 0 0 0
388cpu16 2189 75 1108 30151 605 0 36 0 0 0
389cpu17 2113 240 1212 30252 393 0 0 0 0 0
390cpu18 1547 89 1132 30984 346 0 68 0 0 0
391cpu19 2009 87 1479 30265 360 0 7 0 0 0
392cpu20 1832 20 1260 30762 268 0 1 0 0 0
393cpu21 1396 10 669 31952 157 0 0 0 0 0
394cpu22 1466 249 908 30772 567 0 142 0 0 0
395cpu23 1868 33 890 30967 388 0 0 0 0 0
396intr 5968724 39 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1830 0 21 658 0 0 0 0 0 0 0 185 0 206 0 0 0 0 0 0 0 0 0 0 0 0 0 10756 12407 32939 620 3309 8687 22003 1735 1 0 0 0 0 0 0 0 492 0 0 0 0 0 0 0 0 0 0 0 0 23 67418 0 169 169 169 169 0 4770 0 0 0 0 0 0 0 72534 90229 23 36684 79 45360 4 74224 17 64117 72 65789 38 87 25 212 0 0 0 2973 0 3527 0 82311 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
397ctxt 9220606
398btime 1698004999
399processes 10505
400procs_running 3
401procs_blocked 1
402softirq 1572362 6570 73617 6 106501 103799 0 729 724985 18 556137\n\
403		".into());
404
405		assert!(first.cpu_nth(0).is_some());
406		assert_eq!(first.cpu().unwrap(), CpuStat {
407			user: 47500,
408			nice: 2396,
409			system: 21138,
410			idle: 741776,
411			iowait: 6759,
412			irq: 0,
413			softirq: 516
414		});
415
416
417		let second = Stat::from_string("\
418cpu  598326 3695 207316 16449301 11326 0 5035 0 0 0
419cpu0 17756 59 5304 695144 394 0 2671 0 0 0
420cpu1 24815 195 5214 689481 343 0 281 0 0 0
421cpu2 23030 111 5271 691609 188 0 28 0 0 0
422cpu3 37215 147 7633 674968 428 0 23 0 0 0
423cpu4 35260 43 6956 677812 425 0 2 0 0 0
424cpu5 32865 364 7053 679702 371 0 25 0 0 0
425cpu6 15264 65 17016 681953 264 0 2 0 0 0
426cpu7 25513 94 15448 677409 368 0 30 0 0 0
427cpu8 23536 72 16582 678224 276 0 0 0 0 0
428cpu9 27646 68 5548 685186 406 0 1031 0 0 0
429cpu10 27508 495 5536 686719 309 0 0 0 0 0
430cpu11 25780 38 5424 688852 413 0 0 0 0 0
431cpu12 27720 133 5704 686025 849 0 0 0 0 0
432cpu13 25348 288 5167 689086 472 0 10 0 0 0
433cpu14 22885 160 5622 690560 608 0 287 0 0 0
434cpu15 25662 95 6380 688143 248 0 0 0 0 0
435cpu16 24917 118 7501 686875 852 0 106 0 0 0
436cpu17 24053 320 7208 688030 711 0 0 0 0 0
437cpu18 19499 154 16800 681362 768 0 128 0 0 0
438cpu19 21094 126 16548 680076 501 0 12 0 0 0
439cpu20 21863 58 17398 678483 597 0 14 0 0 0
440cpu21 23657 93 5421 691105 246 0 15 0 0 0
441cpu22 22550 310 5334 690909 719 0 358 0 0 0
442cpu23 22883 81 5240 691578 558 0 2 0 0 0
443intr 93982176 39 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 47273 0 21 658 0 0 0 0 0 0 0 462 0 520 0 0 0 0 0 0 0 0 0 0 0 0 0 67446 34716 59371 10575 28561 29891 81562 29376 1 0 0 0 0 0 0 0 718 0 0 0 0 0 0 0 0 0 0 0 0 23 94910 0 3608 3608 3608 3608 0 82604 0 0 0 0 0 0 0 127375 128843 23 60563 802 75923 571 96606 158 104005 128 94386 71 214 204 401 0 0 0 2973 0 5386 0 1757983 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
444ctxt 157231394
445btime 1698004999
446processes 93053
447procs_running 5
448procs_blocked 0
449softirq 19512683 120053 1138489 8 420631 143436 0 10350 10473743 18 7205955\n\
450		".into());
451
452		let first_cpu = first.cpu().unwrap();
453		let second_cpu = second.cpu().unwrap();
454
455		let usage = second_cpu.usage(&first_cpu);
456		assert_eq!(usage, 0.04514286735257322);
457	}
458}