use std::collections::HashMap;
use std::fs::File;
use std::io;
use std::time::Duration;
use crate::{util, Error};
pub struct DiskStats {
inner: HashMap<String, DiskStat>,
}
impl DiskStats {
const PATH: &'static str = "/proc/diskstats";
pub fn from_system() -> io::Result<Self> {
DiskStats::from_reader(File::open(Self::PATH)?)
}
fn from_reader(reader: impl io::Read) -> io::Result<Self> {
let mut reader = util::LineParser::new(reader);
let mut inner = HashMap::new();
loop {
match reader.parse_line(DiskStat::from_str) {
Ok(disk_stat) => {
if inner.insert(disk_stat.name.clone(), disk_stat).is_some() {
panic!("Duplicate device name in /proc/diskstats");
}
}
Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
Err(e) => return Err(e),
}
}
Ok(DiskStats { inner })
}
pub fn iter(&self) -> impl Iterator<Item = &DiskStat> {
self.inner.values()
}
}
impl std::ops::Deref for DiskStats {
type Target = HashMap<String, DiskStat>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl IntoIterator for DiskStats {
type IntoIter = std::collections::hash_map::IntoIter<String, DiskStat>;
type Item = (String, DiskStat);
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
pub struct DiskStat {
pub major: u64,
pub minor: u64,
pub name: String,
pub reads_completed: u64,
pub reads_merged: u64,
pub sectors_read: u64,
pub time_reading: Duration,
pub writes_completed: u64,
pub writes_merged: u64,
pub sectors_written: u64,
pub time_writing: Duration,
pub io_in_progress: u64,
pub time_io: Duration,
pub time_io_weighted: Duration,
}
macro_rules! err_msg {
($inner:expr, $msg:expr) => {
$inner.ok_or_else(|| Error::from($msg))
};
}
impl DiskStat {
fn from_str(input: &str) -> Result<DiskStat, Error> {
let (input, major) = err_msg!(util::parse_u64(input), "major number")?;
let (input, minor) = err_msg!(util::parse_u64(input), "minor number")?;
let (input, name) = err_msg!(util::parse_token(input), "device name")?;
let name = name.to_owned();
let (input, reads_completed) =
err_msg!(util::parse_u64(input), "reads completed successfully")?;
let (input, reads_merged) = err_msg!(util::parse_u64(input), "reads merged")?;
let (input, sectors_read) = err_msg!(util::parse_u64(input), "sectors read")?;
let (input, time_reading) = err_msg!(util::parse_u64(input), "time spent reading (ms)")?;
let time_reading = Duration::from_millis(time_reading);
let (input, writes_completed) =
err_msg!(util::parse_u64(input), "writes completed successfully")?;
let (input, writes_merged) = err_msg!(util::parse_u64(input), "writes merged")?;
let (input, sectors_written) = err_msg!(util::parse_u64(input), "sectors written")?;
let (input, time_writing) = err_msg!(util::parse_u64(input), "time writing")?;
let time_writing = Duration::from_millis(time_writing);
let (input, io_in_progress) =
err_msg!(util::parse_u64(input), "I/Os currently in progress")?;
let (input, time_io) = err_msg!(util::parse_u64(input), "time spent doing I/Os (ms)")?;
let time_io = Duration::from_millis(time_io);
let (_input, time_io_weighted) = err_msg!(
util::parse_u64(input),
"weighted time spent doing I/Os (ms)"
)?;
let time_io_weighted = Duration::from_millis(time_io_weighted);
Ok(DiskStat {
major,
minor,
name,
reads_completed,
reads_merged,
sectors_read,
time_reading,
writes_completed,
writes_merged,
sectors_written,
time_writing,
io_in_progress,
time_io,
time_io_weighted,
})
}
}
#[cfg(test)]
mod tests {
use super::DiskStats;
use std::io;
#[test]
fn proc_diskstats() {
let raw = "\
8 16 sdb 213 0 18712 564 0 0 0 0 0 217 794
8 17 sdb1 48 0 4688 157 0 0 0 0 0 164 227
8 18 sdb2 44 0 4656 204 0 0 0 0 0 167 254
8 19 sdb3 44 0 4656 187 0 0 0 0 0 164 234
8 0 sda 446866 32893 8168064 20164 339296 376515 86758441 4343530 0 250860 4704740
8 1 sda1 143 30 11462 24 1 0 8 0 0 50 64
8 2 sda2 46 0 4992 0 0 0 0 0 0 17 17
8 3 sda3 6 0 36 0 0 0 0 0 0 4 4
8 5 sda5 446599 32863 8148758 20140 331949 376515 86758433 4337104 0 233207 4686390
8 32 sdc 7354 0 1580168 91987 7 0 56 0 0 91374 96127
8 33 sdc1 7279 0 1575472 91310 7 0 56 0 0 90670 95424
11 0 sr0 0 0 0 0 0 0 0 0 0 0 0
";
let _stat = DiskStats::from_reader(io::Cursor::new(raw)).unwrap();
}
}