#![deny(intra_doc_link_resolution_failure)]
#[cfg(unix)]
extern crate libc;
#[macro_use]
extern crate bitflags;
#[macro_use]
extern crate lazy_static;
extern crate byteorder;
extern crate hex;
extern crate libflate;
#[cfg(unix)]
mod platform_specific_items {
pub use libc::pid_t;
pub use libc::sysconf;
pub use libc::{_SC_CLK_TCK, _SC_PAGESIZE};
}
#[cfg(windows)]
mod platform_specific_items {
pub type pid_t = i32;
pub fn sysconf(_: i32) -> i64 {
panic!()
}
pub const _SC_CLK_TCK: i32 = 2;
pub const _SC_PAGESIZE: i32 = 30;
}
use crate::platform_specific_items::*;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read, Write};
use std::mem;
use std::os::raw::c_char;
use std::path::{Path, PathBuf};
use std::str::FromStr;
#[cfg(feature = "chrono")]
use chrono::{DateTime, Local};
const PROC_CONFIG_GZ: &str = "/proc/config.gz";
const BOOT_CONFIG: &str = "/boot/config";
trait IntoOption<T> {
fn into_option(t: Self) -> Option<T>;
}
impl<T> IntoOption<T> for Option<T> {
fn into_option(t: Option<T>) -> Option<T> {
t
}
}
impl<T, R> IntoOption<T> for Result<T, R> {
fn into_option(t: Result<T, R>) -> Option<T> {
t.ok()
}
}
pub(crate) trait IntoResult<T, E> {
fn into(t: Self) -> Result<T, E>;
}
macro_rules! build_internal_error {
($err: expr) => {
crate::ProcError::InternalError(crate::InternalError {
msg: format!("Internal Unwrap Error: {}", $err),
file: file!(),
line: line!(),
#[cfg(feature = "backtrace")]
backtrace: backtrace::Backtrace::new(),
})
};
($err: expr, $msg: expr) => {
crate::ProcError::InternalError(crate::InternalError {
msg: format!("Internal Unwrap Error: {}: {}", $msg, $err),
file: file!(),
line: line!(),
#[cfg(feature = "backtrace")]
backtrace: backtrace::Backtrace::new(),
})
};
}
struct NoneError;
impl std::fmt::Display for NoneError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NoneError")
}
}
impl<T> IntoResult<T, NoneError> for Option<T> {
fn into(t: Option<T>) -> Result<T, NoneError> {
t.ok_or(NoneError)
}
}
impl<T, E> IntoResult<T, E> for Result<T, E> {
fn into(t: Result<T, E>) -> Result<T, E> {
t
}
}
#[macro_use]
#[allow(unused_macros)]
macro_rules! proc_panic {
($e:expr) => {
crate::IntoOption::into_option($e).unwrap_or_else(|| {
panic!(
"Failed to unwrap {}. Please report this as a procfs bug.",
stringify!($e)
)
})
};
($e:expr, $msg:expr) => {
crate::IntoOption::into_option($e).unwrap_or_else(|| {
panic!(
"Failed to unwrap {} ({}). Please report this as a procfs bug.",
stringify!($e),
$msg
)
})
};
}
macro_rules! expect {
($e:expr) => {
match crate::IntoResult::into($e) {
Ok(v) => v,
Err(e) => return Err(build_internal_error!(e)),
}
};
($e:expr, $msg:expr) => {
match crate::IntoResult::into($e) {
Ok(v) => v,
Err(e) => return Err(build_internal_error!(e, $msg)),
}
};
}
#[macro_use]
macro_rules! from_str {
($t:tt, $e:expr) => {{
let e = $e;
expect!(
$t::from_str_radix(e, 10),
format!(
"Failed to parse {} ({:?}) as a {}",
stringify!($e),
e,
stringify!($t),
)
)
}};
($t:tt, $e:expr, $radix:expr) => {{
let e = $e;
expect!(
$t::from_str_radix(e, $radix),
format!(
"Failed to parse {} ({:?}) as a {}",
stringify!($e),
e,
stringify!($t)
)
)
}};
($t:tt, $e:expr, $radix:expr, pid:$pid:expr) => {{
let e = $e;
expect!(
$t::from_str_radix(e, $radix),
format!(
"Failed to parse {} ({:?}) as a {} (pid {})",
stringify!($e),
e,
stringify!($t),
$pid
)
)
}};
}
macro_rules! wrap_io_error {
($path:expr, $expr:expr) => {
match $expr {
Ok(v) => Ok(v),
Err(e) => {
let kind = e.kind();
Err(io::Error::new(
kind,
IoErrorWrapper {
path: $path.clone(),
inner: e.into_inner(),
},
))
}
}
};
}
pub(crate) fn read_file<P: AsRef<Path>>(path: P) -> ProcResult<String> {
let mut f = FileWrapper::open(path)?;
let mut buf = String::new();
f.read_to_string(&mut buf)?;
Ok(buf)
}
pub(crate) fn write_file<P: AsRef<Path>, T: AsRef<[u8]>>(path: P, buf: T) -> ProcResult<()> {
let mut f = File::open(path)?;
f.write_all(buf.as_ref())?;
Ok(())
}
pub(crate) fn read_value<P, T, E>(path: P) -> ProcResult<T>
where
P: AsRef<Path>,
T: FromStr<Err = E>,
ProcError: From<E>,
{
let val = read_file(path)?;
Ok(<T as FromStr>::from_str(val.trim())?)
}
pub(crate) fn write_value<P: AsRef<Path>, T: fmt::Display>(path: P, value: T) -> ProcResult<()> {
write_file(path, value.to_string().as_bytes())
}
pub(crate) fn from_iter<'a, I, U>(i: I) -> ProcResult<U>
where
I: IntoIterator<Item = &'a str>,
U: FromStr,
{
let mut iter = i.into_iter();
let val = expect!(iter.next());
match FromStr::from_str(val) {
Ok(u) => Ok(u),
Err(..) => Err(build_internal_error!("Failed to convert")),
}
}
pub mod process;
mod meminfo;
pub use crate::meminfo::*;
pub mod net;
mod cpuinfo;
pub use crate::cpuinfo::*;
mod cgroups;
pub use crate::cgroups::*;
pub mod sys;
pub use crate::sys::kernel::Version as KernelVersion;
mod pressure;
pub use crate::pressure::*;
mod diskstats;
pub use diskstats::*;
lazy_static! {
static ref TICKS_PER_SECOND: i64 = {
ticks_per_second().unwrap()
};
static ref KERNEL: KernelVersion = {
KernelVersion::current().unwrap()
};
static ref PAGESIZE: i64 = {
page_size().unwrap()
};
}
fn convert_to_kibibytes(num: u64, unit: &str) -> ProcResult<u64> {
match unit {
"B" => Ok(num),
"KiB" | "kiB" | "kB" | "KB" => Ok(num * 1024),
"MiB" | "miB" | "MB" | "mB" => Ok(num * 1024 * 1024),
"GiB" | "giB" | "GB" | "gB" => Ok(num * 1024 * 1024 * 1024),
unknown => Err(build_internal_error!(format!(
"Unknown unit type {}",
unknown
))),
}
}
trait FromStrRadix: Sized {
fn from_str_radix(t: &str, radix: u32) -> Result<Self, std::num::ParseIntError>;
}
impl FromStrRadix for u64 {
fn from_str_radix(s: &str, radix: u32) -> Result<u64, std::num::ParseIntError> {
u64::from_str_radix(s, radix)
}
}
impl FromStrRadix for i32 {
fn from_str_radix(s: &str, radix: u32) -> Result<i32, std::num::ParseIntError> {
i32::from_str_radix(s, radix)
}
}
fn split_into_num<T: FromStrRadix>(s: &str, sep: char, radix: u32) -> ProcResult<(T, T)> {
let mut s = s.split(sep);
let a = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
let b = expect!(FromStrRadix::from_str_radix(expect!(s.next()), radix));
Ok((a, b))
}
#[derive(Debug)]
struct IoErrorWrapper {
path: PathBuf,
inner: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl std::error::Error for IoErrorWrapper {}
impl fmt::Display for IoErrorWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
if let Some(inner) = &self.inner {
write!(f, "IO Error({}): {}", self.path.display(), inner)
} else {
write!(f, "IO Error({})", self.path.display())
}
}
}
struct FileWrapper {
inner: File,
path: PathBuf,
}
impl FileWrapper {
fn open<P: AsRef<Path>>(path: P) -> Result<FileWrapper, io::Error> {
let p = path.as_ref();
match File::open(&p) {
Ok(f) => Ok(FileWrapper {
inner: f,
path: p.to_owned(),
}),
Err(e) => {
let kind = e.kind();
Err(io::Error::new(
kind,
IoErrorWrapper {
path: p.to_owned(),
inner: e.into_inner(),
},
))
}
}
}
}
impl Read for FileWrapper {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
wrap_io_error!(self.path, self.inner.read(buf))
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
wrap_io_error!(self.path, self.inner.read_to_end(buf))
}
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
wrap_io_error!(self.path, self.inner.read_to_string(buf))
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
wrap_io_error!(self.path, self.inner.read_exact(buf))
}
}
pub type ProcResult<T> = Result<T, ProcError>;
#[derive(Debug)]
pub enum ProcError {
PermissionDenied(Option<PathBuf>),
NotFound(Option<PathBuf>),
Incomplete(Option<PathBuf>),
Io(std::io::Error, Option<PathBuf>),
Other(String),
InternalError(InternalError),
}
#[cfg_attr(
not(feature = "backtrace"),
doc = "If you compile with the optional `backtrace` feature, you can gain access to a stack trace of where the error happened."
)]
pub struct InternalError {
pub msg: String,
pub file: &'static str,
pub line: u32,
#[cfg(feature = "backtrace")]
pub backtrace: backtrace::Backtrace,
}
impl std::fmt::Debug for InternalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"bug at {}:{} (please report this procfs bug)\n{}",
self.file, self.line, self.msg
)
}
}
impl std::fmt::Display for InternalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"bug at {}:{} (please report this procfs bug)\n{}",
self.file, self.line, self.msg
)
}
}
impl From<std::io::Error> for ProcError {
fn from(io: std::io::Error) -> Self {
use std::io::ErrorKind;
let kind = io.kind();
let path: Option<PathBuf> = io.get_ref().and_then(|inner| {
if let Some(ref inner) = inner.downcast_ref::<IoErrorWrapper>() {
Some(inner.path.clone())
} else {
None
}
});
match kind {
ErrorKind::PermissionDenied => ProcError::PermissionDenied(path),
ErrorKind::NotFound => ProcError::NotFound(path),
_other => ProcError::Io(io, path),
}
}
}
impl From<&'static str> for ProcError {
fn from(val: &'static str) -> Self {
ProcError::Other(val.to_owned())
}
}
impl From<std::num::ParseIntError> for ProcError {
fn from(val: std::num::ParseIntError) -> Self {
ProcError::Other(format!("ParseIntError: {}", val))
}
}
impl std::fmt::Display for ProcError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
ProcError::PermissionDenied(Some(p)) => write!(f, "Permission Denied: {}", p.display()),
ProcError::NotFound(Some(p)) => write!(f, "File not found: {}", p.display()),
ProcError::Incomplete(Some(p)) => write!(f, "Data incomplete: {}", p.display()),
ProcError::Io(inner, Some(p)) => {
write!(f, "Unexpected IO error({}): {}", p.display(), inner)
}
ProcError::PermissionDenied(None) => write!(f, "Permission Denied"),
ProcError::NotFound(None) => write!(f, "File not found"),
ProcError::Incomplete(None) => write!(f, "Data incomplete"),
ProcError::Io(inner, None) => write!(f, "Unexpected IO error: {}", inner),
ProcError::Other(s) => write!(f, "Uknown error {}", s),
ProcError::InternalError(e) => write!(f, "Internal error: {}", e),
}
}
}
impl std::error::Error for ProcError {}
#[derive(Debug)]
pub struct LoadAverage {
pub one: f32,
pub five: f32,
pub fifteen: f32,
pub cur: u32,
pub max: u32,
pub latest_pid: u32,
}
impl LoadAverage {
pub fn new() -> ProcResult<LoadAverage> {
let mut f = FileWrapper::open("/proc/loadavg")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
let mut s = s.split_whitespace();
let one = expect!(f32::from_str(expect!(s.next())));
let five = expect!(f32::from_str(expect!(s.next())));
let fifteen = expect!(f32::from_str(expect!(s.next())));
let curmax = expect!(s.next());
let latest_pid = expect!(u32::from_str(expect!(s.next())));
let mut s = curmax.split('/');
let cur = expect!(u32::from_str(expect!(s.next())));
let max = expect!(u32::from_str(expect!(s.next())));
Ok(LoadAverage {
one,
five,
fifteen,
cur,
max,
latest_pid,
})
}
}
pub fn ticks_per_second() -> std::io::Result<i64> {
if cfg!(unix) {
match unsafe { sysconf(_SC_CLK_TCK) } {
-1 => Err(std::io::Error::last_os_error()),
x => Ok(x.into()),
}
} else {
panic!("Not supported on non-unix platforms")
}
}
#[cfg(feature = "chrono")]
pub fn boot_time() -> ProcResult<DateTime<Local>> {
use chrono::TimeZone;
let secs = boot_time_secs()?;
Ok(chrono::Local.timestamp(secs as i64, 0))
}
#[cfg_attr(
not(feature = "chrono"),
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."
)]
#[cfg_attr(
feature = "chrono",
doc = "See also [boot_time()] to get the boot time as a `DateTime`"
)]
pub fn boot_time_secs() -> ProcResult<u64> {
BOOT_TIME.with(|x| {
let mut btime = x.borrow_mut();
if let Some(btime) = *btime {
Ok(btime)
} else {
let stat = KernelStats::new()?;
*btime = Some(stat.btime);
Ok(stat.btime)
}
})
}
thread_local! {
static BOOT_TIME : std::cell::RefCell<Option<u64>> = std::cell::RefCell::new(None);
}
pub fn page_size() -> std::io::Result<i64> {
if cfg!(unix) {
match unsafe { sysconf(_SC_PAGESIZE) } {
-1 => Err(std::io::Error::last_os_error()),
x => Ok(x.into()),
}
} else {
panic!("Not supported on non-unix platforms")
}
}
#[derive(Debug, PartialEq)]
pub enum ConfigSetting {
Yes,
Module,
Value(String),
}
pub fn kernel_config() -> ProcResult<HashMap<String, ConfigSetting>> {
use libflate::gzip::Decoder;
let reader: Box<dyn BufRead> = if Path::new(PROC_CONFIG_GZ).exists() {
let file = FileWrapper::open(PROC_CONFIG_GZ)?;
let decoder = Decoder::new(file)?;
Box::new(BufReader::new(decoder))
} else {
let mut kernel: libc::utsname = unsafe { mem::zeroed() };
if unsafe { libc::uname(&mut kernel) != 0 } {
return Err(ProcError::Other("Failed to call uname()".to_string()));
}
let filename = format!(
"{}-{}",
BOOT_CONFIG,
unsafe { CStr::from_ptr(kernel.release.as_ptr() as *const c_char) }.to_string_lossy()
);
if Path::new(&filename).exists() {
let file = FileWrapper::open(filename)?;
Box::new(BufReader::new(file))
} else {
let file = FileWrapper::open(BOOT_CONFIG)?;
Box::new(BufReader::new(file))
}
};
let mut map = HashMap::new();
for line in reader.lines() {
let line = line?;
if line.starts_with('#') {
continue;
}
if line.contains('=') {
let mut s = line.splitn(2, '=');
let name = expect!(s.next()).to_owned();
let value = match expect!(s.next()) {
"y" => ConfigSetting::Yes,
"m" => ConfigSetting::Module,
s => ConfigSetting::Value(s.to_owned()),
};
map.insert(name, value);
}
}
Ok(map)
}
#[derive(Debug)]
pub struct CpuTime {
pub user: f32,
pub nice: f32,
pub system: f32,
pub idle: f32,
pub iowait: Option<f32>,
pub irq: Option<f32>,
pub softirq: Option<f32>,
pub steal: Option<f32>,
pub guest: Option<f32>,
pub guest_nice: Option<f32>,
}
impl CpuTime {
fn from_str(s: &str) -> ProcResult<CpuTime> {
let mut s = s.split_whitespace();
s.next();
let user = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
let nice = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
let system = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
let idle = from_str!(u64, expect!(s.next())) as f32 / *TICKS_PER_SECOND as f32;
let iowait = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
let irq = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
let softirq = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
let steal = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
let guest = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
let guest_nice = s
.next()
.map(|s| Ok(from_str!(u64, s) as f32 / *TICKS_PER_SECOND as f32))
.transpose()?;
Ok(CpuTime {
user,
nice,
system,
idle,
iowait,
irq,
softirq,
steal,
guest,
guest_nice,
})
}
}
#[derive(Debug)]
pub struct KernelStats {
pub total: CpuTime,
pub cpu_time: Vec<CpuTime>,
pub ctxt: u64,
pub btime: u64,
pub processes: u64,
pub procs_running: Option<u32>,
pub procs_blocked: Option<u32>,
}
impl KernelStats {
pub fn new() -> ProcResult<KernelStats> {
KernelStats::from_reader(FileWrapper::open("/proc/stat")?)
}
fn from_reader<R: io::Read>(r: R) -> ProcResult<KernelStats> {
let bufread = BufReader::new(r);
let lines = bufread.lines();
let mut total_cpu = None;
let mut cpus = Vec::new();
let mut ctxt = None;
let mut btime = None;
let mut processes = None;
let mut procs_running = None;
let mut procs_blocked = None;
for line in lines {
let line = line?;
if line.starts_with("cpu ") {
total_cpu = Some(CpuTime::from_str(&line)?);
} else if line.starts_with("cpu") {
cpus.push(CpuTime::from_str(&line)?);
} else if line.starts_with("ctxt ") {
ctxt = Some(from_str!(u64, &line[5..]));
} else if line.starts_with("btime ") {
btime = Some(from_str!(u64, &line[6..]));
} else if line.starts_with("processes ") {
processes = Some(from_str!(u64, &line[10..]));
} else if line.starts_with("procs_running ") {
procs_running = Some(from_str!(u32, &line[14..]));
} else if line.starts_with("procs_blocked ") {
procs_blocked = Some(from_str!(u32, &line[14..]));
}
}
Ok(KernelStats {
total: expect!(total_cpu),
cpu_time: cpus,
ctxt: expect!(ctxt),
btime: expect!(btime),
processes: expect!(processes),
procs_running,
procs_blocked,
})
}
}
pub fn vmstat() -> ProcResult<HashMap<String, i64>> {
let file = FileWrapper::open("/proc/vmstat")?;
let reader = BufReader::new(file);
let mut map = HashMap::new();
for line in reader.lines() {
let line = line?;
let mut split = line.split_whitespace();
let name = expect!(split.next());
let val = from_str!(i64, expect!(split.next()));
map.insert(name.to_owned(), val);
}
Ok(map)
}
#[derive(Debug)]
pub struct KernelModule {
pub name: String,
pub size: u32,
pub refcount: i32,
pub used_by: Vec<String>,
pub state: String,
}
pub fn modules() -> ProcResult<HashMap<String, KernelModule>> {
let mut map = HashMap::new();
let file = FileWrapper::open("/proc/modules")?;
let reader = BufReader::new(file);
for line in reader.lines() {
let line: String = line?;
let mut s = line.split_whitespace();
let name = expect!(s.next());
let size = from_str!(u32, expect!(s.next()));
let refcount = from_str!(i32, expect!(s.next()));
let used_by: &str = expect!(s.next());
let state = expect!(s.next());
map.insert(
name.to_string(),
KernelModule {
name: name.to_string(),
size,
refcount,
used_by: if used_by == "-" {
Vec::new()
} else {
used_by
.split(',')
.filter(|s| !s.is_empty())
.map(|s| s.to_string())
.collect()
},
state: state.to_string(),
},
);
}
Ok(map)
}
#[cfg(test)]
mod tests {
extern crate failure;
use super::process::Process;
use super::*;
#[test]
fn test_statics() {
println!("{:?}", *TICKS_PER_SECOND);
println!("{:?}", *KERNEL);
println!("{:?}", *PAGESIZE);
}
#[test]
fn test_kernel_from_str() {
let k = KernelVersion::from_str("1.2.3").unwrap();
assert_eq!(k.major, 1);
assert_eq!(k.minor, 2);
assert_eq!(k.patch, 3);
let k = KernelVersion::from_str("4.9.16-gentoo").unwrap();
assert_eq!(k.major, 4);
assert_eq!(k.minor, 9);
assert_eq!(k.patch, 16);
}
#[test]
fn test_kernel_cmp() {
let a = KernelVersion::from_str("1.2.3").unwrap();
let b = KernelVersion::from_str("1.2.3").unwrap();
let c = KernelVersion::from_str("1.2.4").unwrap();
let d = KernelVersion::from_str("1.5.4").unwrap();
let e = KernelVersion::from_str("2.5.4").unwrap();
assert_eq!(a, b);
assert!(a < c);
assert!(a < d);
assert!(a < e);
assert!(e > d);
assert!(e > c);
assert!(e > b);
}
#[test]
fn test_loadavg() {
let load = LoadAverage::new().unwrap();
println!("{:?}", load);
}
#[test]
fn test_from_str() -> ProcResult<()> {
assert_eq!(from_str!(u8, "12"), 12);
assert_eq!(from_str!(u8, "A", 16), 10);
Ok(())
}
#[test]
fn test_from_str_fail() {
fn inner() -> ProcResult<()> {
let s = "four";
from_str!(u8, s);
unreachable!()
}
assert!(inner().is_err())
}
#[test]
fn test_kernel_config() {
match std::env::var("TRAVIS") {
Ok(ref s) if s == "true" => return,
_ => {}
}
if !Path::new(PROC_CONFIG_GZ).exists() && !Path::new(BOOT_CONFIG).exists() {
return;
}
let config = kernel_config().unwrap();
println!("{:#?}", config);
}
#[test]
fn test_file_io_errors() {
fn inner<P: AsRef<Path>>(p: P) -> Result<(), ProcError> {
let mut file = FileWrapper::open(p)?;
let mut buf = [0; 128];
file.read_exact(&mut buf[0..128])?;
Ok(())
}
let err = inner("/this_should_not_exist").unwrap_err();
println!("{}", err);
match err {
ProcError::NotFound(Some(p)) => {
assert_eq!(p, Path::new("/this_should_not_exist"));
}
x => panic!("Unexpected return value: {:?}", x),
}
match inner("/proc/loadavg") {
Err(ProcError::Io(_, Some(p))) => {
assert_eq!(p, Path::new("/proc/loadavg"));
}
x => panic!("Unexpected return value: {:?}", x),
}
}
#[test]
fn test_failure() {
fn inner() -> Result<(), failure::Error> {
let _load = LoadAverage::new()?;
Ok(())
}
let _ = inner();
fn inner2() -> Result<(), failure::Error> {
let proc = Process::new(1)?;
let _io = proc.maps()?;
Ok(())
}
let _ = inner2();
}
#[test]
fn test_nopanic() {
fn _inner() -> ProcResult<bool> {
let x: Option<bool> = None;
let y: bool = expect!(x);
Ok(y)
}
let r = _inner();
println!("{:?}", r);
assert!(r.is_err());
fn _inner2() -> ProcResult<bool> {
let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
Ok(true)
}
let r = _inner2();
println!("{:?}", r);
assert!(r.is_err());
}
#[cfg(feature = "backtrace")]
#[test]
fn test_backtrace() {
fn _inner() -> ProcResult<bool> {
let _f: std::fs::File = expect!(std::fs::File::open("/doesnotexist"));
Ok(true)
}
let r = _inner();
println!("{:?}", r);
}
#[test]
fn test_kernel_stat() {
let stat = KernelStats::new().unwrap();
println!("{:#?}", stat);
let boottime = boot_time_secs().unwrap();
let diff = (boottime as i32 - stat.btime as i32).abs();
assert!(diff <= 1);
let cpuinfo = CpuInfo::new().unwrap();
assert_eq!(cpuinfo.num_cores(), stat.cpu_time.len());
let user: f32 = stat.cpu_time.iter().map(|i| i.user).sum();
let nice: f32 = stat.cpu_time.iter().map(|i| i.nice).sum();
let system: f32 = stat.cpu_time.iter().map(|i| i.system).sum();
assert!(
(stat.total.user - user).abs() < 60.0,
"sum:{} total:{} diff:{}",
stat.total.user,
user,
stat.total.user - user
);
assert!(
(stat.total.nice - nice).abs() < 60.0,
"sum:{} total:{} diff:{}",
stat.total.nice,
nice,
stat.total.nice - nice
);
assert!(
(stat.total.system - system).abs() < 60.0,
"sum:{} total:{} diff:{}",
stat.total.system,
system,
stat.total.system - system
);
let diff = stat.total.idle - stat.cpu_time.iter().map(|i| i.idle).sum::<f32>().abs();
assert!(diff < 10.0, "idle time difference too high: {}", diff);
}
#[test]
fn test_vmstat() {
let stat = vmstat().unwrap();
println!("{:?}", stat);
}
#[test]
fn test_modules() {
let mods = modules().unwrap();
for module in mods.values() {
println!("{:?}", module);
}
}
}