Skip to main content

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