1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::{FileWrapper, ProcResult};
use std::io::{BufRead, BufReader};

/// Disk IO stat information
///
/// To fully understand these fields, please see the [iostats.txt](https://www.kernel.org/doc/Documentation/iostats.txt)
/// kernel documentation.
///
/// For an example, see the [diskstats.rs](https://github.com/eminence/procfs/tree/master/examples)
/// example in the source repo.
// Doc reference: https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
// Doc reference: https://www.kernel.org/doc/Documentation/iostats.txt
#[derive(Debug)]
pub struct DiskStat {
    /// The device major number
    pub major: i32,

    /// The device minor number
    pub minor: i32,

    /// Device name
    pub name: String,

    /// Reads completed successfully
    ///
    /// This is the total number of reads completed successfully
    pub reads: usize,

    /// Reads merged
    ///
    /// The number of adjacent reads that have been merged for efficiency.
    pub merged: usize,

    /// Sectors read successfully
    ///
    /// This is the total number of sectors read successfully.
    pub sectors_read: usize,

    /// Time spent reading (ms)
    pub time_reading: usize,

    /// writes completed
    pub writes: usize,

    /// writes merged
    ///
    /// The number of adjacent writes that have been merged for efficiency.
    pub writes_merged: usize,

    /// Sectors written successfully
    pub sectors_written: usize,

    /// Time spent writing (ms)
    pub time_writing: usize,

    /// I/Os currently in progress
    pub in_progress: usize,

    /// Time spent doing I/Os (ms)
    pub time_in_progress: usize,

    /// Weighted time spent doing I/Os (ms)
    pub weighted_time_in_progress: usize,

    /// Discards completed successfully
    ///
    /// (since kernel 4.18)
    pub discards: Option<usize>,

    /// Discards merged
    pub discards_merged: Option<usize>,

    /// Sectors discarded
    pub sectors_discarded: Option<usize>,

    /// Time spent discarding
    pub time_discarding: Option<usize>,

    /// Flush requests completed successfully
    ///
    /// (since kernel 5.5)
    pub flushes: Option<usize>,

    /// Time spent flushing
    pub time_flushing: Option<usize>,
}

/// Get disk IO stat info from /proc/diskstats
pub fn diskstats() -> ProcResult<Vec<DiskStat>> {
    let file = FileWrapper::open("/proc/diskstats")?;
    let reader = BufReader::new(file);
    let mut v = Vec::new();

    for line in reader.lines() {
        let line = line?;
        v.push(DiskStat::from_line(&line)?);
    }
    Ok(v)
}

impl DiskStat {
    pub fn from_line(line: &str) -> ProcResult<DiskStat> {
        let mut s = line.trim().split_whitespace();

        let major = from_str!(i32, expect!(s.next()));
        let minor = from_str!(i32, expect!(s.next()));
        let name = expect!(s.next()).to_string();
        let reads = from_str!(usize, expect!(s.next()));
        let merged = from_str!(usize, expect!(s.next()));
        let sectors_read = from_str!(usize, expect!(s.next()));
        let time_reading = from_str!(usize, expect!(s.next()));
        let writes = from_str!(usize, expect!(s.next()));
        let writes_merged = from_str!(usize, expect!(s.next()));
        let sectors_written = from_str!(usize, expect!(s.next()));
        let time_writing = from_str!(usize, expect!(s.next()));
        let in_progress = from_str!(usize, expect!(s.next()));
        let time_in_progress = from_str!(usize, expect!(s.next()));
        let weighted_time_in_progress = from_str!(usize, expect!(s.next()));
        let discards = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());
        let discards_merged = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());
        let sectors_discarded = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());
        let time_discarding = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());
        let flushes = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());
        let time_flushing = s.next().and_then(|s| usize::from_str_radix(s, 10).ok());

        Ok(DiskStat {
            major,
            minor,
            name,
            reads,
            merged,
            sectors_read,
            time_reading,
            writes,
            writes_merged,
            sectors_written,
            time_writing,
            in_progress,
            time_in_progress,
            weighted_time_in_progress,
            discards,
            discards_merged,
            sectors_discarded,
            time_discarding,
            flushes,
            time_flushing,
        })
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn diskstat() {
        for disk in super::diskstats().unwrap() {
            println!("{:?}", disk);
        }
    }
}