use std::collections::HashMap;
use std::str::FromStr;
use std::time::Duration;
use crate::disk::DiskIoCounters;
use crate::{read_file, Error, Result};
const DISK_SECTOR_SIZE: u64 = 512;
const PROC_DISKSTATS: &str = "/proc/diskstats";
const PROC_PARTITIONS: &str = "/proc/partitions";
impl FromStr for DiskIoCounters {
type Err = Error;
fn from_str(line: &str) -> Result<DiskIoCounters> {
let fields = match line.split_whitespace().collect::<Vec<_>>() {
fields if fields.len() >= 14 => Ok(fields),
_ => Err(Error::MissingData {
path: PROC_DISKSTATS.into(),
contents: line.to_string(),
}),
}?;
let parse = |s: &str| -> Result<u64> {
s.parse().map_err(|err| Error::ParseInt {
path: PROC_DISKSTATS.into(),
contents: line.to_string(),
source: err,
})
};
Ok(DiskIoCounters {
read_count: parse(fields[3])?,
write_count: parse(fields[7])?,
read_bytes: parse(fields[5])? * DISK_SECTOR_SIZE,
write_bytes: parse(fields[9])? * DISK_SECTOR_SIZE,
read_time: Duration::from_millis(parse(fields[6])?),
write_time: Duration::from_millis(parse(fields[10])?),
busy_time: Duration::from_millis(parse(fields[12])?),
read_merged_count: parse(fields[4])?,
write_merged_count: parse(fields[8])?,
})
}
}
fn get_partitions(contents: &str) -> Result<Vec<&str>> {
contents
.lines()
.skip(2)
.map(|line| {
let fields = match line.split_whitespace().collect::<Vec<_>>() {
fields if fields.len() >= 4 => Ok(fields),
_ => Err(Error::MissingData {
path: PROC_PARTITIONS.into(),
contents: line.to_string(),
}),
}?;
Ok(fields[3])
})
.collect()
}
pub(crate) fn disk_io_counters_per_partition() -> Result<HashMap<String, DiskIoCounters>> {
let contents = read_file(PROC_PARTITIONS)?;
let partitions = get_partitions(&contents)?;
let contents = read_file(PROC_DISKSTATS)?;
let mut io_counters: HashMap<String, DiskIoCounters> = HashMap::new();
for line in contents.lines() {
let fields = match line.split_whitespace().collect::<Vec<_>>() {
fields if fields.len() >= 14 => Ok(fields),
_ => Err(Error::MissingData {
path: PROC_DISKSTATS.into(),
contents: line.to_string(),
}),
}?;
let name = fields[2];
if partitions.contains(&name) {
io_counters.insert(String::from(name), DiskIoCounters::from_str(line)?);
}
}
Ok(io_counters)
}