rigtop_core/linux/
procstat.rs1use std::fs;
10
11use super::SysRoots;
12
13pub fn thread_count(roots: &SysRoots, pid: u32) -> Option<u32> {
15 read(roots, pid)?.threads
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct ProcStat {
21 pub threads: Option<u32>,
22 pub nice: Option<i32>,
23}
24
25pub fn read(roots: &SysRoots, pid: u32) -> Option<ProcStat> {
27 let text = fs::read_to_string(roots.proc.join(pid.to_string()).join("stat")).ok()?;
28 Some(ProcStat {
29 threads: parse_thread_count(&text),
30 nice: parse_nice(&text),
31 })
32}
33
34pub(crate) fn parse_nice(stat: &str) -> Option<i32> {
36 let after_name = &stat[stat.rfind(')')? + 1..];
37 after_name.split_whitespace().nth(16)?.parse().ok()
38}
39
40pub(crate) fn parse_thread_count(stat: &str) -> Option<u32> {
44 let after_name = &stat[stat.rfind(')')? + 1..];
45 after_name.split_whitespace().nth(17)?.parse().ok()
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use crate::linux::testutil::write;
53
54 #[test]
55 fn counts_threads_even_with_odd_command_names() {
56 let stat = "4242 (Web Content (x)) S 1 4242 4242 0 -1 4194560 100 0 0 0 \
57 50 20 0 0 20 0 37 0 1234 567890 1000 18446744073709551615";
58 assert_eq!(parse_thread_count(stat), Some(37));
59 assert_eq!(parse_nice(stat), Some(0));
60 let niced = "77 (make) R 1 77 77 0 -1 4194560 100 0 0 0 50 20 0 0 30 10 1 0";
61 assert_eq!(parse_nice(niced), Some(10));
62 assert_eq!(parse_thread_count("1 (init) S 1"), None, "truncated");
63 assert_eq!(parse_thread_count("garbage"), None);
64 }
65
66 #[test]
67 fn reads_from_proc() {
68 let tmp = tempfile::tempdir().unwrap();
69 write(
70 tmp.path(),
71 "proc/7/stat",
72 "7 (rigtop) R 1 7 7 0 -1 0 0 0 0 0 0 0 0 0 20 0 3 0 1 1 1\n",
73 );
74 let roots = SysRoots {
75 sys: tmp.path().join("sys"),
76 proc: tmp.path().join("proc"),
77 };
78 assert_eq!(thread_count(&roots, 7), Some(3));
79 assert_eq!(thread_count(&roots, 8), None);
80 }
81}