Skip to main content

input_pipeline_dso/
inspect_handler.rs

1// Copyright 2021 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 crate::input_device::{Handled, InputDeviceEvent, InputDeviceType, InputEvent, InputEventType};
6use crate::input_handler::{Handler, InputHandler};
7use async_trait::async_trait;
8use fuchsia_inspect::health::Reporter;
9use fuchsia_inspect::{
10    self as inspect, ExponentialHistogramParams, HistogramProperty, Inspector, NumericProperty,
11    Property,
12};
13
14use fuchsia_sync::Mutex;
15use futures::FutureExt;
16use inspect::Node;
17use sorted_vec_map::SortedVecSet;
18use std::cell::RefCell;
19use std::collections::VecDeque;
20use std::fmt::Debug;
21use std::rc::Rc;
22use std::sync::Arc;
23use strum::EnumCount;
24
25const MAX_RECENT_EVENT_LOG_SIZE: usize = 125;
26const LATENCY_HISTOGRAM_PROPERTIES: ExponentialHistogramParams<i64> = ExponentialHistogramParams {
27    floor: 0,
28    initial_step: 1,
29    step_multiplier: 10,
30    // Seven buckets allows us to report
31    // *      < 0 msec (added automatically by Inspect)
32    // *      0-1 msec
33    // *     1-10 msec
34    // *   10-100 msec
35    // * 100-1000 msec
36    // *     1-10 sec
37    // *   10-100 sec
38    // * 100-1000 sec
39    // *    >1000 sec (added automatically by Inspect)
40    buckets: 7,
41};
42
43#[derive(Debug)]
44struct EventCounters {
45    /// A node that contains the counters below.
46    _node: inspect::Node,
47    /// The number of total events that this handler has seen so far.
48    events_count: inspect::UintProperty,
49    /// The number of events with a wake lease seen so far.
50    events_with_wake_lease_count: inspect::UintProperty,
51    /// The number of total handled events that this handler has seen so far.
52    handled_events_count: inspect::UintProperty,
53    /// The timestamp (in nanoseconds) when the last event was seen by this
54    /// handler (not when the event itself was generated). 0 if unset.
55    last_seen_timestamp_ns: inspect::IntProperty,
56    /// The event time at which the last recorded event was generated.
57    /// 0 if unset.
58    last_generated_timestamp_ns: inspect::IntProperty,
59}
60
61impl EventCounters {
62    fn create(root: &inspect::Node, event_type: InputEventType) -> EventCounters {
63        let node = root.create_child(format!("{}", event_type));
64        let events_count = node.create_uint("events_count", 0);
65        let events_with_wake_lease_count = node.create_uint("events_with_wake_lease_count", 0);
66        let handled_events_count = node.create_uint("handled_events_count", 0);
67        let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
68        let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
69        EventCounters {
70            _node: node,
71            events_count,
72            events_with_wake_lease_count,
73            handled_events_count,
74            last_seen_timestamp_ns,
75            last_generated_timestamp_ns,
76        }
77    }
78
79    pub fn count_event(
80        &self,
81        time: zx::MonotonicInstant,
82        event_time: zx::MonotonicInstant,
83        handled: &Handled,
84        has_wake_lease: bool,
85    ) {
86        self.events_count.add(1);
87        if has_wake_lease {
88            self.events_with_wake_lease_count.add(1);
89        }
90        if *handled == Handled::Yes {
91            self.handled_events_count.add(1);
92        }
93        self.last_seen_timestamp_ns.set(time.into_nanos());
94        self.last_generated_timestamp_ns.set(event_time.into_nanos());
95    }
96}
97
98#[derive(Debug)]
99pub(crate) struct CircularBuffer<T> {
100    // Size of CircularBuffer
101    _size: usize,
102    // VecDeque of recent events with capacity of `size`
103    _events: VecDeque<T>,
104}
105
106pub(crate) trait BufferNode {
107    fn get_name(&self) -> &'static str;
108    fn record_inspect(&self, node: &Node);
109}
110
111impl<T> CircularBuffer<T>
112where
113    T: BufferNode,
114{
115    pub(crate) fn new(size: usize) -> Self {
116        let events = VecDeque::with_capacity(size);
117        CircularBuffer { _size: size, _events: events }
118    }
119
120    pub(crate) fn push(&mut self, event: T) {
121        if self._events.len() >= self._size {
122            std::mem::drop(self._events.pop_front());
123        }
124        self._events.push_back(event);
125    }
126
127    pub(crate) fn record_all_lazy_inspect(
128        &self,
129        inspector: inspect::Inspector,
130    ) -> inspect::Inspector {
131        self._events.iter().enumerate().for_each(|(i, event)| {
132            // Include leading zeros so Inspect will display events in correct numerical order.
133            // Inspect displays nodes in alphabetical order by default.
134            inspector.root().record_child(format!("{:03}_{}", i, event.get_name()), move |node| {
135                event.record_inspect(node)
136            });
137        });
138        inspector
139    }
140}
141
142impl BufferNode for InputEvent {
143    fn get_name(&self) -> &'static str {
144        self.get_event_type()
145    }
146
147    fn record_inspect(&self, node: &Node) {
148        InputEvent::record_inspect(self, node);
149    }
150}
151
152/// A [InputHandler] that records various metrics about the flow of events.
153/// All events are passed through unmodified.  Some properties of those events
154/// may be exposed in the metrics.  No PII information should ever be exposed
155/// this way.
156pub struct InspectHandler<F> {
157    /// A function that obtains the current timestamp.
158    now: RefCell<F>,
159    /// A node that contains the statistics about this particular handler.
160    node: inspect::Node,
161    /// The number of total events that this handler has seen so far.
162    events_count: inspect::UintProperty,
163    /// The number of events with a wake lease seen so far.
164    events_with_wake_lease_count: inspect::UintProperty,
165    /// The timestamp (in nanoseconds) when the last event was seen by this
166    /// handler (not when the event itself was generated). 0 if unset.
167    last_seen_timestamp_ns: inspect::IntProperty,
168    /// The event time at which the last recorded event was generated.
169    /// 0 if unset.
170    last_generated_timestamp_ns: inspect::IntProperty,
171    /// An inventory of event counters by type.
172    events_by_type: [Option<EventCounters>; InputEventType::COUNT],
173    /// Log of recent events in the order they were received.
174    recent_events_log: Option<Arc<Mutex<CircularBuffer<InputEvent>>>>,
175    /// Histogram of latency from the binding timestamp for an `InputEvent` until
176    /// the time the `InputEvent` was observed by this handler. Reported in milliseconds,
177    /// because values less than 1 msec aren't especially interesting.
178    pipeline_latency_ms: inspect::IntExponentialHistogramProperty,
179    // This node records the health status of `InspectHandler`.
180    health_node: RefCell<fuchsia_inspect::health::Node>,
181}
182
183impl<F: FnMut() -> zx::MonotonicInstant + 'static> Debug for InspectHandler<F> {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        f.debug_struct("InspectHandler")
186            .field("node", &self.node)
187            .field("events_count", &self.events_count)
188            .field("events_with_wake_lease_count", &self.events_with_wake_lease_count)
189            .field("last_seen_timestamp_ns", &self.last_seen_timestamp_ns)
190            .field("last_generated_timestamp_ns", &self.last_generated_timestamp_ns)
191            .field("events_by_type", &self.events_by_type)
192            .field("recent_events_log", &self.recent_events_log)
193            .field("pipeline_latency_ms", &self.pipeline_latency_ms)
194            .finish()
195    }
196}
197
198impl<F: FnMut() -> zx::MonotonicInstant + 'static> Handler for InspectHandler<F> {
199    fn set_handler_healthy(self: std::rc::Rc<Self>) {
200        self.health_node.borrow_mut().set_ok();
201    }
202
203    fn set_handler_unhealthy(self: std::rc::Rc<Self>, msg: &str) {
204        self.health_node.borrow_mut().set_unhealthy(msg);
205    }
206
207    fn get_name(&self) -> &'static str {
208        "InspectHandler"
209    }
210
211    fn interest(&self) -> Vec<InputEventType> {
212        vec![
213            InputEventType::Keyboard,
214            InputEventType::LightSensor,
215            InputEventType::ConsumerControls,
216            InputEventType::Mouse,
217            InputEventType::TouchScreen,
218            InputEventType::Touchpad,
219            #[cfg(test)]
220            InputEventType::Fake,
221        ]
222    }
223}
224
225#[async_trait(?Send)]
226impl<F: FnMut() -> zx::MonotonicInstant + 'static> InputHandler for InspectHandler<F> {
227    async fn handle_input_event(self: Rc<Self>, input_event: InputEvent) -> Vec<InputEvent> {
228        fuchsia_trace::duration!("input", "inspect_handler");
229        let tracing_id = input_event.trace_id.unwrap_or_else(|| 0.into());
230        fuchsia_trace::flow_step!("input", "event_in_input_pipeline", tracing_id);
231
232        let event_time = input_event.event_time;
233        let now = (self.now.borrow_mut())();
234        self.events_count.add(1);
235
236        let has_wake_lease = match &input_event.device_event {
237            InputDeviceEvent::ConsumerControls(e) => e.wake_lease.is_some(),
238            InputDeviceEvent::Mouse(e) => e.wake_lease.is_some(),
239            InputDeviceEvent::TouchScreen(e) => e.wake_lease.is_some(),
240            _ => false,
241        };
242        if has_wake_lease {
243            self.events_with_wake_lease_count.add(1);
244        }
245        self.last_seen_timestamp_ns.set(now.into_nanos());
246        self.last_generated_timestamp_ns.set(event_time.into_nanos());
247        let event_type = InputEventType::from(&input_event.device_event);
248        self.events_by_type[event_type as usize]
249            .as_ref()
250            .unwrap_or_else(|| panic!("no event counters for {}", event_type))
251            .count_event(now, event_time, &input_event.handled, has_wake_lease);
252        if let Some(recent_events_log) = &self.recent_events_log {
253            recent_events_log.lock().push(input_event.clone());
254        }
255        self.pipeline_latency_ms.insert((now - event_time).into_millis());
256        vec![input_event]
257    }
258}
259
260/// Creates a new inspect handler instance.
261///
262/// `node` is the inspect node that will receive the stats.
263pub fn make_inspect_handler(
264    node: inspect::Node,
265    supported_input_devices: &SortedVecSet<&InputDeviceType>,
266    displays_recent_events: bool,
267) -> Rc<InspectHandler<fn() -> zx::MonotonicInstant>> {
268    InspectHandler::new_internal(
269        node,
270        zx::MonotonicInstant::get,
271        supported_input_devices,
272        displays_recent_events,
273    )
274}
275
276impl<F> InspectHandler<F> {
277    /// Creates a new inspect handler instance, using `now` to supply the current timestamp.
278    /// Expected to be useful in testing mainly.
279    fn new_internal(
280        node: inspect::Node,
281        now: F,
282        supported_input_devices: &SortedVecSet<&InputDeviceType>,
283        displays_recent_events: bool,
284    ) -> Rc<Self> {
285        let event_count = node.create_uint("events_count", 0);
286        let events_with_wake_lease_count_node = node.create_uint("events_with_wake_lease_count", 0);
287        let last_seen_timestamp_ns = node.create_int("last_seen_timestamp_ns", 0);
288        let last_generated_timestamp_ns = node.create_int("last_generated_timestamp_ns", 0);
289
290        let recent_events_log = match displays_recent_events {
291            true => {
292                let recent_events =
293                    Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
294                record_lazy_recent_events(&node, Arc::clone(&recent_events));
295                Some(recent_events)
296            }
297            false => None,
298        };
299
300        let pipeline_latency_ms = node
301            .create_int_exponential_histogram("pipeline_latency_ms", LATENCY_HISTOGRAM_PROPERTIES);
302
303        let mut health_node = fuchsia_inspect::health::Node::new(&node);
304        health_node.set_starting_up();
305
306        let mut events_by_type: [Option<EventCounters>; InputEventType::COUNT] = Default::default();
307        if supported_input_devices.contains(&InputDeviceType::Keyboard) {
308            events_by_type[InputEventType::Keyboard as usize] =
309                Some(EventCounters::create(&node, InputEventType::Keyboard));
310        }
311        if supported_input_devices.contains(&InputDeviceType::ConsumerControls) {
312            events_by_type[InputEventType::ConsumerControls as usize] =
313                Some(EventCounters::create(&node, InputEventType::ConsumerControls));
314        }
315        if supported_input_devices.contains(&InputDeviceType::LightSensor) {
316            events_by_type[InputEventType::LightSensor as usize] =
317                Some(EventCounters::create(&node, InputEventType::LightSensor));
318        }
319        if supported_input_devices.contains(&InputDeviceType::Mouse) {
320            events_by_type[InputEventType::Mouse as usize] =
321                Some(EventCounters::create(&node, InputEventType::Mouse));
322        }
323        if supported_input_devices.contains(&InputDeviceType::Touch) {
324            events_by_type[InputEventType::TouchScreen as usize] =
325                Some(EventCounters::create(&node, InputEventType::TouchScreen));
326            events_by_type[InputEventType::Touchpad as usize] =
327                Some(EventCounters::create(&node, InputEventType::Touchpad));
328        }
329        #[cfg(test)]
330        {
331            events_by_type[InputEventType::Fake as usize] =
332                Some(EventCounters::create(&node, InputEventType::Fake));
333        }
334
335        Rc::new(Self {
336            now: RefCell::new(now),
337            node,
338            events_count: event_count,
339            events_with_wake_lease_count: events_with_wake_lease_count_node,
340            last_seen_timestamp_ns,
341            last_generated_timestamp_ns,
342            events_by_type,
343            recent_events_log,
344            pipeline_latency_ms,
345            health_node: RefCell::new(health_node),
346        })
347    }
348}
349
350fn record_lazy_recent_events(
351    node: &inspect::Node,
352    recent_events: Arc<Mutex<CircularBuffer<InputEvent>>>,
353) {
354    node.record_lazy_child("recent_events_log", move || {
355        let recent_events_clone = Arc::clone(&recent_events);
356        async move {
357            let inspector = Inspector::default();
358            let events = recent_events_clone.lock();
359            Ok(events.record_all_lazy_inspect(inspector))
360        }
361        .boxed()
362    });
363}
364
365#[cfg(test)]
366mod tests {
367    use super::*;
368    use crate::input_device::{self, InputDeviceDescriptor, InputDeviceEvent};
369    use crate::keyboard_binding::KeyboardDeviceDescriptor;
370    use crate::light_sensor::types::Rgbc;
371    use crate::light_sensor_binding::{LightSensorDeviceDescriptor, LightSensorEvent};
372    use crate::mouse_binding::{
373        MouseDeviceDescriptor, MouseLocation, MousePhase, PrecisionScroll, WheelDelta,
374    };
375    use crate::testing_utilities::{
376        consumer_controls_device_descriptor, create_consumer_controls_event,
377        create_fake_handled_input_event, create_fake_input_event, create_keyboard_event,
378        create_mouse_event, create_touch_contact, create_touch_screen_event, create_touchpad_event,
379        next_client_old_stream,
380    };
381    use crate::touch_binding::{TouchScreenDeviceDescriptor, TouchpadDeviceDescriptor};
382    use crate::utils::Position;
383    use diagnostics_assertions::{AnyProperty, assert_data_tree};
384    use fidl_fuchsia_input_report::InputDeviceMarker;
385    use sorted_vec_map::SortedVecMap;
386    use test_case::test_case;
387
388    fn fixed_now() -> zx::MonotonicInstant {
389        zx::MonotonicInstant::ZERO + zx::MonotonicDuration::from_nanos(42)
390    }
391
392    #[fuchsia::test]
393    async fn circular_buffer_no_overflow() {
394        let mut circular_buffer = CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE);
395        assert_eq!(circular_buffer._size, MAX_RECENT_EVENT_LOG_SIZE);
396
397        let first_event_time = zx::MonotonicInstant::get();
398        circular_buffer.push(create_fake_input_event(first_event_time));
399        let second_event_time = zx::MonotonicInstant::get();
400        circular_buffer.push(create_fake_input_event(second_event_time));
401
402        // Fill up `events` VecDeque
403        for _i in 2..MAX_RECENT_EVENT_LOG_SIZE {
404            let curr_event_time = zx::MonotonicInstant::get();
405            circular_buffer.push(create_fake_input_event(curr_event_time));
406            match circular_buffer._events.back() {
407                Some(event) => assert_eq!(event.event_time, curr_event_time),
408                None => assert!(false),
409            }
410        }
411
412        // Verify first event at the front
413        match circular_buffer._events.front() {
414            Some(event) => assert_eq!(event.event_time, first_event_time),
415            None => assert!(false),
416        }
417
418        // CircularBuffer `events` should be full, pushing another event should remove the first event.
419        let last_event_time = zx::MonotonicInstant::get();
420        circular_buffer.push(create_fake_input_event(last_event_time));
421        match circular_buffer._events.front() {
422            Some(event) => assert_eq!(event.event_time, second_event_time),
423            None => assert!(false),
424        }
425        match circular_buffer._events.back() {
426            Some(event) => assert_eq!(event.event_time, last_event_time),
427            None => assert!(false),
428        }
429    }
430
431    #[fuchsia::test]
432    async fn recent_events_log_records_inspect() {
433        let inspector = fuchsia_inspect::Inspector::default();
434
435        let recent_events_log =
436            Arc::new(Mutex::new(CircularBuffer::new(MAX_RECENT_EVENT_LOG_SIZE)));
437        record_lazy_recent_events(inspector.root(), Arc::clone(&recent_events_log));
438
439        let keyboard_descriptor = InputDeviceDescriptor::Keyboard(KeyboardDeviceDescriptor {
440            keys: vec![fidl_fuchsia_input::Key::A, fidl_fuchsia_input::Key::B],
441            ..Default::default()
442        });
443        let mouse_descriptor = InputDeviceDescriptor::Mouse(MouseDeviceDescriptor {
444            device_id: 1u32,
445            absolute_x_range: None,
446            absolute_y_range: None,
447            wheel_v_range: None,
448            wheel_h_range: None,
449            buttons: None,
450        });
451        let touch_screen_descriptor =
452            InputDeviceDescriptor::TouchScreen(TouchScreenDeviceDescriptor {
453                device_id: 1,
454                contacts: vec![],
455            });
456        let touchpad_descriptor = InputDeviceDescriptor::Touchpad(TouchpadDeviceDescriptor {
457            device_id: 1,
458            contacts: vec![],
459        });
460
461        let pressed_buttons = SortedVecSet::from(vec![1u8, 21u8, 15u8]);
462        let mut pressed_buttons_vec: Vec<u64> = vec![];
463        pressed_buttons.iter().for_each(|button| {
464            pressed_buttons_vec.push(*button as u64);
465        });
466
467        let (light_sensor_proxy, _) = next_client_old_stream::<
468            InputDeviceMarker,
469            fidl_next_fuchsia_input_report::InputDevice,
470        >();
471
472        let recent_events = vec![
473            create_keyboard_event(
474                fidl_fuchsia_input::Key::A,
475                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
476                None,
477                &keyboard_descriptor,
478                None,
479            ),
480            create_consumer_controls_event(
481                vec![
482                    fidl_fuchsia_input::ConsumerControlButton::VolumeUp,
483                    fidl_fuchsia_input::ConsumerControlButton::VolumeUp,
484                    fidl_fuchsia_input::ConsumerControlButton::Pause,
485                    fidl_fuchsia_input::ConsumerControlButton::VolumeDown,
486                    fidl_fuchsia_input::ConsumerControlButton::MicMute,
487                    fidl_fuchsia_input::ConsumerControlButton::CameraDisable,
488                    fidl_fuchsia_input::ConsumerControlButton::FactoryReset,
489                    fidl_fuchsia_input::ConsumerControlButton::Reboot,
490                ],
491                zx::MonotonicInstant::get(),
492                &consumer_controls_device_descriptor(),
493            ),
494            create_mouse_event(
495                MouseLocation::Absolute(Position { x: 7.0f32, y: 15.0f32 }),
496                Some(WheelDelta { ticks: 5i64, physical_pixel: Some(8.0f32) }),
497                Some(WheelDelta { ticks: 10i64, physical_pixel: Some(8.0f32) }),
498                Some(PrecisionScroll::Yes),
499                MousePhase::Move,
500                SortedVecSet::from(vec![1u8]),
501                pressed_buttons.clone(),
502                zx::MonotonicInstant::get(),
503                &mouse_descriptor,
504            ),
505            create_touch_screen_event(
506                SortedVecMap::from_iter(vec![
507                    (
508                        fidl_fuchsia_ui_input::PointerEventPhase::Add,
509                        vec![create_touch_contact(1u32, Position { x: 10.0, y: 30.0 })],
510                    ),
511                    (
512                        fidl_fuchsia_ui_input::PointerEventPhase::Move,
513                        vec![create_touch_contact(1u32, Position { x: 11.0, y: 31.0 })],
514                    ),
515                ]),
516                zx::MonotonicInstant::get(),
517                &touch_screen_descriptor,
518            ),
519            create_touchpad_event(
520                vec![
521                    create_touch_contact(1u32, Position { x: 0.0, y: 0.0 }),
522                    create_touch_contact(2u32, Position { x: 10.0, y: 10.0 }),
523                ],
524                SortedVecSet::new(),
525                zx::MonotonicInstant::get(),
526                &touchpad_descriptor,
527            ),
528            InputEvent {
529                device_event: InputDeviceEvent::LightSensor(LightSensorEvent {
530                    device_proxy: light_sensor_proxy,
531                    rgbc: Rgbc { red: 1, green: 2, blue: 3, clear: 14747 },
532                }),
533                device_descriptor: InputDeviceDescriptor::LightSensor(
534                    LightSensorDeviceDescriptor {
535                        vendor_id: 1,
536                        product_id: 2,
537                        device_id: 3,
538                        sensor_layout: Rgbc { red: 1, green: 2, blue: 3, clear: 4 },
539                    },
540                ),
541                event_time: zx::MonotonicInstant::get(),
542                handled: input_device::Handled::No,
543                trace_id: None,
544            },
545            create_keyboard_event(
546                fidl_fuchsia_input::Key::B,
547                fidl_fuchsia_ui_input3::KeyEventType::Pressed,
548                None,
549                &keyboard_descriptor,
550                None,
551            ),
552        ];
553
554        for event in recent_events.into_iter() {
555            recent_events_log.lock().push(event);
556        }
557
558        assert_data_tree!(inspector, root: {
559            recent_events_log: {
560                "000_keyboard_event": {
561                    event_time: AnyProperty,
562                },
563                "001_consumer_controls_event": {
564                    event_time: AnyProperty,
565                    pressed_buttons: vec!["volume_up", "volume_up", "pause", "volume_down", "mic_mute", "camera_disable", "factory_reset", "reboot"],
566                },
567                "002_mouse_event": {
568                    event_time: AnyProperty,
569                    wheel_delta_v: {
570                        ticks: 5i64,
571                        physical_pixel: 8.0f64,
572                    },
573                    wheel_delta_h: {
574                        ticks: 10i64,
575                        physical_pixel: 8.0f64,
576                    },
577                    is_precision_scroll: "yes",
578                    phase: "move",
579                    affected_buttons: vec![1u64],
580                    pressed_buttons: pressed_buttons_vec.clone(),
581                },
582                "003_touch_screen_event": {
583                    event_time: AnyProperty,
584                    injector_contacts: {
585                        add: {
586                            "1": {},
587                        },
588                        change: {
589                            "1": {},
590                        },
591                        remove: {},
592                    },
593                    pressed_buttons: Vec::<String>::new(),
594                },
595                "004_touchpad_event": {
596                    event_time: AnyProperty,
597                    pressed_buttons: Vec::<u64>::new(),
598                    injector_contacts: {
599                        "1": {},
600                        "2": {},
601                    },
602                },
603                "005_light_sensor_event": {
604                    event_time: AnyProperty,
605                    red: 1u64,
606                    green: 2u64,
607                    blue: 3u64,
608                    clear: 14747u64,
609                },
610                "006_keyboard_event": {
611                    event_time: AnyProperty,
612                },
613            }
614        });
615    }
616
617    #[fuchsia::test]
618    async fn verify_inspect_no_recent_events_log() {
619        let inspector = inspect::Inspector::default();
620        let root = inspector.root();
621        let test_node = root.create_child("test_node");
622        let supported_input_devices: SortedVecSet<&InputDeviceType> = SortedVecSet::from([
623            &input_device::InputDeviceType::Keyboard,
624            &input_device::InputDeviceType::ConsumerControls,
625            &input_device::InputDeviceType::LightSensor,
626            &input_device::InputDeviceType::Mouse,
627            &input_device::InputDeviceType::Touch,
628        ]);
629
630        let handler = super::InspectHandler::new_internal(
631            test_node,
632            fixed_now,
633            &supported_input_devices,
634            /* displays_recent_events = */ false,
635        );
636        assert_data_tree!(inspector, root: {
637            test_node: contains {
638                events_count: 0u64,
639                last_seen_timestamp_ns: 0i64,
640                last_generated_timestamp_ns: 0i64,
641                consumer_controls: {
642                     events_count: 0u64,
643                     events_with_wake_lease_count: 0u64,
644                     handled_events_count: 0u64,
645                     last_generated_timestamp_ns: 0i64,
646                     last_seen_timestamp_ns: 0i64,
647                },
648                fake: {
649                     events_count: 0u64,
650                     events_with_wake_lease_count: 0u64,
651                     handled_events_count: 0u64,
652                     last_generated_timestamp_ns: 0i64,
653                     last_seen_timestamp_ns: 0i64,
654                },
655                keyboard: {
656                     events_count: 0u64,
657                     events_with_wake_lease_count: 0u64,
658                     handled_events_count: 0u64,
659                     last_generated_timestamp_ns: 0i64,
660                     last_seen_timestamp_ns: 0i64,
661                },
662                light_sensor: {
663                     events_count: 0u64,
664                     events_with_wake_lease_count: 0u64,
665                     handled_events_count: 0u64,
666                     last_generated_timestamp_ns: 0i64,
667                     last_seen_timestamp_ns: 0i64,
668                },
669                mouse: {
670                     events_count: 0u64,
671                     events_with_wake_lease_count: 0u64,
672                     handled_events_count: 0u64,
673                     last_generated_timestamp_ns: 0i64,
674                     last_seen_timestamp_ns: 0i64,
675                },
676                touch_screen: {
677                     events_count: 0u64,
678                     events_with_wake_lease_count: 0u64,
679                     handled_events_count: 0u64,
680                     last_generated_timestamp_ns: 0i64,
681                     last_seen_timestamp_ns: 0i64,
682                },
683                touchpad: {
684                    events_count: 0u64,
685                    events_with_wake_lease_count: 0u64,
686                    handled_events_count: 0u64,
687                    last_generated_timestamp_ns: 0i64,
688                    last_seen_timestamp_ns: 0i64,
689               },
690           }
691        });
692
693        handler
694            .clone()
695            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
696            .await;
697        assert_data_tree!(inspector, root: {
698            test_node: contains {
699                events_count: 1u64,
700                last_seen_timestamp_ns: 42i64,
701                last_generated_timestamp_ns: 43i64,
702                consumer_controls: {
703                     events_count: 0u64,
704                     events_with_wake_lease_count: 0u64,
705                     handled_events_count: 0u64,
706                     last_generated_timestamp_ns: 0i64,
707                     last_seen_timestamp_ns: 0i64,
708                },
709                fake: {
710                     events_count: 1u64,
711                     events_with_wake_lease_count: 0u64, // Fake event doesn't have wake lease in this test setup
712                     handled_events_count: 0u64,
713                     last_generated_timestamp_ns: 43i64,
714                     last_seen_timestamp_ns: 42i64,
715                },
716                keyboard: {
717                     events_count: 0u64,
718                     events_with_wake_lease_count: 0u64,
719                     handled_events_count: 0u64,
720                     last_generated_timestamp_ns: 0i64,
721                     last_seen_timestamp_ns: 0i64,
722                },
723                light_sensor: {
724                     events_count: 0u64,
725                     events_with_wake_lease_count: 0u64,
726                     handled_events_count: 0u64,
727                     last_generated_timestamp_ns: 0i64,
728                     last_seen_timestamp_ns: 0i64,
729                },
730                mouse: {
731                     events_count: 0u64,
732                     events_with_wake_lease_count: 0u64,
733                     handled_events_count: 0u64,
734                     last_generated_timestamp_ns: 0i64,
735                     last_seen_timestamp_ns: 0i64,
736                },
737                touch_screen: {
738                     events_count: 0u64,
739                     events_with_wake_lease_count: 0u64,
740                     handled_events_count: 0u64,
741                     last_generated_timestamp_ns: 0i64,
742                     last_seen_timestamp_ns: 0i64,
743                },
744                touchpad: {
745                    events_count: 0u64,
746                    events_with_wake_lease_count: 0u64,
747                    handled_events_count: 0u64,
748                    last_generated_timestamp_ns: 0i64,
749                    last_seen_timestamp_ns: 0i64,
750               },
751            }
752        });
753
754        handler
755            .clone()
756            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
757            .await;
758        assert_data_tree!(inspector, root: {
759            test_node: contains {
760                events_count: 2u64,
761                last_seen_timestamp_ns: 42i64,
762                last_generated_timestamp_ns: 44i64,
763                consumer_controls: {
764                     events_count: 0u64,
765                     events_with_wake_lease_count: 0u64,
766                     handled_events_count: 0u64,
767                     last_generated_timestamp_ns: 0i64,
768                     last_seen_timestamp_ns: 0i64,
769                },
770                fake: {
771                     events_count: 2u64,
772                     events_with_wake_lease_count: 0u64,
773                     handled_events_count: 0u64,
774                     last_generated_timestamp_ns: 44i64,
775                     last_seen_timestamp_ns: 42i64,
776                },
777                keyboard: {
778                     events_count: 0u64,
779                     events_with_wake_lease_count: 0u64,
780                     handled_events_count: 0u64,
781                     last_generated_timestamp_ns: 0i64,
782                     last_seen_timestamp_ns: 0i64,
783                },
784                light_sensor: {
785                     events_count: 0u64,
786                     events_with_wake_lease_count: 0u64,
787                     handled_events_count: 0u64,
788                     last_generated_timestamp_ns: 0i64,
789                     last_seen_timestamp_ns: 0i64,
790                },
791                mouse: {
792                     events_count: 0u64,
793                     events_with_wake_lease_count: 0u64,
794                     handled_events_count: 0u64,
795                     last_generated_timestamp_ns: 0i64,
796                     last_seen_timestamp_ns: 0i64,
797                },
798                touch_screen: {
799                     events_count: 0u64,
800                     events_with_wake_lease_count: 0u64,
801                     handled_events_count: 0u64,
802                     last_generated_timestamp_ns: 0i64,
803                     last_seen_timestamp_ns: 0i64,
804                },
805                touchpad: {
806                    events_count: 0u64,
807                    events_with_wake_lease_count: 0u64,
808                    handled_events_count: 0u64,
809                    last_generated_timestamp_ns: 0i64,
810                    last_seen_timestamp_ns: 0i64,
811               },
812            }
813        });
814
815        handler
816            .clone()
817            .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
818                44,
819            )))
820            .await;
821        assert_data_tree!(inspector, root: {
822            test_node: contains {
823                events_count: 3u64,
824                last_seen_timestamp_ns: 42i64,
825                last_generated_timestamp_ns: 44i64,
826                consumer_controls: {
827                     events_count: 0u64,
828                     events_with_wake_lease_count: 0u64,
829                     handled_events_count: 0u64,
830                     last_generated_timestamp_ns: 0i64,
831                     last_seen_timestamp_ns: 0i64,
832                },
833                fake: {
834                     events_count: 3u64,
835                     events_with_wake_lease_count: 0u64,
836                     handled_events_count: 1u64,
837                     last_generated_timestamp_ns: 44i64,
838                     last_seen_timestamp_ns: 42i64,
839                },
840                keyboard: {
841                     events_count: 0u64,
842                     events_with_wake_lease_count: 0u64,
843                     handled_events_count: 0u64,
844                     last_generated_timestamp_ns: 0i64,
845                     last_seen_timestamp_ns: 0i64,
846                },
847                light_sensor: {
848                     events_count: 0u64,
849                     events_with_wake_lease_count: 0u64,
850                     handled_events_count: 0u64,
851                     last_generated_timestamp_ns: 0i64,
852                     last_seen_timestamp_ns: 0i64,
853                },
854                mouse: {
855                     events_count: 0u64,
856                     events_with_wake_lease_count: 0u64,
857                     handled_events_count: 0u64,
858                     last_generated_timestamp_ns: 0i64,
859                     last_seen_timestamp_ns: 0i64,
860                },
861                touch_screen: {
862                     events_count: 0u64,
863                     events_with_wake_lease_count: 0u64,
864                     handled_events_count: 0u64,
865                     last_generated_timestamp_ns: 0i64,
866                     last_seen_timestamp_ns: 0i64,
867                },
868                touchpad: {
869                    events_count: 0u64,
870                    events_with_wake_lease_count: 0u64,
871                    handled_events_count: 0u64,
872                    last_generated_timestamp_ns: 0i64,
873                    last_seen_timestamp_ns: 0i64,
874               },
875            }
876        });
877    }
878
879    #[fuchsia::test]
880    async fn verify_inspect_with_recent_events_log() {
881        let inspector = inspect::Inspector::default();
882        let root = inspector.root();
883        let test_node = root.create_child("test_node");
884        let supported_input_devices: SortedVecSet<&InputDeviceType> = SortedVecSet::from([
885            &input_device::InputDeviceType::Keyboard,
886            &input_device::InputDeviceType::ConsumerControls,
887            &input_device::InputDeviceType::LightSensor,
888            &input_device::InputDeviceType::Mouse,
889            &input_device::InputDeviceType::Touch,
890        ]);
891
892        let handler = super::InspectHandler::new_internal(
893            test_node,
894            fixed_now,
895            &supported_input_devices,
896            /* displays_recent_events = */ true,
897        );
898        assert_data_tree!(inspector, root: {
899            test_node: contains {
900                events_count: 0u64,
901                last_seen_timestamp_ns: 0i64,
902                last_generated_timestamp_ns: 0i64,
903                recent_events_log: {},
904                consumer_controls: {
905                     events_count: 0u64,
906                     events_with_wake_lease_count: 0u64,
907                     handled_events_count: 0u64,
908                     last_generated_timestamp_ns: 0i64,
909                     last_seen_timestamp_ns: 0i64,
910                },
911                fake: {
912                     events_count: 0u64,
913                     events_with_wake_lease_count: 0u64,
914                     handled_events_count: 0u64,
915                     last_generated_timestamp_ns: 0i64,
916                     last_seen_timestamp_ns: 0i64,
917                },
918                keyboard: {
919                     events_count: 0u64,
920                     events_with_wake_lease_count: 0u64,
921                     handled_events_count: 0u64,
922                     last_generated_timestamp_ns: 0i64,
923                     last_seen_timestamp_ns: 0i64,
924                },
925                light_sensor: {
926                     events_count: 0u64,
927                     events_with_wake_lease_count: 0u64,
928                     handled_events_count: 0u64,
929                     last_generated_timestamp_ns: 0i64,
930                     last_seen_timestamp_ns: 0i64,
931                },
932                mouse: {
933                     events_count: 0u64,
934                     events_with_wake_lease_count: 0u64,
935                     handled_events_count: 0u64,
936                     last_generated_timestamp_ns: 0i64,
937                     last_seen_timestamp_ns: 0i64,
938                },
939                touch_screen: {
940                     events_count: 0u64,
941                     events_with_wake_lease_count: 0u64,
942                     handled_events_count: 0u64,
943                     last_generated_timestamp_ns: 0i64,
944                     last_seen_timestamp_ns: 0i64,
945                },
946                touchpad: {
947                    events_count: 0u64,
948                    events_with_wake_lease_count: 0u64,
949                    handled_events_count: 0u64,
950                    last_generated_timestamp_ns: 0i64,
951                    last_seen_timestamp_ns: 0i64,
952               },
953           }
954        });
955
956        handler
957            .clone()
958            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(43i64)))
959            .await;
960        assert_data_tree!(inspector, root: {
961            test_node: contains {
962                events_count: 1u64,
963                last_seen_timestamp_ns: 42i64,
964                last_generated_timestamp_ns: 43i64,
965                recent_events_log: {
966                    "000_fake_event": {
967                        event_time: 43i64,
968                    },
969                },
970                consumer_controls: {
971                     events_count: 0u64,
972                     events_with_wake_lease_count: 0u64,
973                     handled_events_count: 0u64,
974                     last_generated_timestamp_ns: 0i64,
975                     last_seen_timestamp_ns: 0i64,
976                },
977                fake: {
978                     events_count: 1u64,
979                     events_with_wake_lease_count: 0u64,
980                     handled_events_count: 0u64,
981                     last_generated_timestamp_ns: 43i64,
982                     last_seen_timestamp_ns: 42i64,
983                },
984                keyboard: {
985                     events_count: 0u64,
986                     events_with_wake_lease_count: 0u64,
987                     handled_events_count: 0u64,
988                     last_generated_timestamp_ns: 0i64,
989                     last_seen_timestamp_ns: 0i64,
990                },
991                light_sensor: {
992                     events_count: 0u64,
993                     events_with_wake_lease_count: 0u64,
994                     handled_events_count: 0u64,
995                     last_generated_timestamp_ns: 0i64,
996                     last_seen_timestamp_ns: 0i64,
997                },
998                mouse: {
999                     events_count: 0u64,
1000                     events_with_wake_lease_count: 0u64,
1001                     handled_events_count: 0u64,
1002                     last_generated_timestamp_ns: 0i64,
1003                     last_seen_timestamp_ns: 0i64,
1004                },
1005                touch_screen: {
1006                     events_count: 0u64,
1007                     events_with_wake_lease_count: 0u64,
1008                     handled_events_count: 0u64,
1009                     last_generated_timestamp_ns: 0i64,
1010                     last_seen_timestamp_ns: 0i64,
1011                },
1012                touchpad: {
1013                    events_count: 0u64,
1014                    events_with_wake_lease_count: 0u64,
1015                    handled_events_count: 0u64,
1016                    last_generated_timestamp_ns: 0i64,
1017                    last_seen_timestamp_ns: 0i64,
1018               },
1019            }
1020        });
1021
1022        handler
1023            .clone()
1024            .handle_input_event(create_fake_input_event(zx::MonotonicInstant::from_nanos(44i64)))
1025            .await;
1026        assert_data_tree!(inspector, root: {
1027            test_node: contains {
1028                events_count: 2u64,
1029                last_seen_timestamp_ns: 42i64,
1030                last_generated_timestamp_ns: 44i64,
1031                recent_events_log: {
1032                    "000_fake_event": {
1033                        event_time: 43i64,
1034                    },
1035                    "001_fake_event": {
1036                        event_time: 44i64,
1037                    },
1038                },
1039                consumer_controls: {
1040                     events_count: 0u64,
1041                     events_with_wake_lease_count: 0u64,
1042                     handled_events_count: 0u64,
1043                     last_generated_timestamp_ns: 0i64,
1044                     last_seen_timestamp_ns: 0i64,
1045                },
1046                fake: {
1047                     events_count: 2u64,
1048                     events_with_wake_lease_count: 0u64,
1049                     handled_events_count: 0u64,
1050                     last_generated_timestamp_ns: 44i64,
1051                     last_seen_timestamp_ns: 42i64,
1052                },
1053                keyboard: {
1054                     events_count: 0u64,
1055                     events_with_wake_lease_count: 0u64,
1056                     handled_events_count: 0u64,
1057                     last_generated_timestamp_ns: 0i64,
1058                     last_seen_timestamp_ns: 0i64,
1059                },
1060                light_sensor: {
1061                     events_count: 0u64,
1062                     events_with_wake_lease_count: 0u64,
1063                     handled_events_count: 0u64,
1064                     last_generated_timestamp_ns: 0i64,
1065                     last_seen_timestamp_ns: 0i64,
1066                },
1067                mouse: {
1068                     events_count: 0u64,
1069                     events_with_wake_lease_count: 0u64,
1070                     handled_events_count: 0u64,
1071                     last_generated_timestamp_ns: 0i64,
1072                     last_seen_timestamp_ns: 0i64,
1073                },
1074                touch_screen: {
1075                     events_count: 0u64,
1076                     events_with_wake_lease_count: 0u64,
1077                     handled_events_count: 0u64,
1078                     last_generated_timestamp_ns: 0i64,
1079                     last_seen_timestamp_ns: 0i64,
1080                },
1081                touchpad: {
1082                    events_count: 0u64,
1083                    events_with_wake_lease_count: 0u64,
1084                    handled_events_count: 0u64,
1085                    last_generated_timestamp_ns: 0i64,
1086                    last_seen_timestamp_ns: 0i64,
1087               },
1088            }
1089        });
1090
1091        handler
1092            .clone()
1093            .handle_input_event(create_fake_handled_input_event(zx::MonotonicInstant::from_nanos(
1094                44,
1095            )))
1096            .await;
1097        assert_data_tree!(inspector, root: {
1098            test_node: contains {
1099                events_count: 3u64,
1100                last_seen_timestamp_ns: 42i64,
1101                last_generated_timestamp_ns: 44i64,
1102                recent_events_log: {
1103                    "000_fake_event": {
1104                        event_time: 43i64,
1105                    },
1106                    "001_fake_event": {
1107                        event_time: 44i64,
1108                    },
1109                    "002_fake_event": {
1110                        event_time: 44i64,
1111                    },
1112                },
1113                consumer_controls: {
1114                     events_count: 0u64,
1115                     events_with_wake_lease_count: 0u64,
1116                     handled_events_count: 0u64,
1117                     last_generated_timestamp_ns: 0i64,
1118                     last_seen_timestamp_ns: 0i64,
1119                },
1120                fake: {
1121                     events_count: 3u64,
1122                     events_with_wake_lease_count: 0u64,
1123                     handled_events_count: 1u64,
1124                     last_generated_timestamp_ns: 44i64,
1125                     last_seen_timestamp_ns: 42i64,
1126                },
1127                keyboard: {
1128                     events_count: 0u64,
1129                     events_with_wake_lease_count: 0u64,
1130                     handled_events_count: 0u64,
1131                     last_generated_timestamp_ns: 0i64,
1132                     last_seen_timestamp_ns: 0i64,
1133                },
1134                light_sensor: {
1135                     events_count: 0u64,
1136                     events_with_wake_lease_count: 0u64,
1137                     handled_events_count: 0u64,
1138                     last_generated_timestamp_ns: 0i64,
1139                     last_seen_timestamp_ns: 0i64,
1140                },
1141                mouse: {
1142                     events_count: 0u64,
1143                     events_with_wake_lease_count: 0u64,
1144                     handled_events_count: 0u64,
1145                     last_generated_timestamp_ns: 0i64,
1146                     last_seen_timestamp_ns: 0i64,
1147                },
1148                touch_screen: {
1149                     events_count: 0u64,
1150                     events_with_wake_lease_count: 0u64,
1151                     handled_events_count: 0u64,
1152                     last_generated_timestamp_ns: 0i64,
1153                     last_seen_timestamp_ns: 0i64,
1154                },
1155                touchpad: {
1156                    events_count: 0u64,
1157                    events_with_wake_lease_count: 0u64,
1158                    handled_events_count: 0u64,
1159                    last_generated_timestamp_ns: 0i64,
1160                    last_seen_timestamp_ns: 0i64,
1161               },
1162            }
1163        });
1164    }
1165
1166    #[test_case([i64::MIN]; "min value")]
1167    #[test_case([-1]; "negative value")]
1168    #[test_case([0]; "zero")]
1169    #[test_case([1]; "positive value")]
1170    #[test_case([i64::MAX]; "max value")]
1171    #[test_case([1_000_000, 10_000_000, 100_000_000, 1000_000_000]; "multiple values")]
1172    #[fuchsia::test(allow_stalls = false)]
1173    async fn updates_latency_histogram(
1174        latencies_nsec: impl IntoIterator<Item = i64> + Clone + 'static,
1175    ) {
1176        let inspector = inspect::Inspector::default();
1177        let root = inspector.root();
1178        let test_node = root.create_child("test_node");
1179
1180        let mut seen_timestamps =
1181            latencies_nsec.clone().into_iter().map(zx::MonotonicInstant::from_nanos);
1182        let now = move || {
1183            seen_timestamps.next().expect("internal error: test has more events than latencies")
1184        };
1185        let handler = super::InspectHandler::new_internal(
1186            test_node,
1187            now,
1188            &SortedVecSet::new(),
1189            /* displays_recent_events = */ false,
1190        );
1191        for _latency in latencies_nsec.clone() {
1192            handler
1193                .clone()
1194                .handle_input_event(create_fake_input_event(zx::MonotonicInstant::ZERO))
1195                .await;
1196        }
1197
1198        let mut histogram_assertion = diagnostics_assertions::HistogramAssertion::exponential(
1199            super::LATENCY_HISTOGRAM_PROPERTIES,
1200        );
1201        histogram_assertion
1202            .insert_values(latencies_nsec.into_iter().map(|nsec| nsec / 1000 / 1000));
1203        assert_data_tree!(inspector, root: {
1204            test_node: contains {
1205                pipeline_latency_ms: histogram_assertion
1206            }
1207        })
1208    }
1209}