fuchsia_triage/plugins/
memory.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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                // Short circuit if value could not be found. This is not an error.
35                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}