1 // Don't throw clippy warnings for manual string stripping.
2 // The suggested fix with `strip_prefix` removes support for Rust 1.33 and 1.38
3 #![allow(clippy::unknown_clippy_lints)]
4 #![allow(clippy::manual_strip)]
5 // Don't throw rustc lint warnings for the deprecated name `intra_doc_link_resolution_failure`.
6 // The suggested rename to `broken_intra_doc_links` removes support for Rust 1.33 and 1.38.
7 #![allow(renamed_and_removed_lints)]
8 #![deny(intra_doc_link_resolution_failure)]
9 //! This crate provides to an interface into the linux `procfs` filesystem, usually mounted at
10 //! `/proc`.
11 //!
12 //! This is a pseudo-filesystem which is available on most every linux system and provides an
13 //! interface to kernel data structures.
14 //!
15 //!
16 //! # Kernel support
17 //!
18 //! Not all fields/data are available in each kernel.  Some fields were added in specific kernel
19 //! releases, and other fields are only present in certain kernel configuration options are
20 //! enabled.  These are represented as `Option` fields in this crate.
21 //!
22 //! This crate aims to support all 2.6 kernels (and newer).  WSL2 is also supported.
23 //!
24 //! # Documentation
25 //!
26 //! In almost all cases, the documentation is taken from the
27 //! [`proc.5`](http://man7.org/linux/man-pages/man5/proc.5.html) manual page.  This means that
28 //! sometimes the style of writing is not very "rusty", or may do things like reference related files
29 //! (instead of referencing related structs).  Contributions to improve this are welcome.
30 //!
31 //! # Panicing
32 //!
33 //! While previous versions of the library could panic, this current version aims to be panic-free
34 //! in a many situations as possible.  Whenever the procfs crate encounters a bug in its own
35 //! parsing code, it will return an [`InternalError`](enum.ProcError.html#variant.InternalError) error.  This should be considered a
36 //! bug and should be [reported](https://github.com/eminence/procfs).  If you encounter a panic,
37 //! please report that as well.
38 //!
39 //! # Cargo features
40 //!
41 //! The following cargo features are available:
42 //!
43 //! * `chrono` -- Default.  Optional.  This feature enables a few methods that return values as `DateTime` objects.
44 //! * `backtrace` -- Optional.  This feature lets you get a stack trace whenever an `InternalError` is raised.
45 //!
46 //! # Examples
47 //!
48 //! Examples can be found in the various modules shown below, or in the
49 //! [examples](https://github.com/eminence/procfs/tree/master/examples) folder of the code repository.
50 //!
51 
52 use bitflags::bitflags;
53 use lazy_static::lazy_static;
54 use libc::pid_t;
55 use libc::sysconf;
56 use libc::{_SC_CLK_TCK, _SC_PAGESIZE};
57 
58 use std::ffi::CStr;
59 use std::fmt;
60 use std::fs::File;
61 use std::io::{self, BufRead, BufReader, Read, Write};
62 use std::mem;
63 use std::os::raw::c_char;
64 use std::path::{Path, PathBuf};
65 use std::str::FromStr;
66 use std::{collections::HashMap, time::Duration};
67 
68 #[cfg(feature = "chrono")]
69 use chrono::{DateTime, Local};
70 
71 const PROC_CONFIG_GZ: &str = "/proc/config.gz";
72 const BOOT_CONFIG: &str = "/boot/config";
73 
74 trait IntoOption<T> {
into_option(t: Self) -> Option<T>75     fn into_option(t: Self) -> Option<T>;
76 }
77 
78 impl<T> IntoOption<T> for Option<T> {
into_option(t: Option<T>) -> Option<T>79     fn into_option(t: Option<T>) -> Option<T> {
80         t
81     }
82 }
83 
84 impl<T, R> IntoOption<T> for Result<T, R> {
into_option(t: Result<T, R>) -> Option<T>85     fn into_option(t: Result<T, R>) -> Option<T> {
86         t.ok()
87     }
88 }
89 
90 pub(crate) trait IntoResult<T, E> {
into(t: Self) -> Result<T, E>91     fn into(t: Self) -> Result<T, E>;
92 }
93 
94 macro_rules! build_internal_error {
95     ($err: expr) => {
96         crate::ProcError::InternalError(crate::InternalError {
97             msg: format!("Internal Unwrap Error: {}", $err),
98             file: file!(),
99             line: line!(),
100             #[cfg(feature = "backtrace")]
101             backtrace: backtrace::Backtrace::new(),
102         })
103     };
104     ($err: expr, $msg: expr) => {
105         crate::ProcError::InternalError(crate::InternalError {
106             msg: format!("Internal Unwrap Error: {}: {}", $msg, $err),
107             file: file!(),
108             line: line!(),
109             #[cfg(feature = "backtrace")]
110             backtrace: backtrace::Backtrace::new(),
111         })
112     };
113 }
114 
115 // custom NoneError, since std::option::NoneError is nightly-only
116 // See https://github.com/rust-lang/rust/issues/42327
117 struct NoneError;
118 
119 impl std::fmt::Display for NoneError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result120     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121         write!(f, "NoneError")
122     }
123 }
124 
125 impl<T> IntoResult<T, NoneError> for Option<T> {
into(t: Option<T>) -> Result<T, NoneError>126     fn into(t: Option<T>) -> Result<T, NoneError> {
127         t.ok_or(NoneError)
128     }
129 }
130 
131 impl<T, E> IntoResult<T, E> for Result<T, E> {
into(t: Result<T, E>) -> Result<T, E>132     fn into(t: Result<T, E>) -> Result<T, E> {
133         t
134     }
135 }
136 
137 #[macro_use]
138 #[allow(unused_macros)]
139 macro_rules! proc_panic {
140     ($e:expr) => {
141         crate::IntoOption::into_option($e).unwrap_or_else(|| {
142             panic!(
143                 "Failed to unwrap {}. Please report this as a procfs bug.",
144                 stringify!($e)
145             )
146         })
147     };
148     ($e:expr, $msg:expr) => {
149         crate::IntoOption::into_option($e).unwrap_or_else(|| {
150             panic!(
151                 "Failed to unwrap {} ({}). Please report this as a procfs bug.",
152                 stringify!($e),
153                 $msg
154             )
155         })
156     };
157 }
158 
159 macro_rules! expect {
160     ($e:expr) => {
161         match crate::IntoResult::into($e) {
162             Ok(v) => v,
163             Err(e) => return Err(build_internal_error!(e)),
164         }
165     };
166     ($e:expr, $msg:expr) => {
167         match crate::IntoResult::into($e) {
168             Ok(v) => v,
169             Err(e) => return Err(build_internal_error!(e, $msg)),
170         }
171     };
172 }
173 
174 #[macro_use]
175 macro_rules! from_str {
176     ($t:tt, $e:expr) => {{
177         let e = $e;
178         expect!(
179             $t::from_str_radix(e, 10),
180             format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t),)
181         )
182     }};
183     ($t:tt, $e:expr, $radix:expr) => {{
184         let e = $e;
185         expect!(
186             $t::from_str_radix(e, $radix),
187             format!("Failed to parse {} ({:?}) as a {}", stringify!($e), e, stringify!($t))
188         )
189     }};
190     ($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{
191         let e = $e;
192         expect!(
193             $t::from_str_radix(e, $radix),
194             format!(
195                 "Failed to parse {} ({:?}) as a {} (pid {})",
196                 stringify!($e),
197                 e,
198                 stringify!($t),
199                 $pid
200             )
201         )
202     }};
203 }
204 
205 macro_rules! wrap_io_error {
206     ($path:expr, $expr:expr) => {
207         match $expr {
208             Ok(v) => Ok(v),
209             Err(e) => {
210                 let kind = e.kind();
211                 Err(::std::io::Error::new(
212                     kind,
213                     crate::IoErrorWrapper {
214                         path: $path.to_owned(),
215                         inner: e.into_inner(),
216                     },
217                 ))
218             }
219         }
220     };
221 }
222 
read_file<P: AsRef<Path>>(path: P) -> ProcResult<String>223 pub(crate) fn read_file<P: AsRef<Path>>(path: P) -> ProcResult<String> {
224     let mut f = FileWrapper::open(path)?;
225     let mut buf = String::new();
226     f.read_to_string(&mut buf)?;
227     Ok(buf)
228 }
229 
write_file<P: AsRef<Path>, T: AsRef<[u8]>>(path: P, buf: T) -> ProcResult<()>230 pub(crate) fn write_file<P: AsRef<Path>, T: AsRef<[u8]>>(path: P, buf: T) -> ProcResult<()> {
231     let mut f = File::open(path)?;
232     f.write_all(buf.as_ref())?;
233     Ok(())
234 }
235 
read_value<P, T, E>(path: P) -> ProcResult<T> where P: AsRef<Path>, T: FromStr<Err = E>, ProcError: From<E>,236 pub(crate) fn read_value<P, T, E>(path: P) -> ProcResult<T>
237 where
238     P: AsRef<Path>,
239     T: FromStr<Err = E>,
240     ProcError: From<E>,
241 {
242     let val = read_file(path)?;
243     Ok(<T as FromStr>::from_str(val.trim())?)
244     //Ok(val.trim().parse()?)
245 }
246 
write_value<P: AsRef<Path>, T: fmt::Display>(path: P, value: T) -> ProcResult<()>247 pub(crate) fn write_value<P: AsRef<Path>, T: fmt::Display>(path: P, value: T) -> ProcResult<()> {
248     write_file(path, value.to_string().as_bytes())
249 }
250 
from_iter<'a, I, U>(i: I) -> ProcResult<U> where I: IntoIterator<Item = &'a str>, U: FromStr,251 pub(crate) fn from_iter<'a, I, U>(i: I) -> ProcResult<U>
252 where
253     I: IntoIterator<Item = &'a str>,
254     U: FromStr,
255 {
256     let mut iter = i.into_iter();
257     let val = expect!(iter.next());
258     match FromStr::from_str(val) {
259         Ok(u) => Ok(u),
260         Err(..) => Err(build_internal_error!("Failed to convert")),
261     }
262 }
263 
264 pub mod process;
265 
266 mod meminfo;
267 pub use crate::meminfo::*;
268 
269 pub mod net;
270 
271 mod cpuinfo;
272 pub use crate::cpuinfo::*;
273 
274 mod cgroups;
275 pub use crate::cgroups::*;
276 
277 pub mod sys;
278 pub use crate::sys::kernel::Version as KernelVersion;
279 
280 mod pressure;
281 pub use crate::pressure::*;
282 
283 mod diskstats;
284 pub use diskstats::*;
285 
286 mod locks;
287 pub use locks::*;
288 
289 pub mod keyring;
290 
291 lazy_static! {
292     /// The number of clock ticks per second.
293     ///
294     /// This is calculated from `sysconf(_SC_CLK_TCK)`.
295     static ref TICKS_PER_SECOND: i64 = {
296         ticks_per_second().unwrap()
297     };
298     /// The version of the currently running kernel.
299     ///
300     /// This is a lazily constructed static.  You can also get this information via
301     /// [KernelVersion::new()].
302     static ref KERNEL: KernelVersion = {
303         KernelVersion::current().unwrap()
304     };
305     /// Memory page size, in bytes.
306     ///
307     /// This is calculated from `sysconf(_SC_PAGESIZE)`.
308     static ref PAGESIZE: i64 = {
309         page_size().unwrap()
310     };
311 }
312 
convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64>313 fn convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64> {
314     match unit {
315         "B" => Ok(num),
316         "KiB" | "kiB" | "kB" | "KB" => Ok(num * 1024),
317         "MiB" | "miB" | "MB" | "mB" => Ok(num * 1024 * 1024),
318         "GiB" | "giB" | "GB" | "gB" => Ok(num * 1024 * 1024 * 1024),
319         unknown => Err(build_internal_error!(format!("Unknown unit type {}", unknown))),
320     }
321 }
322 
323 trait FromStrRadix: Sized {
from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>324     fn from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>;
325 }
326 
327 impl FromStrRadix for u64 {
from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError>328     fn from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError> {
329         u64::from_str_radix(s, radix)
330     }
331 }
332 impl FromStrRadix for i32 {
from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError>333     fn from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError> {
334         i32::from_str_radix(s, radix)
335     }
336 }
337 
split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)>338 fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> {
339     let mut s = s.split(sep);
340     let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
341     let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
342     Ok((a, b))
343 }
344 
345 /// This is used to hold both an IO error as well as the path of the file that originated the error
346 #[derive(Debug)]
347 struct IoErrorWrapper {
348     path: PathBuf,
349     inner: Option<Box<dyn std::error::Error + Send + Sync>>,
350 }
351 
352 impl std::error::Error for IoErrorWrapper {}
353 impl fmt::Display for IoErrorWrapper {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>354     fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
355         if let Some(inner) = &self.inner {
356             write!(f, "IO Error({}): {}", self.path.display(), inner)
357         } else {
358             write!(f, "IO Error({})", self.path.display())
359         }
360     }
361 }
362 
363 /// A wrapper around a `File` that remembers the name of the path
364 struct FileWrapper {
365     inner: File,
366     path: PathBuf,
367 }
368 
369 impl FileWrapper {
open<P: AsRef<Path>>(path: P) -> Result<FileWrapper, io::Error>370     fn open<P: AsRef<Path>>(path: P) -> Result<FileWrapper, io::Error> {
371         let p = path.as_ref();
372         match File::open(&p) {
373             Ok(f) => Ok(FileWrapper {
374                 inner: f,
375                 path: p.to_owned(),
376             }),
377             Err(e) => {
378                 let kind = e.kind();
379                 Err(io::Error::new(
380                     kind,
381                     IoErrorWrapper {
382                         path: p.to_owned(),
383                         inner: e.into_inner(),
384                     },
385                 ))
386             }
387         }
388     }
389 }
390 
391 impl Read for FileWrapper {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>392     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
393         wrap_io_error!(self.path, self.inner.read(buf))
394     }
read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize>395     fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
396         wrap_io_error!(self.path, self.inner.read_to_end(buf))
397     }
read_to_string(&mut self, buf: &mut String) -> io::Result<usize>398     fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
399         wrap_io_error!(self.path, self.inner.read_to_string(buf))
400     }
read_exact(&mut self, buf: &mut [u8]) -> io::Result<()>401     fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
402         wrap_io_error!(self.path, self.inner.read_exact(buf))
403     }
404 }
405 
406 /// The main error type for the procfs crate.
407 ///
408 /// For more info, see the [ProcError] type.
409 pub type ProcResult<T> = Result<T, ProcError>;
410 
411 /// The various error conditions in the procfs crate.
412 ///
413 /// Most of the variants have an `Option<PathBuf>` component.  If the error root cause was related
414 /// to some operation on a file, the path of this file will be stored in this component.
415 #[derive(Debug)]
416 pub enum ProcError {
417     /// A standard permission denied error.
418     ///
419     /// This will be a common error, since some files in the procfs filesystem are only readable by
420     /// the root user.
421     PermissionDenied(Option<PathBuf>),
422     /// This might mean that the process no longer exists, or that your kernel doesn't support the
423     /// feature you are trying to use.
424     NotFound(Option<PathBuf>),
425     /// This might mean that a procfs file has incomplete contents.
426     ///
427     /// If you encounter this error, consider retrying the operation.
428     Incomplete(Option<PathBuf>),
429     /// Any other IO error (rare).
430     Io(std::io::Error, Option<PathBuf>),
431     /// Any other non-IO error (very rare).
432     Other(String),
433     /// This error indicates that some unexpected error occurred.  This is a bug.  The inner
434     /// [InternalError] struct will contain some more info.
435     ///
436     /// If you ever encounter this error, consider it a bug in the procfs crate and please report
437     /// it on github.
438     InternalError(InternalError),
439 }
440 
441 /// An internal error in the procfs crate
442 ///
443 /// If you encounter this error, consider it a bug and please report it on
444 /// [github](https://github.com/eminence/procfs).
445 ///
446 /// If you compile with the optional `backtrace` feature (disabled by default),
447 /// you can gain access to a stack trace of where the error happened.
448 pub struct InternalError {
449     pub msg: String,
450     pub file: &'static str,
451     pub line: u32,
452     #[cfg(feature = "backtrace")]
453     pub backtrace: backtrace::Backtrace,
454 }
455 
456 impl std::fmt::Debug for InternalError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result457     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
458         write!(
459             f,
460             "bug at {}:{} (please report this procfs bug)\n{}",
461             self.file, self.line, self.msg
462         )
463     }
464 }
465 
466 impl std::fmt::Display for InternalError {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result467     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
468         write!(
469             f,
470             "bug at {}:{} (please report this procfs bug)\n{}",
471             self.file, self.line, self.msg
472         )
473     }
474 }
475 
476 impl From<std::io::Error> for ProcError {
from(io: std::io::Error) -> Self477     fn from(io: std::io::Error) -> Self {
478         use std::io::ErrorKind;
479         let kind = io.kind();
480         let path: Option<PathBuf> = io.get_ref().and_then(|inner| {
481             if let Some(ref inner) = inner.downcast_ref::<IoErrorWrapper>() {
482                 Some(inner.path.clone())
483             } else {
484                 None
485             }
486         });
487         match kind {
488             ErrorKind::PermissionDenied => ProcError::PermissionDenied(path),
489             ErrorKind::NotFound => ProcError::NotFound(path),
490             _other => ProcError::Io(io, path),
491         }
492     }
493 }
494 
495 impl From<&'static str> for ProcError {
from(val: &'static str) -> Self496     fn from(val: &'static str) -> Self {
497         ProcError::Other(val.to_owned())
498     }
499 }
500 
501 impl From<std::num::ParseIntError> for ProcError {
from(val: std::num::ParseIntError) -> Self502     fn from(val: std::num::ParseIntError) -> Self {
503         ProcError::Other(format!("ParseIntError: {}", val))
504     }
505 }
506 
507 impl From<std::string::ParseError> for ProcError {
from(e: std::string::ParseError) -> Self508     fn from(e: std::string::ParseError) -> Self {
509         match e {}
510     }
511 }
512 
513 impl std::fmt::Display for ProcError {
fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>514     fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
515         match self {
516             // Variants with paths:
517             ProcError::PermissionDenied(Some(p)) => write!(f, "Permission Denied: {}", p.display()),
518             ProcError::NotFound(Some(p)) => write!(f, "File not found: {}", p.display()),
519             ProcError::Incomplete(Some(p)) => write!(f, "Data incomplete: {}", p.display()),
520             ProcError::Io(inner, Some(p)) => {
521                 write!(f, "Unexpected IO error({}): {}", p.display(), inner)
522             }
523             // Variants without paths:
524             ProcError::PermissionDenied(None) => write!(f, "Permission Denied"),
525             ProcError::NotFound(None) => write!(f, "File not found"),
526             ProcError::Incomplete(None) => write!(f, "Data incomplete"),
527             ProcError::Io(inner, None) => write!(f, "Unexpected IO error: {}", inner),
528 
529             ProcError::Other(s) => write!(f, "Unknown error {}", s),
530             ProcError::InternalError(e) => write!(f, "Internal error: {}", e),
531         }
532     }
533 }
534 
535 impl std::error::Error for ProcError {}
536 
537 /// Load average figures.
538 ///
539 /// Load averages are calculated as the number of jobs in the run queue (state R) or waiting for
540 /// disk I/O (state D) averaged over 1, 5, and 15 minutes.
541 #[derive(Debug)]
542 pub struct LoadAverage {
543     /// The one-minute load average
544     pub one: f32,
545     /// The five-minute load average
546     pub five: f32,
547     /// The fifteen-minute load average
548     pub fifteen: f32,
549     /// The number of currently runnable kernel scheduling  entities  (processes,  threads).
550     pub cur: u32,
551     /// The number of kernel scheduling entities that currently exist on the system.
552     pub max: u32,
553     /// The fifth field is the PID of the process that was most recently created on the system.
554     pub latest_pid: u32,
555 }
556 
557 impl LoadAverage {
558     /// Reads load average info from `/proc/loadavg`
new() -> ProcResult<LoadAverage>559     pub fn new() -> ProcResult<LoadAverage> {
560         let mut f = FileWrapper::open("/proc/loadavg")?;
561         let mut s = String::new();
562         f.read_to_string(&mut s)?;
563         let mut s = s.split_whitespace();
564 
565         let one = expect!(f32::from_str(expect!(s.next())));
566         let five = expect!(f32::from_str(expect!(s.next())));
567         let fifteen = expect!(f32::from_str(expect!(s.next())));
568         let curmax = expect!(s.next());
569         let latest_pid = expect!(u32::from_str(expect!(s.next())));
570 
571         let mut s = curmax.split('/');
572         let cur = expect!(u32::from_str(expect!(s.next())));
573         let max = expect!(u32::from_str(expect!(s.next())));
574 
575         Ok(LoadAverage {
576             one,
577             five,
578             fifteen,
579             cur,
580             max,
581             latest_pid,
582         })
583     }
584 }
585 
586 /// Return the number of ticks per second.
587 ///
588 /// This isn't part of the proc file system, but it's a useful thing to have, since several fields
589 /// count in ticks.  This is calculated from `sysconf(_SC_CLK_TCK)`.
ticks_per_second() -> std::io::Result<i64>590 pub fn ticks_per_second() -> std::io::Result<i64> {
591     if cfg!(unix) {
592         match unsafe { sysconf(_SC_CLK_TCK) } {
593             -1 => Err(std::io::Error::last_os_error()),
594             x => Ok(x.into()),
595         }
596     } else {
597         panic!("Not supported on non-unix platforms")
598     }
599 }
600 
601 /// The boot time of the system, as a `DateTime` object.
602 ///
603 /// This is calculated from `/proc/stat`.
604 ///
605 /// This function requires the "chrono" features to be enabled (which it is by default).
606 #[cfg(feature = "chrono")]
boot_time() -> ProcResult<DateTime<Local>>607 pub fn boot_time() -> ProcResult<DateTime<Local>> {
608     use chrono::TimeZone;
609     let secs = boot_time_secs()?;
610 
611     Ok(chrono::Local.timestamp(secs as i64, 0))
612 }
613 
614 /// The boottime of the system, in seconds since the epoch
615 ///
616 /// This is calculated from `/proc/stat`.
617 ///
618 #[cfg_attr(
619     not(feature = "chrono"),
620     doc = "If you compile with the optional `chrono` feature, you can use the `boot_time()` method to get the boot time as a `DateTime` object."
621 )]
622 #[cfg_attr(
623     feature = "chrono",
624     doc = "See also [boot_time()] to get the boot time as a `DateTime`"
625 )]
boot_time_secs() -> ProcResult<u64>626 pub fn boot_time_secs() -> ProcResult<u64> {
627     BOOT_TIME.with(|x| {
628         let mut btime = x.borrow_mut();
629         if let Some(btime) = *btime {
630             Ok(btime)
631         } else {
632             let stat = KernelStats::new()?;
633             *btime = Some(stat.btime);
634             Ok(stat.btime)
635         }
636     })
637 }
638 
639 thread_local! {
640     static BOOT_TIME : std::cell::RefCell<Option<u64>> = std::cell::RefCell::new(None);
641 }
642 
643 /// Memory page size, in bytes.
644 ///
645 /// This is calculated from `sysconf(_SC_PAGESIZE)`.
page_size() -> std::io::Result<i64>646 pub fn page_size() -> std::io::Result<i64> {
647     if cfg!(unix) {
648         match unsafe { sysconf(_SC_PAGESIZE) } {
649             -1 => Err(std::io::Error::last_os_error()),
650             x => Ok(x.into()),
651         }
652     } else {
653         panic!("Not supported on non-unix platforms")
654     }
655 }
656 
657 /// Possible values for a kernel config option
658 #[derive(Debug, PartialEq)]
659 pub enum ConfigSetting {
660     Yes,
661     Module,
662     Value(String),
663 }
664 /// Returns a configuration options used to build the currently running kernel
665 ///
666 /// If CONFIG_KCONFIG_PROC is available, the config is read from `/proc/config.gz`.
667 /// Else look in `/boot/config-$(uname -r)` or `/boot/config` (in that order).
kernel_config() -> ProcResult<HashMap<String, ConfigSetting>>668 pub fn kernel_config() -> ProcResult<HashMap<String, ConfigSetting>> {
669     use flate2::read::GzDecoder;
670 
671     let reader: Box<dyn BufRead> = if Path::new(PROC_CONFIG_GZ).exists() {
672         let file = FileWrapper::open(PROC_CONFIG_GZ)?;
673         let decoder = GzDecoder::new(file);
674         Box::new(BufReader::new(decoder))
675     } else {
676         let mut kernel: libc::utsname = unsafe { mem::zeroed() };
677 
678         if unsafe { libc::uname(&mut kernel) != 0 } {
679             return Err(ProcError::Other("Failed to call uname()".to_string()));
680         }
681 
682         let filename = format!(
683             "{}-{}",
684             BOOT_CONFIG,
685             unsafe { CStr::from_ptr(kernel.release.as_ptr() as *const c_char) }.to_string_lossy()
686         );
687 
688         if Path::new(&filename).exists() {
689             let file = FileWrapper::open(filename)?;
690             Box::new(BufReader::new(file))
691         } else {
692             let file = FileWrapper::open(BOOT_CONFIG)?;
693             Box::new(BufReader::new(file))
694         }
695     };
696 
697     let mut map = HashMap::new();
698 
699     for line in reader.lines() {
700         let line = line?;
701         if line.starts_with('#') {
702             continue;
703         }
704         if line.contains('=') {
705             let mut s = line.splitn(2, '=');
706             let name = expect!(s.next()).to_owned();
707             let value = match expect!(s.next()) {
708                 "y" => ConfigSetting::Yes,
709                 "m" => ConfigSetting::Module,
710                 s => ConfigSetting::Value(s.to_owned()),
711             };
712             map.insert(name, value);
713         }
714     }
715 
716     Ok(map)
717 }
718 
719 /// The amount of time, measured in ticks, the CPU has been in specific states
720 ///
721 /// These fields are measured in ticks because the underlying data from the kernel is measured in ticks.
722 /// The number of ticks per second can be returned by [`ticks_per_second()`](crate::ticks_per_second)
723 /// and is generally 100 on most systems.
724 
725 /// To convert this value to seconds, you can divide by the tps.  There are also convenience methods
726 /// that you can use too.
727 #[derive(Debug)]
728 pub struct CpuTime {
729     /// Ticks spent in user mode
730     pub user: u64,
731     /// Ticks spent in user mode with low priority (nice)
732     pub nice: u64,
733     /// Ticks spent in system mode
734     pub system: u64,
735     /// Ticks spent in the idle state
736     pub idle: u64,
737     /// Ticks waiting for I/O to complete
738     ///
739     /// This value is not reliable, for the following reasons:
740     ///
741     /// 1. The CPU will not wait for I/O to complete; iowait is the time that a
742     ///    task is waiting for I/O to complete.  When a CPU goes into idle state
743     ///    for outstanding task I/O, another task will be scheduled on this CPU.
744     ///
745     /// 2. On a multi-core CPU, this task waiting for I/O to complete is not running
746     ///    on any CPU, so the iowait for each CPU is difficult to calculate.
747     ///
748     /// 3. The value in this field may *decrease* in certain conditions.
749     ///
750     /// (Since Linux 2.5.41)
751     pub iowait: Option<u64>,
752     /// Ticks servicing interrupts
753     ///
754     /// (Since Linux 2.6.0)
755     pub irq: Option<u64>,
756     /// Ticks servicing softirqs
757     ///
758     /// (Since Linux 2.6.0)
759     pub softirq: Option<u64>,
760     /// Ticks of stolen time.
761     ///
762     /// Stolen time is the time spent in other operating systems when running in
763     /// a virtualized environment.
764     ///
765     /// (Since Linux 2.6.11)
766     pub steal: Option<u64>,
767     /// Ticks spent running a virtual CPU for guest operating systems under control
768     /// of the linux kernel
769     ///
770     /// (Since Linux 2.6.24)
771     pub guest: Option<u64>,
772     /// Ticks spent running a niced guest
773     ///
774     /// (Since Linux 2.6.33)
775     pub guest_nice: Option<u64>,
776 
777     tps: u64,
778 }
779 
780 impl CpuTime {
from_str(s: &str) -> ProcResult<CpuTime>781     fn from_str(s: &str) -> ProcResult<CpuTime> {
782         let mut s = s.split_whitespace();
783 
784         // Store this field in the struct so we don't have to attempt to unwrap ticks_per_second() when we convert
785         // from ticks into other time units
786         let tps = crate::ticks_per_second()? as u64;
787 
788         s.next();
789         let user = from_str!(u64, expect!(s.next()));
790         let nice = from_str!(u64, expect!(s.next()));
791         let system = from_str!(u64, expect!(s.next()));
792         let idle = from_str!(u64, expect!(s.next()));
793 
794         let iowait = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
795         let irq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
796         let softirq = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
797         let steal = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
798         let guest = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
799         let guest_nice = s.next().map(|s| Ok(from_str!(u64, s))).transpose()?;
800 
801         Ok(CpuTime {
802             user,
803             nice,
804             system,
805             idle,
806             iowait,
807             irq,
808             softirq,
809             steal,
810             guest,
811             guest_nice,
812             tps,
813         })
814     }
815 
816     /// Milliseconds spent in user mode
user_ms(&self) -> u64817     pub fn user_ms(&self) -> u64 {
818         let ms_per_tick = 1000 / self.tps;
819         self.user * ms_per_tick
820     }
821 
822     /// Time spent in user mode
user_duration(&self) -> Duration823     pub fn user_duration(&self) -> Duration {
824         Duration::from_millis(self.user_ms())
825     }
826 
827     /// Milliseconds spent in user mode with low priority (nice)
nice_ms(&self) -> u64828     pub fn nice_ms(&self) -> u64 {
829         let ms_per_tick = 1000 / self.tps;
830         self.nice * ms_per_tick
831     }
832 
833     /// Time spent in user mode with low priority (nice)
nice_duration(&self) -> Duration834     pub fn nice_duration(&self) -> Duration {
835         Duration::from_millis(self.nice_ms())
836     }
837 
838     /// Milliseconds spent in system mode
system_ms(&self) -> u64839     pub fn system_ms(&self) -> u64 {
840         let ms_per_tick = 1000 / self.tps;
841         self.system * ms_per_tick
842     }
843 
844     /// Time spent in system mode
system_duration(&self) -> Duration845     pub fn system_duration(&self) -> Duration {
846         Duration::from_millis(self.system_ms())
847     }
848 
849     /// Milliseconds spent in the idle state
idle_ms(&self) -> u64850     pub fn idle_ms(&self) -> u64 {
851         let ms_per_tick = 1000 / self.tps;
852         self.idle * ms_per_tick
853     }
854 
855     /// Time spent in the idle state
idle_duration(&self) -> Duration856     pub fn idle_duration(&self) -> Duration {
857         Duration::from_millis(self.idle_ms())
858     }
859 
860     /// Milliseconds spent waiting for I/O to complete
iowait_ms(&self) -> Option<u64>861     pub fn iowait_ms(&self) -> Option<u64> {
862         let ms_per_tick = 1000 / self.tps;
863         self.iowait.map(|io| io * ms_per_tick)
864     }
865 
866     /// Time spent waiting for I/O to complete
iowait_duration(&self) -> Option<Duration>867     pub fn iowait_duration(&self) -> Option<Duration> {
868         self.iowait_ms().map(|io| Duration::from_millis(io))
869     }
870 
871     /// Milliseconds spent servicing interrupts
irq_ms(&self) -> Option<u64>872     pub fn irq_ms(&self) -> Option<u64> {
873         let ms_per_tick = 1000 / self.tps;
874         self.irq.map(|ms| ms * ms_per_tick)
875     }
876 
877     /// Time spent servicing interrupts
irq_duration(&self) -> Option<Duration>878     pub fn irq_duration(&self) -> Option<Duration> {
879         self.irq_ms().map(|ms| Duration::from_millis(ms))
880     }
881 
882     /// Milliseconds spent servicing softirqs
softirq_ms(&self) -> Option<u64>883     pub fn softirq_ms(&self) -> Option<u64> {
884         let ms_per_tick = 1000 / self.tps;
885         self.softirq.map(|ms| ms * ms_per_tick)
886     }
887 
888     /// Time spent servicing softirqs
softirq_duration(&self) -> Option<Duration>889     pub fn softirq_duration(&self) -> Option<Duration> {
890         self.softirq_ms().map(|ms| Duration::from_millis(ms))
891     }
892 
893     /// Milliseconds of stolen time
steal_ms(&self) -> Option<u64>894     pub fn steal_ms(&self) -> Option<u64> {
895         let ms_per_tick = 1000 / self.tps;
896         self.steal.map(|ms| ms * ms_per_tick)
897     }
898 
899     /// Amount of stolen time
steal_duration(&self) -> Option<Duration>900     pub fn steal_duration(&self) -> Option<Duration> {
901         self.steal_ms().map(|ms| Duration::from_millis(ms))
902     }
903 
904     /// Milliseconds spent running a virtual CPU for guest operating systems under control of the linux kernel
guest_ms(&self) -> Option<u64>905     pub fn guest_ms(&self) -> Option<u64> {
906         let ms_per_tick = 1000 / self.tps;
907         self.guest.map(|ms| ms * ms_per_tick)
908     }
909 
910     /// Time spent running a virtual CPU for guest operating systems under control of the linux kernel
guest_duration(&self) -> Option<Duration>911     pub fn guest_duration(&self) -> Option<Duration> {
912         self.guest_ms().map(|ms| Duration::from_millis(ms))
913     }
914 
915     /// Milliseconds spent running a niced guest
guest_nice_ms(&self) -> Option<u64>916     pub fn guest_nice_ms(&self) -> Option<u64> {
917         let ms_per_tick = 1000 / self.tps;
918         self.guest_nice.map(|ms| ms * ms_per_tick)
919     }
920 
921     /// Time spent running a niced guest
guest_nice_duration(&self) -> Option<Duration>922     pub fn guest_nice_duration(&self) -> Option<Duration> {
923         self.guest_nice_ms().map(|ms| Duration::from_millis(ms))
924     }
925 }
926 
927 /// Kernel/system statistics, from `/proc/stat`
928 #[derive(Debug)]
929 pub struct KernelStats {
930     /// The amount of time the system spent in various states
931     pub total: CpuTime,
932     /// The amount of time that specific CPUs spent in various states
933     pub cpu_time: Vec<CpuTime>,
934 
935     /// The number of context switches that the system underwent
936     pub ctxt: u64,
937 
938     /// Boot time, in number of seconds since the Epoch
939     pub btime: u64,
940 
941     /// Number of forks since boot
942     pub processes: u64,
943 
944     /// Number of processes in runnable state
945     ///
946     /// (Since Linux 2.5.45)
947     pub procs_running: Option<u32>,
948 
949     /// Number of processes blocked waiting for I/O
950     ///
951     /// (Since Linux 2.5.45)
952     pub procs_blocked: Option<u32>,
953 }
954 
955 impl KernelStats {
new() -> ProcResult<KernelStats>956     pub fn new() -> ProcResult<KernelStats> {
957         KernelStats::from_reader(FileWrapper::open("/proc/stat")?)
958     }
959 
from_reader<R: io::Read>(r: R) -> ProcResult<KernelStats>960     fn from_reader<R: io::Read>(r: R) -> ProcResult<KernelStats> {
961         let bufread = BufReader::new(r);
962         let lines = bufread.lines();
963 
964         let mut total_cpu = None;
965         let mut cpus = Vec::new();
966         let mut ctxt = None;
967         let mut btime = None;
968         let mut processes = None;
969         let mut procs_running = None;
970         let mut procs_blocked = None;
971 
972         for line in lines {
973             let line = line?;
974             if line.starts_with("cpu ") {
975                 total_cpu = Some(CpuTime::from_str(&line)?);
976             } else if line.starts_with("cpu") {
977                 cpus.push(CpuTime::from_str(&line)?);
978             } else if line.starts_with("ctxt ") {
979                 ctxt = Some(from_str!(u64, &line[5..]));
980             } else if line.starts_with("btime ") {
981                 btime = Some(from_str!(u64, &line[6..]));
982             } else if line.starts_with("processes ") {
983                 processes = Some(from_str!(u64, &line[10..]));
984             } else if line.starts_with("procs_running ") {
985                 procs_running = Some(from_str!(u32, &line[14..]));
986             } else if line.starts_with("procs_blocked ") {
987                 procs_blocked = Some(from_str!(u32, &line[14..]));
988             }
989         }
990 
991         Ok(KernelStats {
992             total: expect!(total_cpu),
993             cpu_time: cpus,
994             ctxt: expect!(ctxt),
995             btime: expect!(btime),
996             processes: expect!(processes),
997             procs_running,
998             procs_blocked,
999         })
1000     }
1001 }
1002 
1003 /// Get various virtual memory statistics
1004 ///
1005 /// Since the exact set of statistics will vary from kernel to kernel,
1006 /// and because most of them are not well documented, this function
1007 /// returns a HashMap instead of a struct.  Consult the kernel source
1008 /// code for more details of this data.
1009 ///
1010 /// This data is taken from the `/proc/vmstat` file.
1011 ///
1012 /// (since Linux 2.6.0)
vmstat() -> ProcResult<HashMap<String, i64>>1013 pub fn vmstat() -> ProcResult<HashMap<String, i64>> {
1014     let file = FileWrapper::open("/proc/vmstat")?;
1015     let reader = BufReader::new(file);
1016     let mut map = HashMap::new();
1017     for line in reader.lines() {
1018         let line = line?;
1019         let mut split = line.split_whitespace();
1020         let name = expect!(split.next());
1021         let val = from_str!(i64, expect!(split.next()));
1022         map.insert(name.to_owned(), val);
1023     }
1024 
1025     Ok(map)
1026 }
1027 
1028 /// Details about a loaded kernel module
1029 ///
1030 /// For an example, see the [lsmod.rs](https://github.com/eminence/procfs/tree/master/examples)
1031 /// example in the source repo.
1032 #[derive(Debug)]
1033 pub struct KernelModule {
1034     /// The name of the module
1035     pub name: String,
1036 
1037     /// The size of the module
1038     pub size: u32,
1039 
1040     /// The number of references in the kernel to this module.  This can be -1 if the module is unloading
1041     pub refcount: i32,
1042 
1043     /// A list of modules that depend on this module.
1044     pub used_by: Vec<String>,
1045 
1046     /// The module state
1047     ///
1048     /// This will probably always be "Live", but it could also be either "Unloading" or "Loading"
1049     pub state: String,
1050 }
1051 
1052 /// Get a list of loaded kernel modules
1053 ///
1054 /// This corresponds to the data in `/proc/modules`.
modules() -> ProcResult<HashMap<String, KernelModule>>1055 pub fn modules() -> ProcResult<HashMap<String, KernelModule>> {
1056     // kernel reference: kernel/module.c m_show()
1057 
1058     let mut map = HashMap::new();
1059     let file = FileWrapper::open("/proc/modules")?;
1060     let reader = BufReader::new(file);
1061     for line in reader.lines() {
1062         let line: String = line?;
1063         let mut s = line.split_whitespace();
1064         let name = expect!(s.next());
1065         let size = from_str!(u32, expect!(s.next()));
1066         let refcount = from_str!(i32, expect!(s.next()));
1067         let used_by: &str = expect!(s.next());
1068         let state = expect!(s.next());
1069 
1070         map.insert(
1071             name.to_string(),
1072             KernelModule {
1073                 name: name.to_string(),
1074                 size,
1075                 refcount,
1076                 used_by: if used_by == "-" {
1077                     Vec::new()
1078                 } else {
1079                     used_by
1080                         .split(',')
1081                         .filter(|s| !s.is_empty())
1082                         .map(|s| s.to_string())
1083                         .collect()
1084                 },
1085                 state: state.to_string(),
1086             },
1087         );
1088     }
1089 
1090     Ok(map)
1091 }
1092 
1093 #[cfg(test)]
1094 mod tests {
1095     use super::*;
1096 
1097     #[test]
test_statics()1098     fn test_statics() {
1099         println!("{:?}", *TICKS_PER_SECOND);
1100         println!("{:?}", *KERNEL);
1101         println!("{:?}", *PAGESIZE);
1102     }
1103 
1104     #[test]
test_kernel_from_str()1105     fn test_kernel_from_str() {
1106         let k = KernelVersion::from_str("1.2.3").unwrap();
1107         assert_eq!(k.major, 1);
1108         assert_eq!(k.minor, 2);
1109         assert_eq!(k.patch, 3);
1110 
1111         let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
1112         assert_eq!(k.major, 4);
1113         assert_eq!(k.minor, 9);
1114         assert_eq!(k.patch, 16);
1115     }
1116 
1117     #[test]
test_kernel_cmp()1118     fn test_kernel_cmp() {
1119         let a = KernelVersion::from_str("1.2.3").unwrap();
1120         let b = KernelVersion::from_str("1.2.3").unwrap();
1121         let c = KernelVersion::from_str("1.2.4").unwrap();
1122         let d = KernelVersion::from_str("1.5.4").unwrap();
1123         let e = KernelVersion::from_str("2.5.4").unwrap();
1124 
1125         assert_eq!(a, b);
1126         assert!(a < c);
1127         assert!(a < d);
1128         assert!(a < e);
1129         assert!(e > d);
1130         assert!(e > c);
1131         assert!(e > b);
1132     }
1133 
1134     #[test]
test_loadavg()1135     fn test_loadavg() {
1136         let load = LoadAverage::new().unwrap();
1137         println!("{:?}", load);
1138     }
1139 
1140     #[test]
test_from_str() -> ProcResult<()>1141     fn test_from_str() -> ProcResult<()> {
1142         assert_eq!(from_str!(u8, "12"), 12);
1143         assert_eq!(from_str!(u8, "A", 16), 10);
1144         Ok(())
1145     }
1146 
1147     #[test]
test_from_str_fail()1148     fn test_from_str_fail() {
1149         fn inner() -> ProcResult<()> {
1150             let s = "four";
1151             from_str!(u8, s);
1152             unreachable!()
1153         }
1154 
1155         assert!(inner().is_err())
1156     }
1157 
1158     #[test]
test_kernel_config()1159     fn test_kernel_config() {
1160         // TRAVIS
1161         // we don't have access to the kernel_config on travis, so skip that test there
1162         match std::env::var("TRAVIS") {
1163             Ok(ref s) if s == "true" => return,
1164             _ => {}
1165         }
1166         if !Path::new(PROC_CONFIG_GZ).exists() && !Path::new(BOOT_CONFIG).exists() {
1167             return;
1168         }
1169 
1170         let config = kernel_config().unwrap();
1171         println!("{:#?}", config);
1172     }
1173 
1174     #[test]
test_file_io_errors()1175     fn test_file_io_errors() {
1176         fn inner<P: AsRef<Path>>(p: P) -> Result<(), ProcError> {
1177             let mut file = FileWrapper::open(p)?;
1178 
1179             let mut buf = [0; 128];
1180             file.read_exact(&mut buf[0..128])?;
1181 
1182             Ok(())
1183         }
1184 
1185         let err = inner("/this_should_not_exist").unwrap_err();
1186         println!("{}", err);
1187 
1188         match err {
1189             ProcError::NotFound(Some(p)) => {
1190                 assert_eq!(p, Path::new("/this_should_not_exist"));
1191             }
1192             x => panic!("Unexpected return value: {:?}", x),
1193         }
1194 
1195         match inner("/proc/loadavg") {
1196             Err(ProcError::Io(_, Some(p))) => {
1197                 assert_eq!(p, Path::new("/proc/loadavg"));
1198             }
1199             x => panic!("Unexpected return value: {:?}", x),
1200         }
1201     }
1202 
1203     #[test]
test_nopanic()1204     fn test_nopanic() {
1205         fn _inner() -> ProcResult<bool> {
1206             let x: Option<bool> = None;
1207             let y: bool = expect!(x);
1208             Ok(y)
1209         }
1210 
1211         let r = _inner();
1212         println!("{:?}", r);
1213         assert!(r.is_err());
1214 
1215         fn _inner2() -> ProcResult<bool> {
1216             let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1217             Ok(true)
1218         }
1219 
1220         let r = _inner2();
1221         println!("{:?}", r);
1222         assert!(r.is_err());
1223     }
1224 
1225     #[cfg(feature = "backtrace")]
1226     #[test]
test_backtrace()1227     fn test_backtrace() {
1228         fn _inner() -> ProcResult<bool> {
1229             let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
1230             Ok(true)
1231         }
1232 
1233         let r = _inner();
1234         println!("{:?}", r);
1235     }
1236 
1237     #[test]
test_kernel_stat()1238     fn test_kernel_stat() {
1239         let stat = KernelStats::new().unwrap();
1240         println!("{:#?}", stat);
1241 
1242         // the boottime from KernelStats should match the boottime from /proc/uptime
1243         let boottime = boot_time_secs().unwrap();
1244 
1245         let diff = (boottime as i32 - stat.btime as i32).abs();
1246         assert!(diff <= 1);
1247 
1248         let cpuinfo = CpuInfo::new().unwrap();
1249         assert_eq!(cpuinfo.num_cores(), stat.cpu_time.len());
1250 
1251         // the sum of each individual CPU should be equal to the total cpu entry
1252         // note: on big machines with 128 cores, it seems that the differences can be rather high,
1253         // especially when heavily loaded.  So this test tolerates a 6000-tick discrepancy
1254         // (60 seconds in a 100-tick-per-second kernel)
1255 
1256         let user: u64 = stat.cpu_time.iter().map(|i| i.user).sum();
1257         let nice: u64 = stat.cpu_time.iter().map(|i| i.nice).sum();
1258         let system: u64 = stat.cpu_time.iter().map(|i| i.system).sum();
1259         assert!(
1260             (stat.total.user as i64 - user as i64).abs() < 6000,
1261             "sum:{} total:{} diff:{}",
1262             stat.total.user,
1263             user,
1264             stat.total.user - user
1265         );
1266         assert!(
1267             (stat.total.nice as i64 - nice as i64).abs() < 6000,
1268             "sum:{} total:{} diff:{}",
1269             stat.total.nice,
1270             nice,
1271             stat.total.nice - nice
1272         );
1273         assert!(
1274             (stat.total.system as i64 - system as i64).abs() < 6000,
1275             "sum:{} total:{} diff:{}",
1276             stat.total.system,
1277             system,
1278             stat.total.system - system
1279         );
1280 
1281         let diff = stat.total.idle as i64 - (stat.cpu_time.iter().map(|i| i.idle).sum::<u64>() as i64).abs();
1282         assert!(diff < 1000, "idle time difference too high: {}", diff);
1283     }
1284 
1285     #[test]
test_vmstat()1286     fn test_vmstat() {
1287         let stat = vmstat().unwrap();
1288         println!("{:?}", stat);
1289     }
1290 
1291     #[test]
test_modules()1292     fn test_modules() {
1293         let mods = modules().unwrap();
1294         for module in mods.values() {
1295             println!("{:?}", module);
1296         }
1297     }
1298 
1299     #[test]
tests_tps()1300     fn tests_tps() {
1301         let tps = ticks_per_second().unwrap();
1302         println!("{} ticks per second", tps);
1303     }
1304 }
1305