#![deny(warnings)]
#![deny(missing_docs)]
extern crate byteorder;
extern crate libc;
use std::borrow::Cow;
use std::collections::HashMap;
use std::io;
use std::marker::PhantomData;
mod ffi;
mod kstat_ctl;
pub mod kstat_named;
use kstat_ctl::{Kstat, KstatCtl};
use kstat_named::KstatNamedData;
#[derive(Debug)]
pub struct KstatData {
pub class: String,
pub module: String,
pub instance: i32,
pub name: String,
pub snaptime: i64,
pub crtime: i64,
pub data: HashMap<String, KstatNamedData>,
}
#[derive(Debug)]
pub struct KstatReader<'a> {
module: Option<Cow<'a, str>>,
instance: Option<i32>,
name: Option<Cow<'a, str>>,
class: Option<Cow<'a, str>>,
ctl: KstatCtl,
}
impl<'a> KstatReader<'a> {
pub fn new<S>(
module: Option<S>,
instance: Option<i32>,
name: Option<S>,
class: Option<S>,
) -> io::Result<Self>
where
S: Into<Cow<'a, str>>,
{
let ctl = KstatCtl::new()?;
let module = module.map_or(None, |m| Some(m.into()));
let name = name.map_or(None, |n| Some(n.into()));
let class = class.map_or(None, |c| Some(c.into()));
Ok(KstatReader {
module,
instance,
name,
class,
ctl,
})
}
pub fn read(&self) -> io::Result<Vec<KstatData>> {
self.ctl.chain_update()?;
let mut ret = Vec::new();
let mut kstat_ptr = self.ctl.get_chain();
while !kstat_ptr.is_null() {
let kstat = Kstat {
inner: kstat_ptr,
_marker: PhantomData,
};
kstat_ptr = unsafe { (*kstat_ptr).ks_next };
let ks_type = kstat.get_type();
if ks_type != ffi::KSTAT_TYPE_NAMED && ks_type != ffi::KSTAT_TYPE_IO {
continue;
}
if self.module.is_some() && kstat.get_module() != *self.module.as_ref().unwrap() {
continue;
}
if self.instance.is_some() && kstat.get_instance() != *self.instance.as_ref().unwrap() {
continue;
}
if self.name.is_some() && kstat.get_name() != *self.name.as_ref().unwrap() {
continue;
}
if self.class.is_some() && kstat.get_class() != *self.class.as_ref().unwrap() {
continue;
}
match kstat.read(&self.ctl) {
Ok(k) => ret.push(k),
Err(e) => {
match e.raw_os_error().unwrap() {
libc::ENXIO => continue,
libc::EIO => continue,
_ => return Err(e),
}
}
}
}
Ok(ret)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_reader() {
let reader =
KstatReader::new::<String>(None, None, None, None).expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
assert!(stats.len() > 0);
}
#[test]
fn module_reader() {
let module = "cpu";
let reader =
KstatReader::new(Some(module), None, None, None).expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
for stat in stats {
assert_eq!(stat.module, module);
}
}
#[test]
fn instance_reader() {
let instance: i32 = 0;
let reader = KstatReader::new::<String>(None, Some(instance), None, None)
.expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
for stat in stats {
assert_eq!(stat.instance, instance);
}
}
#[test]
fn name_reader() {
let name = "vm";
let reader =
KstatReader::new(None, None, Some(name), None).expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
for stat in stats {
assert_eq!(stat.name, name);
}
}
#[test]
fn class_reader() {
let class = "misc";
let reader =
KstatReader::new(None, None, None, Some(class)).expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
for stat in stats {
assert_eq!(stat.class, class);
}
}
#[test]
fn module_instance_name_class_reader() {
let module = "unix";
let instance = 1;
let name = "kmem_alloc_16";
let class = "keme_cache";
let reader = KstatReader::new(Some(module), Some(instance), Some(name), Some(class))
.expect("failed to create reader");
let stats = reader.read().expect("failed to read kstat(s)");
for stat in stats {
assert_eq!(stat.module, module);
assert_eq!(stat.instance, instance);
assert_eq!(stat.name, name);
assert_eq!(stat.class, class);
}
}
}