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
//! Process memory usage information from `/proc/[pid]/statm`.

use std::fs::File;
use std::io::Result;

use libc::pid_t;
use nom::{digit, line_ending, space};

use parsers::{map_result, parse_usize, read_to_end};

/// Process memory usage information.
///
/// All values are in units of pages.
///
/// See `man 5 proc` and `Linux/fs/proc/array.c`.
#[derive(Debug, Default, PartialEq, Eq, Hash)]
pub struct Statm {
    /// Total virtual memory size.
    pub size: usize,
    /// Resident non-swapped memory.
    pub resident: usize,
    /// Shared memory.
    pub share: usize,
    /// Resident executable memory.
    pub text: usize,
    /// Resident data and stack memory.
    pub data: usize,
}

/// Parses the statm file format.
named!(parse_statm<Statm>,
    chain!(size: parse_usize     ~ space ~
           resident: parse_usize ~ space ~
           share: parse_usize    ~ space ~
           text: parse_usize     ~ space ~
           digit                 ~ space ~         // lib - unused since linux 2.6
           data: parse_usize     ~ space ~
           digit                 ~ line_ending,    // dt - unused since linux 2.6
           || { Statm { size: size,
                        resident: resident,
                        share: share,
                        text: text,
                        data: data } }));

/// Parses the provided statm file.
fn statm_file(file: &mut File) -> Result<Statm> {
    let mut buf = [0; 256]; // A typical statm file is about 25 bytes
    map_result(parse_statm(try!(read_to_end(file, &mut buf))))
}

/// Returns memory status information for the process with the provided pid.
pub fn statm(pid: pid_t) -> Result<Statm> {
    statm_file(&mut try!(File::open(&format!("/proc/{}/statm", pid))))
}

/// Returns memory status information for the current process.
pub fn statm_self() -> Result<Statm> {
    statm_file(&mut try!(File::open("/proc/self/statm")))
}

/// Returns memory status information from the thread with the provided parent process ID and thread ID.
pub fn statm_task(process_id: pid_t, thread_id: pid_t) -> Result<Statm> {
    statm_file(&mut try!(File::open(&format!("/proc/{}/task/{}/statm", process_id, thread_id))))
}

#[cfg(test)]
mod tests {
    use parsers::tests::unwrap;
    use super::{parse_statm, statm, statm_self};

    /// Test that the system statm files can be parsed.
    #[test]
    fn test_statm() {
        statm_self().unwrap();
        statm(1).unwrap();
    }

    #[test]
    fn test_parse_statm() {
        let statm_text = b"11837 2303 1390 330 0 890 0\n";
        let statm = unwrap(parse_statm(statm_text));
        assert_eq!(11837, statm.size);
        assert_eq!(2303, statm.resident);
        assert_eq!(1390, statm.share);
        assert_eq!(330, statm.text);
        assert_eq!(890, statm.data);
    }
}

#[cfg(all(test, rustc_nightly))]
mod benches {
    extern crate test;

    use std::fs::File;

    use parsers::read_to_end;
    use super::{parse_statm, statm};

    #[bench]
    fn bench_statm(b: &mut test::Bencher) {
        b.iter(|| test::black_box(statm(1)));
    }

    #[bench]
    fn bench_statm_parse(b: &mut test::Bencher) {
        let mut buf = [0; 256];
        let statm = read_to_end(&mut File::open("/proc/1/statm").unwrap(), &mut buf).unwrap();
        b.iter(|| test::black_box(parse_statm(statm)));
    }
}