Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_sys/src/proc_self.rs

4.7 KiB, 3 runs

created by r1870400018:10592, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Resource footprint of the current process from
2//! `/proc/self/status` and `/proc/self/stat`.
3
4use crate::parse::{
5 parse_num,
6 read_to_string,
7};
8
9use oxedyne_fe2o3_core::prelude::*;
10
11/// Fields extracted from `/proc/self/status`. All byte figures
12/// arrive from the kernel as kB and are converted on ingest.
13#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
14pub struct ProcSelf {
15 /// Resident set size (physical RAM currently in use).
16 pub rss_bytes: u64,
17 /// Virtual memory size.
18 pub vsize_bytes:u64,
19 /// Peak resident set size observed for this process.
20 pub peak_rss: u64,
21 /// Number of threads in the process.
22 pub threads: u32,
23 /// Number of voluntary context switches (cooperative).
24 pub voluntary_ctxt: u64,
25 /// Number of involuntary context switches (pre-empted).
26 pub involuntary_ctxt: u64,
27}
28
29impl ProcSelf {
30 /// Read `/proc/self/status` and parse the fields.
31 pub fn sample() -> Outcome<Self> {
32 let content = res!(read_to_string("/proc/self/status"));
33 Self::from_status(&content)
34 }
35
36 /// Parse a `/proc/self/status` body.
37 pub fn from_status(content: &str) -> Outcome<Self> {
38 let mut out = Self::default();
39 for line in content.lines() {
40 let (name, rest) = match line.split_once(':') {
41 Some(t) => t,
42 None => continue,
43 };
44 let rest = rest.trim();
45 match name {
46 "VmRSS" => {
47 let kb = res!(parse_kb(rest, "VmRSS"));
48 out.rss_bytes = kb.saturating_mul(1024);
49 },
50 "VmSize" => {
51 let kb = res!(parse_kb(rest, "VmSize"));
52 out.vsize_bytes = kb.saturating_mul(1024);
53 },
54 "VmPeak" => {
55 let kb = res!(parse_kb(rest, "VmPeak"));
56 out.peak_rss = kb.saturating_mul(1024);
57 },
58 "Threads" => {
59 out.threads = res!(parse_num::<u32>(rest, "Threads"));
60 },
61 "voluntary_ctxt_switches" => {
62 out.voluntary_ctxt = res!(parse_num::<u64>(rest, "voluntary_ctxt_switches"));
63 },
64 "nonvoluntary_ctxt_switches" => {
65 out.involuntary_ctxt = res!(parse_num::<u64>(rest, "nonvoluntary_ctxt_switches"));
66 },
67 _ => (),
68 }
69 }
70 Ok(out)
71 }
72}
73
74impl ProcSelf {
75 /// Cumulative CPU time (user plus system) consumed by the current
76 /// process, in kernel clock ticks. Read from `/proc/self/stat`
77 /// fields `utime` and `stime`. On Linux the tick rate is
78 /// conventionally 100 Hz, so one tick is 10 ms; comparing two
79 /// samples taken a known interval apart gives the fraction of a
80 /// core the process is burning. A process that blocks while idle
81 /// accrues almost no ticks, whereas one that busy-polls accrues
82 /// them continuously.
83 pub fn cpu_ticks() -> Outcome<u64> {
84 let content = res!(read_to_string("/proc/self/stat"));
85 Self::cpu_ticks_from_stat(&content)
86 }
87
88 /// Parse `utime + stime` from a `/proc/self/stat` body. The
89 /// second field (`comm`) can itself contain spaces and brackets,
90 /// so parsing begins after the final `)`.
91 pub fn cpu_ticks_from_stat(content: &str) -> Outcome<u64> {
92 let tail = match content.rfind(')') {
93 Some(i) => &content[i + 1..],
94 None => return Err(err!(
95 "No comm field terminator ')' found in /proc/self/stat body.";
96 Input, Invalid, Decode)),
97 };
98 // After the `)`, the first token is field 3 (state), so utime
99 // (field 14) and stime (field 15) sit at indices 11 and 12 of
100 // this zero-based token list.
101 let toks: Vec<&str> = tail.split_whitespace().collect();
102 let utime = match toks.get(11) {
103 Some(t) => res!(parse_num::<u64>(t, "utime")),
104 None => return Err(err!(
105 "Missing utime field in /proc/self/stat.";
106 Input, Missing, Decode)),
107 };
108 let stime = match toks.get(12) {
109 Some(t) => res!(parse_num::<u64>(t, "stime")),
110 None => return Err(err!(
111 "Missing stime field in /proc/self/stat.";
112 Input, Missing, Decode)),
113 };
114 Ok(utime.saturating_add(stime))
115 }
116}
117
118/// Parse a value of the form `<number> kB` into `u64` kilobytes.
119fn parse_kb(rest: &str, field: &str) -> Outcome<u64> {
120 let tok = match rest.split_whitespace().next() {
121 Some(t) => t,
122 None => return Err(err!(
123 "{} field is empty.", field;
124 Input, Missing, Decode)),
125 };
126 parse_num::<u64>(tok, field)
127}