1use std::collections::HashMap;
6use std::sync::{Arc, Weak};
7
8use async_trait::async_trait;
9use errors::ModelError;
10use fuchsia_inspect as inspect;
11use fuchsia_inspect::{IntExponentialHistogramProperty, IntLinearHistogramProperty};
12use fuchsia_sync as fsync;
13use inspect::HistogramProperty;
14use moniker::Moniker;
15
16use hooks::{Event, EventPayload, EventType, HasEventType, Hook, HooksRegistration};
17
18const STARTED_DURATIONS: &str = "started_durations";
19const STOPPED_DURATIONS: &str = "stopped_durations";
20const HISTOGRAM: &str = "histogram";
21
22const STARTED_DURATIONS_HISTOGRAM_PARAMS: inspect::ExponentialHistogramParams<i64> =
24 inspect::ExponentialHistogramParams {
25 floor: 4,
26 initial_step: 3,
27 step_multiplier: 2,
28 buckets: 12,
29 };
30
31const STOPPED_DURATIONS_HISTOGRAM_PARAMS: inspect::LinearHistogramParams<i64> =
33 inspect::LinearHistogramParams { floor: 10, step_size: 10, buckets: 24 };
34
35type StopTime = zx::MonotonicInstant;
36
37pub struct DurationStats {
50 _node: inspect::Node,
52 started_durations: ComponentHistograms<IntExponentialHistogramProperty>,
53 stopped_durations: ComponentHistograms<IntLinearHistogramProperty>,
54 escrowing_components: fsync::Mutex<HashMap<Moniker, StopTime>>,
57}
58
59impl DurationStats {
60 pub fn new(node: inspect::Node) -> Self {
62 let started = node.create_child(STARTED_DURATIONS);
63 let histogram = started.create_child(HISTOGRAM);
64 node.record(started);
65 let started_durations = ComponentHistograms {
66 node: histogram,
67 properties: Default::default(),
68 init: |node, name| {
69 node.create_int_exponential_histogram(name, STARTED_DURATIONS_HISTOGRAM_PARAMS)
70 },
71 };
72
73 let stopped = node.create_child(STOPPED_DURATIONS);
74 let histogram = stopped.create_child(HISTOGRAM);
75 node.record(stopped);
76 let stopped_durations = ComponentHistograms {
77 node: histogram,
78 properties: Default::default(),
79 init: |node, name| {
80 node.create_int_linear_histogram(name, STOPPED_DURATIONS_HISTOGRAM_PARAMS)
81 },
82 };
83
84 Self {
85 _node: node,
86 started_durations,
87 stopped_durations,
88 escrowing_components: Default::default(),
89 }
90 }
91
92 pub fn hooks(self: &Arc<Self>) -> Vec<HooksRegistration> {
94 vec![HooksRegistration::new(
95 "DurationStats",
96 vec![EventType::Started, EventType::Stopped],
97 Arc::downgrade(self) as Weak<dyn Hook>,
98 )]
99 }
100
101 fn on_component_started(self: &Arc<Self>, moniker: &Moniker, start_time: zx::MonotonicInstant) {
102 if let Some(stop_time) = self.escrowing_components.lock().get(moniker) {
103 let duration = start_time - *stop_time;
104 self.stopped_durations.record(moniker, duration.into_seconds());
105 }
106 }
107
108 fn on_component_stopped(
109 self: &Arc<Self>,
110 moniker: &Moniker,
111 stop_time: zx::MonotonicInstant,
112 execution_duration: zx::MonotonicDuration,
113 requested_escrow: bool,
114 ) {
115 let mut escrowing_components = self.escrowing_components.lock();
116 if requested_escrow {
117 escrowing_components.insert(moniker.clone(), stop_time);
118 }
119 if !escrowing_components.contains_key(moniker) {
120 return;
121 }
122 self.started_durations.record(moniker, execution_duration.into_seconds());
123 }
124}
125
126struct ComponentHistograms<H: HistogramProperty<Type = i64>> {
131 node: inspect::Node,
132 properties: fsync::Mutex<HashMap<Moniker, H>>,
133 init: fn(&inspect::Node, String) -> H,
134}
135
136impl<H: HistogramProperty<Type = i64>> ComponentHistograms<H> {
137 fn record(&self, moniker: &Moniker, value: i64) {
138 let mut properties = self.properties.lock();
139
140 if let Some(histogram) = properties.get_mut(moniker) {
141 histogram.insert(value);
142 } else {
143 let histogram = (self.init)(&self.node, moniker.to_string());
144 histogram.insert(value);
145
146 properties.insert(moniker.clone(), histogram);
147 }
148 }
149}
150
151#[async_trait]
152impl Hook for DurationStats {
153 async fn on(self: Arc<Self>, event: &Event) -> Result<(), ModelError> {
154 let target_moniker = event
155 .target_moniker
156 .unwrap_instance_moniker_or(ModelError::UnexpectedComponentManagerMoniker)?;
157 match event.event_type() {
158 EventType::Started => {
159 if let EventPayload::Started { runtime, .. } = &event.payload {
160 self.on_component_started(target_moniker, runtime.start_time_monotonic);
161 }
162 }
163 EventType::Stopped => {
164 if let EventPayload::Stopped {
165 stop_time_monotonic,
166 execution_duration,
167 requested_escrow,
168 ..
169 } = &event.payload
170 {
171 self.on_component_stopped(
172 target_moniker,
173 *stop_time_monotonic,
174 *execution_duration,
175 *requested_escrow,
176 );
177 }
178 }
179 _ => {}
180 }
181 Ok(())
182 }
183}