1use crate::act::ActionContext;
6use crate::act_structured::StructuredActionContext;
7use crate::metrics::metric_value::MetricValue;
8use crate::metrics::MetricState;
9use anyhow::{bail, Error};
10use injectable_time::{MonotonicInstant, TimeSource};
11use regex::Regex;
12
13pub(crate) mod act; pub(crate) mod act_structured; pub(crate) mod config; pub mod inspect_logger;
17pub(crate) mod metrics; pub(crate) mod plugins; pub(crate) mod result_format; pub(crate) mod validate; pub use act::{Action, ActionResults, SnapshotTrigger, WarningVec};
23pub use act_structured::TriageOutput;
24pub use config::{ActionTagDirective, DataFetcher, DiagnosticData, ParseResult, Source};
25pub use result_format::ActionResultFormatter;
26
27const DEVICE_UPTIME_KEY: &str = "device.uptime";
28
29fn time_from_snapshot(files: &[DiagnosticData]) -> Option<i64> {
30 if let Some(file) = files.iter().find(|file| file.source == Source::Annotations) {
31 if let DataFetcher::KeyValue(fetcher) = &file.data {
32 if let MetricValue::String(duration) = fetcher.fetch(DEVICE_UPTIME_KEY) {
33 let re = Regex::new(r"^(\d+)d(\d+)h(\d+)m(\d+)s$").unwrap();
34 if let Some(c) = re.captures(&duration) {
35 let dhms = (c.get(1), c.get(2), c.get(3), c.get(4));
36 if let (Some(d), Some(h), Some(m), Some(s)) = dhms {
37 let dhms = (
38 d.as_str().parse::<i64>(),
39 h.as_str().parse::<i64>(),
40 m.as_str().parse::<i64>(),
41 s.as_str().parse::<i64>(),
42 );
43 if let (Ok(d), Ok(h), Ok(m), Ok(s)) = dhms {
44 return Some(1_000_000_000 * (s + 60 * (m + 60 * (h + 24 * d))));
45 }
46 }
47 }
48 }
49 }
50 }
51 None
52}
53
54pub fn analyze(
58 diagnostic_data: &[DiagnosticData],
59 parse_result: &ParseResult,
60) -> Result<ActionResults, Error> {
61 inner_analyze(diagnostic_data, parse_result, false )
62}
63
64pub fn analyze_verbose(
68 diagnostic_data: &[DiagnosticData],
69 parse_result: &ParseResult,
70) -> Result<ActionResults, Error> {
71 inner_analyze(diagnostic_data, parse_result, true )
72}
73
74fn inner_analyze(
75 diagnostic_data: &[DiagnosticData],
76 parse_result: &ParseResult,
77 verbose: bool,
78) -> Result<ActionResults, Error> {
79 parse_result.reset_state();
80 let now = time_from_snapshot(diagnostic_data);
81 let mut action_context =
82 ActionContext::new(&parse_result.metrics, &parse_result.actions, diagnostic_data, now);
83 action_context.set_verbose(verbose);
84 Ok(action_context.process().clone())
85}
86
87pub fn analyze_structured(
90 diagnostic_data: &[DiagnosticData],
91 parse_result: &ParseResult,
92) -> Result<TriageOutput, Error> {
93 parse_result.reset_state();
94 let now = time_from_snapshot(diagnostic_data);
95 let mut structured_action_context = StructuredActionContext::new(
96 &parse_result.metrics,
97 &parse_result.actions,
98 diagnostic_data,
99 now,
100 );
101 Ok(structured_action_context.process().clone())
102}
103
104pub fn snapshots(
106 data: &[DiagnosticData],
107 parse_result: &ParseResult,
108) -> (Vec<SnapshotTrigger>, act::WarningVec) {
109 parse_result.reset_state();
110 let now = Some(MonotonicInstant::new().now());
111 let evaluator = ActionContext::new(&parse_result.metrics, &parse_result.actions, data, now);
112 evaluator.into_snapshots()
113}
114
115pub fn all_selectors(parse: &ParseResult) -> Vec<String> {
116 parse.all_selectors()
117}
118
119pub fn evaluate_int_math(expression: &str) -> Result<i64, Error> {
120 metric_value_to_int(MetricState::evaluate_math(expression))
121}
122
123pub fn metric_value_to_int(metric_value: MetricValue) -> Result<i64, Error> {
124 match metric_value {
125 MetricValue::Int(i) => Ok(i),
126 MetricValue::Float(f) => match metrics::safe_float_to_int(f) {
127 Some(i) => Ok(i),
128 None => bail!("Non-numeric float result {}", f),
129 },
130 MetricValue::Problem(problem) => bail!("Eval error: {:?}", problem),
131 bad_type => bail!("Non-numeric result: {:?}", bad_type),
132 }
133}
134
135#[cfg(test)]
136mod test {
137 use super::*;
138
139 #[fuchsia::test]
140 fn time_parses_correctly() {
141 fn file(name: &str, source: Source, contents: &str) -> DiagnosticData {
142 DiagnosticData::new(name.to_string(), source, contents.to_string()).unwrap()
143 }
144 assert_eq!(time_from_snapshot(&[]), None);
145 let files = vec![file("foo.json", Source::Annotations, r#"{"a":"b"}"#)];
147 assert_eq!(time_from_snapshot(&files), None);
148 let files = vec![file("any.name.works", Source::Annotations, r#"{"device.uptime":"b"}"#)];
149 assert_eq!(time_from_snapshot(&files), None);
150 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1h1m1s"}"#)];
151 assert_eq!(time_from_snapshot(&files), None);
152 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1d1h1m"}"#)];
153 assert_eq!(time_from_snapshot(&files), None);
154 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h0m0s"}"#)];
155 assert_eq!(time_from_snapshot(&files), Some(0));
156 let files = vec![file("a.b", Source::Annotations, r#"{"device.uptime":"2d3h4m5s"}"#)];
157 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 183845));
158 let files = vec![file("a.b", Source::Annotations, r#"{"device.uptime":"11d13h17m19s"}"#)];
159 let seconds = 19 + 17 * 60 + 13 * 3600 + 11 * 3600 * 24;
160 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * seconds));
161 let files = vec![file("", Source::Annotations, r#"{"device.uptime":"3d5h7m11s"}"#)];
162 let hours = 5 + 24 * 3;
163 let minutes = 7 + 60 * hours;
164 let seconds = 11 + 60 * minutes;
165 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * seconds));
166 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h0m1s"}"#)];
167 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000));
168 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d0h1m0s"}"#)];
169 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60));
170 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"0d1h0m0s"}"#)];
171 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60 * 60));
172 let files = vec![file("foo.json", Source::Annotations, r#"{"device.uptime":"1d0h0m0s"}"#)];
173 assert_eq!(time_from_snapshot(&files), Some(1_000_000_000 * 60 * 60 * 24));
174 }
175}