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 '{}' as a valid size. Something is wrong with the output of memory_monitor.",
65 value,
66 ),
67 "Tools>ffx>Profile>Memory".to_string(),
68 ));
69 None
70 }
71 }
72
73 }
74 _ => None,
75 }
76 })
77 .sorted_by(|a, b| a.2.partial_cmp(&b.2).unwrap())
78 .rev()
79 .for_each(|entry| {
80 results.push(Action::new_synthetic_string_gauge(entry.1.to_string(), None, Some(entry.0.to_string())));
81 });
82
83 results
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::metrics::fetch::InspectFetcher;
91
92 #[fuchsia::test]
93 fn test_crashes() {
94 let expected_gauges: Vec<String> =
95 vec!["TestCmx: 2G", "Other: 7M", "Abcd: 10.3k", "Bbb: 9999"]
96 .into_iter()
97 .map(|s| s.to_string())
98 .collect();
99 let expected_errors: Vec<String> =
100 vec!["[DEBUG: BAD DATA] Could not parse 'ABCD' as a valid size. Something is wrong with the output of memory_monitor."]
101 .into_iter()
102 .map(|s| s.to_string())
103 .collect();
104 let fetcher: InspectFetcher = r#"
105[
106 {
107 "moniker": "core/memory_monitor",
108 "metadata": {
109 "name": "root"
110 },
111 "payload": {
112 "root": {
113 "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"
114 }
115 }
116 }
117]
118"#
119 .try_into().expect("failed to parse inspect");
120
121 let empty_diagnostics_vec = Vec::new();
122
123 let mut inputs = FileDataFetcher::new(&empty_diagnostics_vec);
124 inputs.inspect = &fetcher;
125 let result = MemoryPlugin {}.run(&inputs);
126 assert_eq!(result.gauges, expected_gauges);
127 assert_eq!(result.errors, expected_errors);
128 }
129}