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
//! Parsers and data structures for `/proc/[pid]/statm`.

use std::fs::File;
use std::io::Result;
use std::os::unix::raw::pid_t;

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

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

/// Provides information about memory usage, as measured in pages.
#[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")))
}

#[cfg(test)]
mod tests {

    extern crate test;

    use std::fs::File;

    use parsers::read_to_end;
    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);
    }

    #[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)));
    }
}