mprober_lib/process/
process_stat.rs

1use std::{io::ErrorKind, path::Path, str::from_utf8_unchecked};
2
3use page_size::get as get_page_size;
4
5use crate::{
6    process::ProcessState,
7    scanner_rust::{
8        generic_array::typenum::{U192, U32},
9        Scanner, ScannerError,
10    },
11};
12
13#[derive(Default, Debug, Clone)]
14pub struct ProcessStat {
15    pub state:        ProcessState,
16    pub comm:         String,
17    pub ppid:         u32,
18    pub pgrp:         u32,
19    pub session:      u32,
20    pub tty_nr_major: u8,
21    pub tty_nr_minor: u32,
22    pub tpgid:        Option<u32>,
23    pub utime:        u32,
24    pub stime:        u32,
25    pub cutime:       u32,
26    pub cstime:       u32,
27    pub priority:     i8,
28    pub nice:         i8,
29    pub num_threads:  usize,
30    pub starttime:    u64,
31    /// size, VmSize (total program size)
32    pub vsize:        usize,
33    /// resident, VmRSS (resident set size)
34    pub rss:          usize,
35    pub rsslim:       usize,
36    pub processor:    usize,
37    pub rt_priority:  u8,
38    /// RssFile + RssShmem (resident shared size)
39    pub shared:       usize,
40    /// VmRSS - RssFile - RssShmem = RssAnon (resident anonymous memory, process occupied memory)
41    pub rss_anon:     usize,
42}
43
44/// Get the stat of a specific process found by ID by reading the `/proc/PID/stat` file and the `/proc/PID/statm` file.
45///
46/// ```rust
47/// use mprober_lib::process;
48///
49/// let process_stat = process::get_process_stat(1).unwrap();
50///
51/// println!("{process_stat:#?}");
52/// ```
53pub fn get_process_stat(pid: u32) -> Result<ProcessStat, ScannerError> {
54    let mut stat = ProcessStat::default();
55
56    let stat_path = Path::new("/proc").join(pid.to_string()).join("stat");
57
58    let mut sc: Scanner<_, U192> = Scanner::scan_path2(stat_path)?;
59
60    sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
61
62    sc.drop_next_until("(")?.ok_or(ErrorKind::UnexpectedEof)?;
63
64    loop {
65        let comm = sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?;
66
67        if comm.ends_with(b")") {
68            stat.comm.push_str(unsafe { from_utf8_unchecked(&comm[..(comm.len() - 1)]) });
69            break;
70        } else {
71            stat.comm.push_str(unsafe { from_utf8_unchecked(comm.as_ref()) });
72        }
73    }
74
75    stat.state = ProcessState::from_str(unsafe {
76        from_utf8_unchecked(&sc.next_raw()?.ok_or(ErrorKind::UnexpectedEof)?)
77    })
78    .ok_or(ErrorKind::InvalidData)?;
79
80    stat.ppid = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
81    stat.pgrp = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
82    stat.session = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
83
84    {
85        let tty_nr = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
86
87        stat.tty_nr_major = (tty_nr >> 8) as u8;
88        stat.tty_nr_minor = ((tty_nr >> 20) << 8) | (tty_nr & 0xFF);
89    }
90
91    {
92        let tpgid = sc.next_i32()?.ok_or(ErrorKind::UnexpectedEof)?;
93
94        if tpgid >= 0 {
95            stat.tpgid = Some(tpgid as u32);
96        }
97    }
98
99    for _ in 0..5 {
100        sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
101    }
102
103    stat.utime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
104    stat.stime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
105    stat.cutime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
106    stat.cstime = sc.next_u32()?.ok_or(ErrorKind::UnexpectedEof)?;
107    stat.priority = sc.next_i8()?.ok_or(ErrorKind::UnexpectedEof)?;
108    stat.nice = sc.next_i8()?.ok_or(ErrorKind::UnexpectedEof)?;
109    stat.num_threads = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)?;
110
111    sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
112
113    stat.starttime = sc.next_u64()?.ok_or(ErrorKind::UnexpectedEof)?;
114    stat.vsize = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)?;
115    stat.rss = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)? * get_page_size();
116    stat.rsslim = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)?;
117
118    for _ in 0..13 {
119        sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
120    }
121
122    stat.processor = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)?;
123    stat.rt_priority = sc.next_u8()?.ok_or(ErrorKind::UnexpectedEof)?;
124
125    drop(sc);
126
127    let statm_path = Path::new("/proc").join(pid.to_string()).join("statm");
128
129    let mut sc: Scanner<_, U32> = Scanner::scan_path2(statm_path)?;
130
131    for _ in 0..2 {
132        sc.drop_next()?.ok_or(ErrorKind::UnexpectedEof)?;
133    }
134
135    stat.shared = sc.next_usize()?.ok_or(ErrorKind::UnexpectedEof)? * get_page_size();
136
137    stat.rss_anon = stat.rss - stat.shared;
138
139    Ok(stat)
140}