fuchsia_triage/plugins/
memory.rs
1use super::Plugin;
6use crate::act::Action;
7use crate::metrics::fetch::{FileDataFetcher, SelectorString};
8use crate::metrics::metric_value::MetricValue;
9use itertools::Itertools;
10
11pub struct MemoryPlugin();
12
13const SELECTOR: &str = "INSPECT:core/memory_monitor:root:current_digest";
14
15impl Plugin for MemoryPlugin {
16 fn name(&self) -> &'static str {
17 "memory"
18 }
19
20 fn display_name(&self) -> &'static str {
21 "Memory Summary"
22 }
23
24 fn run_structured(&self, inputs: &FileDataFetcher<'_>) -> Vec<Action> {
25 let mut results = Vec::new();
26 let val = match inputs
27 .inspect
28 .fetch(&SelectorString::try_from(SELECTOR.to_string()).expect("invalid selector"))
29 .into_iter()
30 .next()
31 {
32 Some(MetricValue::String(val)) => val,
33 _ => {
34 return results;
36 }
37 };
38
39 val.lines()
40 .filter_map(|line| {
41 let mut split = line.split(": ");
42 let (name, value) = (split.next(), split.next());
43 match (name, value) {
44 (Some(name), Some(value)) => {
45 if value.is_empty() || name == "Free" || name == "timestamp" {
46 return None;
47 }
48 let numeric = value.trim_matches(|c: char| !c.is_ascii_digit());
49 let (mult, parsed) = if value.ends_with("k") {
50 (1_000f64, numeric.parse::<f64>().ok())
51 } else if value.ends_with("M") {
52 (1_000_000f64, numeric.parse::<f64>().ok())
53 } else if value.ends_with("G") {
54 (1_000_000_000f64, numeric.parse::<f64>().ok())
55 } else {
56 (1f64, numeric.parse::<f64>().ok())
57 };
58
59 match parsed{
60 Some(parsed) => Some((name, value, mult*parsed)),
61 None => {
62 results.push(Action::new_synthetic_error(
63 format!(
64 "[DEBUG: BAD DATA] Could not parse '{value}' as a valid size. Something is wrong with the output of memory_monitor.",
65 ),
66 "Tools>ffx>Profile>Memory".to_string(),
67 ));
68 None
69 }
70 }
71
72 }
73 _ => None,
74 }
75 })
76 .sorted_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
77 .rev()
78 .for_each(|entry| {
79 results.push(Action::new_synthetic_string_gauge(entry.1.to_string(), None, Some(entry.0.to_string())));
80 });
81
82 results
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89 use crate::metrics::fetch::InspectFetcher;
90
91 #[fuchsia::test]
92 fn test_crashes() {
93 let expected_gauges: Vec<String> =
94 vec!["TestCmx: 2G", "Other: 7M", "Abcd: 10.3k", "Bbb: 9999"]
95 .into_iter()
96 .map(|s| s.to_string())
97 .collect();
98 let expected_errors: Vec<String> =
99 vec!["[DEBUG: BAD DATA] Could not parse 'ABCD' as a valid size. Something is wrong with the output of memory_monitor."]
100 .into_iter()
101 .map(|s| s.to_string())
102 .collect();
103 let fetcher: InspectFetcher = r#"
104[
105 {
106 "moniker": "core/memory_monitor",
107 "metadata": {
108 "name": "root"
109 },
110 "payload": {
111 "root": {
112 "current_digest": "Abcd: 10.3k\nOther: 7M\nBbb: 9999\n\nTestCmx: 2G\ninvalid_line\ninvalid: \ninvalid_again: ABCD\n\nFree: 100M\ntimestamp: 10234\n\n"
113 }
114 }
115 }
116]
117"#
118 .try_into().expect("failed to parse inspect");
119
120 let empty_diagnostics_vec = Vec::new();
121
122 let mut inputs = FileDataFetcher::new(&empty_diagnostics_vec);
123 inputs.inspect = &fetcher;
124 let result = MemoryPlugin {}.run(&inputs);
125 assert_eq!(result.gauges, expected_gauges);
126 assert_eq!(result.errors, expected_errors);
127 }
128}