use std::collections::HashMap;
const SECTOR_SIZE : u64 = 512;
const MICROSECONDS_IN_MILLISECOND : u64 = 1000;
pub struct DiskStatsReport {
devices: HashMap<String, DeviceDiskStats>,
}
pub struct IoStatsReport {
pub devices: HashMap<String, DeviceIoStats>,
}
impl DiskStatsReport {
pub fn from_output(output: &str) -> DiskStatsReport {
let mut devices = HashMap::new();
output.lines().skip(1).for_each(|line| {
let stats = DeviceDiskStats::from_line(line);
devices.insert(format!("{}:{}", stats.major, stats.minor), stats);
});
DiskStatsReport { devices }
}
pub fn delta(&self, other: &DiskStatsReport, delta_secs: f64) -> IoStatsReport {
let mut devices = HashMap::new();
for (device, stats) in self.devices.iter() {
if let Some(other_stats) = other.devices.get(device) {
if let Some(io_stats) = stats.delta(other_stats, delta_secs) {
devices.insert(device.clone(), io_stats);
}
}
}
IoStatsReport { devices }
}
}
pub struct DeviceDiskStats {
major: i32,
minor: i32,
name: String,
read_ios: u64,
read_merges: u64,
read_sectors: u64,
read_ticks: u64,
write_ios: u64,
write_merges: u64,
write_sectors: u64,
write_ticks: u64,
inflight : u64,
io_ticks: u64,
weighted_io_ticks: u64,
discard_ios: u64,
discard_merges: u64,
discard_sectors: u64,
discard_ticks: u64,
}
#[derive(Debug)]
pub struct DeviceIoStats {
pub read_ios: f64,
pub read_bw : f64,
pub read_io_size : f64,
pub read_latency : f64,
pub write_ios: f64,
pub write_bw : f64,
pub write_io_size : f64,
pub write_latency : f64,
pub discard_ios: f64,
pub discard_bw : f64,
pub discard_io_size : f64,
pub discard_latency : f64,
pub queue_depth : f64,
}
impl DeviceDiskStats {
pub fn from_line(line: &str) -> DeviceDiskStats {
let mut parts = line.split_whitespace();
let major = parts.next().unwrap().parse::<i32>().unwrap();
let minor = parts.next().unwrap().parse::<i32>().unwrap();
let name = parts.next().unwrap().to_string();
let read_ios = parts.next().unwrap().parse::<u64>().unwrap();
let read_merges = parts.next().unwrap().parse::<u64>().unwrap();
let read_sectors = parts.next().unwrap().parse::<u64>().unwrap();
let read_ticks = parts.next().unwrap().parse::<u64>().unwrap();
let write_ios = parts.next().unwrap().parse::<u64>().unwrap();
let write_merges = parts.next().unwrap().parse::<u64>().unwrap();
let write_sectors = parts.next().unwrap().parse::<u64>().unwrap();
let write_ticks = parts.next().unwrap().parse::<u64>().unwrap();
let inflight = parts.next().unwrap().parse::<u64>().unwrap();
let io_ticks = parts.next().unwrap().parse::<u64>().unwrap();
let weighted_io_ticks = parts.next().unwrap().parse::<u64>().unwrap();
let mut discard_ios = 0;
let mut discard_merges = 0;
let mut discard_sectors = 0;
let mut discard_ticks = 0;
if let Some(val) = parts.next() {
discard_ios = val.parse::<u64>().unwrap_or(0);
discard_merges = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
discard_sectors = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
discard_ticks = parts.next().unwrap_or("0").parse::<u64>().unwrap_or(0);
}
DeviceDiskStats {
major,
minor,
name,
read_ios,
read_merges,
read_sectors,
read_ticks,
write_ios,
write_merges,
write_sectors,
write_ticks,
inflight,
io_ticks,
weighted_io_ticks,
discard_ios,
discard_merges,
discard_sectors,
discard_ticks,
}
}
pub fn delta(&self, other: &DeviceDiskStats, delta_secs: f64) -> Option<DeviceIoStats> {
if self.major != other.major || self.minor != other.minor {
return None;
}
let mut stats = DeviceIoStats {
read_ios: ((self.read_ios - other.read_ios) as f64) / delta_secs,
write_ios: ((self.write_ios - other.write_ios) as f64) / delta_secs,
discard_ios: ((self.discard_ios - other.discard_ios) as f64) / delta_secs,
read_latency: 0.0,
read_io_size: 0.0,
read_bw: 0.0,
write_latency: 0.0,
write_io_size: 0.0,
write_bw: 0.0,
discard_latency: 0.0,
discard_io_size: 0.0,
discard_bw: 0.0,
queue_depth: 0.0,
};
if stats.read_ios > 0.0 {
let read_time = self.read_ticks - other.read_ticks;
stats.read_latency = (read_time as f64 / stats.read_ios) * MICROSECONDS_IN_MILLISECOND as f64 / delta_secs;
stats.read_bw = (((self.read_sectors - other.read_sectors) * SECTOR_SIZE) as f64) / delta_secs;
stats.read_io_size = stats.read_bw / stats.read_ios;
}
if stats.write_ios > 0.0 {
let write_time = self.write_ticks - other.write_ticks;
stats.write_latency = (write_time as f64 / stats.write_ios) * MICROSECONDS_IN_MILLISECOND as f64 / delta_secs;
stats.write_bw = (((self.write_sectors - other.write_sectors) * SECTOR_SIZE) as f64) / delta_secs;
stats.write_io_size = stats.write_bw / stats.write_ios;
}
if stats.discard_ios > 0.0 {
let discard_time = self.discard_ticks - other.discard_ticks;
stats.discard_latency = (discard_time as f64 / stats.discard_ios) * MICROSECONDS_IN_MILLISECOND as f64 / delta_secs;
stats.discard_bw = (((self.discard_sectors - other.discard_sectors) * SECTOR_SIZE) as f64) / delta_secs;
stats.discard_io_size = stats.discard_bw / stats.discard_ios;
}
stats.queue_depth = (self.inflight + other.inflight) as f64 / 2.0;
Some(stats)
}
}