Skip to main content

proc_status/
status.rs

1use {
2    crate::*,
3    std::{
4        fs::File,
5        io::Read,
6    },
7};
8
9/// A snapshot of the status of a process
10///
11/// It's stored in a string to ensure consistency
12/// and can be kept around and compared.
13pub struct ProcStatus {
14    content: String,
15}
16
17impl ProcStatus {
18
19    /// read the proc status info of the current process.
20    ///
21    /// It's the same than `ProcStatus::read(ProcRef::ProcSelf)`
22    pub fn read() -> Result<Self, ProcStatusError> {
23        Self::read_for(ProcRef::ProcSelf)
24    }
25
26    /// read the proc status info of a process.
27    pub fn read_for(proc_ref: ProcRef) -> Result<Self, ProcStatusError> {
28        let mut file = match proc_ref {
29            ProcRef::ProcSelf => File::open("/proc/self/status"),
30            ProcRef::ProcId(id) => File::open(format!("/proc/{}/status", id)),
31        }?;
32        let mut content = String::new();
33        file.read_to_string(&mut content)?;
34        Ok(Self { content })
35    }
36
37    /// return an iterator over all key:value entries
38    pub fn entries(&self) -> ProcEntries<'_> {
39        ProcEntries::from_content(&self.content)
40    }
41
42    /// find an entry by name
43    pub fn entry(&self, key: &str) -> Result<ProcEntry<'_>, ProcStatusError> {
44        for entry in self.entries() {
45            let entry = entry?;
46            if entry.key == key {
47                return Ok(entry);
48            }
49        }
50        Err(ProcStatusError::EntryNotFound(key.to_string()))
51    }
52
53    /// return the value of an entry found by key
54    ///
55    /// Example:
56    /// ```
57    /// println!(
58    ///     "current process name: {:?}",
59    ///     proc_status::ProcStatus::read().unwrap().value("Name").unwrap(),
60    /// );
61    /// ```
62    /// Be careful that the values written as "xxx kB" are in
63    /// KiB, not kB, and are written this way for compatibility.
64    pub fn value(&self, key: &str) -> Result<&str, ProcStatusError> {
65        self.entry(key).map(|e| e.value)
66    }
67
68    /// return the value of a memory related entry in KiB
69    #[allow(non_snake_case)]
70    pub fn value_KiB(&self, key: &str) -> Result<usize, ProcStatusError> {
71        self.entry(key).and_then(|e| e.in_KiB())
72    }
73
74    /// return the current and peak ram usage of the process
75    pub fn mem_usage(&self) -> Result<MemUsage, ProcStatusError> {
76        let mut entries = self.entries();
77        while let Some(entry) = entries.next() {
78            let entry = entry?;
79            if entry.key == "VmPeak" {
80                let peak = entry.in_KiB()? * 1024; // proc/status data are in KiB
81                while let Some(entry) = entries.next() {
82                    let entry = entry?;
83                    if entry.key == "VmRSS" {
84                        let current = entry.in_KiB()? * 1024;
85                        return Ok(MemUsage { current, peak });
86                    }
87                }
88                return Err(ProcStatusError::EntryNotFound("VmRSS".to_string()));
89            }
90        }
91        Err(ProcStatusError::EntryNotFound("VmPeak".to_string()))
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    #[test]
99    fn test_read() {
100        let ps = ProcStatus::read().unwrap();
101        let name = ps.value("Name").unwrap();
102        println!("name: {:?}", name);
103        println!("VM peak: {:?}", ps.value("VmPeak").unwrap());
104        println!("VM peak KiB: {:?}", ps.entry("VmPeak").unwrap().in_KiB().unwrap());
105        mem_usage().unwrap();
106    }
107}