Bug 1689358 - Generate minidumps for child process crashes using the minidump-writer...
[gecko.git] / third_party / rust / procfs-core / src / pressure.rs
blob7bf3caf0a47e993684bf71e66668ebed18fc3d68
1 //! Pressure stall information retreived from `/proc/pressure/cpu`,
2 //! `/proc/pressure/memory` and `/proc/pressure/io`
3 //! may not be available on kernels older than 4.20.0
4 //! For reference: <https://lwn.net/Articles/759781/>
5 //!
6 //! See also: <https://www.kernel.org/doc/Documentation/accounting/psi.txt>
8 use crate::{ProcError, ProcResult};
9 use std::collections::HashMap;
11 #[cfg(feature = "serde1")]
12 use serde::{Deserialize, Serialize};
14 /// Pressure stall information for either CPU, memory, or IO.
15 ///
16 /// See also: <https://www.kernel.org/doc/Documentation/accounting/psi.txt>
17 #[derive(Debug, Clone)]
18 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
19 pub struct PressureRecord {
20     /// 10 second window
21     ///
22     /// The percentage of time, over a 10 second window, that either some or all tasks were stalled
23     /// waiting for a resource.
24     pub avg10: f32,
25     /// 60 second window
26     ///
27     /// The percentage of time, over a 60 second window, that either some or all tasks were stalled
28     /// waiting for a resource.
29     pub avg60: f32,
30     /// 300 second window
31     ///
32     /// The percentage of time, over a 300 second window, that either some or all tasks were stalled
33     /// waiting for a resource.
34     pub avg300: f32,
35     /// Total stall time (in microseconds).
36     pub total: u64,
39 /// CPU pressure information
40 #[derive(Debug, Clone)]
41 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
42 pub struct CpuPressure {
43     pub some: PressureRecord,
46 impl super::FromBufRead for CpuPressure {
47     fn from_buf_read<R: std::io::BufRead>(mut r: R) -> ProcResult<Self> {
48         let mut some = String::new();
49         r.read_line(&mut some)?;
51         Ok(CpuPressure {
52             some: parse_pressure_record(&some)?,
53         })
54     }
57 /// Memory pressure information
58 #[derive(Debug, Clone)]
59 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
60 pub struct MemoryPressure {
61     /// This record indicates the share of time in which at least some tasks are stalled
62     pub some: PressureRecord,
63     /// This record indicates this share of time in which all non-idle tasks are stalled
64     /// simultaneously.
65     pub full: PressureRecord,
68 impl super::FromBufRead for MemoryPressure {
69     fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
70         let (some, full) = get_pressure(r)?;
71         Ok(MemoryPressure { some, full })
72     }
75 /// IO pressure information
76 #[derive(Debug, Clone)]
77 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
78 pub struct IoPressure {
79     /// This record indicates the share of time in which at least some tasks are stalled
80     pub some: PressureRecord,
81     /// This record indicates this share of time in which all non-idle tasks are stalled
82     /// simultaneously.
83     pub full: PressureRecord,
86 impl super::FromBufRead for IoPressure {
87     fn from_buf_read<R: std::io::BufRead>(r: R) -> ProcResult<Self> {
88         let (some, full) = get_pressure(r)?;
89         Ok(IoPressure { some, full })
90     }
93 fn get_f32(map: &HashMap<&str, &str>, value: &str) -> ProcResult<f32> {
94     map.get(value).map_or_else(
95         || Err(ProcError::Incomplete(None)),
96         |v| v.parse::<f32>().map_err(|_| ProcError::Incomplete(None)),
97     )
100 fn get_total(map: &HashMap<&str, &str>) -> ProcResult<u64> {
101     map.get("total").map_or_else(
102         || Err(ProcError::Incomplete(None)),
103         |v| v.parse::<u64>().map_err(|_| ProcError::Incomplete(None)),
104     )
107 fn parse_pressure_record(line: &str) -> ProcResult<PressureRecord> {
108     let mut parsed = HashMap::new();
110     if !line.starts_with("some") && !line.starts_with("full") {
111         return Err(ProcError::Incomplete(None));
112     }
114     let values = &line[5..];
116     for kv_str in values.split_whitespace() {
117         let kv_split = kv_str.split('=');
118         let vec: Vec<&str> = kv_split.collect();
119         if vec.len() == 2 {
120             parsed.insert(vec[0], vec[1]);
121         }
122     }
124     Ok(PressureRecord {
125         avg10: get_f32(&parsed, "avg10")?,
126         avg60: get_f32(&parsed, "avg60")?,
127         avg300: get_f32(&parsed, "avg300")?,
128         total: get_total(&parsed)?,
129     })
132 fn get_pressure<R: std::io::BufRead>(mut r: R) -> ProcResult<(PressureRecord, PressureRecord)> {
133     let mut some = String::new();
134     r.read_line(&mut some)?;
135     let mut full = String::new();
136     r.read_line(&mut full)?;
137     Ok((parse_pressure_record(&some)?, parse_pressure_record(&full)?))
140 #[cfg(test)]
141 mod test {
142     use super::*;
143     use std::f32::EPSILON;
145     #[test]
146     fn test_parse_pressure_record() {
147         let record = parse_pressure_record("full avg10=2.10 avg60=0.12 avg300=0.00 total=391926").unwrap();
149         assert!(record.avg10 - 2.10 < EPSILON);
150         assert!(record.avg60 - 0.12 < EPSILON);
151         assert!(record.avg300 - 0.00 < EPSILON);
152         assert_eq!(record.total, 391_926);
153     }
155     #[test]
156     fn test_parse_pressure_record_errs() {
157         assert!(parse_pressure_record("avg10=2.10 avg60=0.12 avg300=0.00 total=391926").is_err());
158         assert!(parse_pressure_record("some avg10=2.10 avg300=0.00 total=391926").is_err());
159         assert!(parse_pressure_record("some avg10=2.10 avg60=0.00 avg300=0.00").is_err());
160     }