fuchsia_inspect_contrib/nodes/
mod.rs

1// Copyright 2019 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
5//! Utilities and wrappers providing higher level functionality for Inspect Nodes and properties.
6
7use fuchsia_inspect::{InspectType, IntProperty, Node, Property};
8use std::borrow::Cow;
9use std::{fmt, marker};
10pub use zx::{BootTimeline, MonotonicTimeline};
11
12mod list;
13mod lru_cache;
14
15pub use list::BoundedListNode;
16pub use lru_cache::LruCacheNode;
17
18/// Implemented by timelines for which we can get the current time.
19pub trait GetCurrentTime: zx::Timeline + Sized {
20    fn get_current_time() -> zx::Instant<Self>;
21}
22
23impl GetCurrentTime for zx::MonotonicTimeline {
24    fn get_current_time() -> zx::MonotonicInstant {
25        zx::MonotonicInstant::get()
26    }
27}
28
29impl GetCurrentTime for zx::BootTimeline {
30    fn get_current_time() -> zx::BootInstant {
31        zx::BootInstant::get()
32    }
33}
34
35/// Returned by functions which take the current time and write it to a property.
36pub struct CreateTimeResult<T> {
37    /// The time written to the property.
38    pub timestamp: zx::Instant<T>,
39    /// A property to which the timestamp was written.
40    pub property: TimeProperty<T>,
41}
42
43/// Extension trait that allows to manage timestamp properties.
44pub trait NodeTimeExt<T: zx::Timeline> {
45    /// Creates a new property holding the current timestamp on the given timeline. Returns the
46    /// current timestamp that was used for the returned property too.
47    fn create_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> CreateTimeResult<T>;
48
49    /// Creates a new property holding the given timestamp.
50    fn create_time_at<'a>(
51        &self,
52        name: impl Into<Cow<'a, str>>,
53        timestamp: zx::Instant<T>,
54    ) -> TimeProperty<T>;
55
56    /// Records a new property holding the current timestamp and returns the instant that was
57    /// recorded.
58    fn record_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> zx::Instant<T>;
59}
60
61impl<T> NodeTimeExt<T> for Node
62where
63    T: zx::Timeline + GetCurrentTime,
64{
65    fn create_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> CreateTimeResult<T> {
66        let timestamp = T::get_current_time();
67        CreateTimeResult { timestamp, property: self.create_time_at(name, timestamp) }
68    }
69
70    fn create_time_at<'a>(
71        &self,
72        name: impl Into<Cow<'a, str>>,
73        timestamp: zx::Instant<T>,
74    ) -> TimeProperty<T> {
75        TimeProperty {
76            inner: self.create_int(name, timestamp.into_nanos()),
77            _phantom: marker::PhantomData,
78        }
79    }
80
81    fn record_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> zx::Instant<T> {
82        let instant = T::get_current_time();
83        self.record_int(name, instant.into_nanos());
84        instant
85    }
86}
87
88/// Wrapper around an int property that stores a monotonic timestamp.
89#[derive(Debug)]
90pub struct TimeProperty<T> {
91    pub(crate) inner: IntProperty,
92    _phantom: marker::PhantomData<T>,
93}
94
95impl<T> TimeProperty<T>
96where
97    T: zx::Timeline + GetCurrentTime,
98{
99    /// Updates the underlying property with the current monotonic timestamp.
100    pub fn update(&self) {
101        self.set_at(T::get_current_time());
102    }
103
104    /// Updates the underlying property with the given timestamp.
105    pub fn set_at(&self, timestamp: zx::Instant<T>) {
106        Property::set(&self.inner, timestamp.into_nanos());
107    }
108}
109
110/// An Inspect Time Property on the boot timeline.
111pub type BootTimeProperty = TimeProperty<zx::BootTimeline>;
112
113/// An Inspect Time Property on the monotonictimeline.
114pub type MonotonicTimeProperty = TimeProperty<zx::MonotonicTimeline>;
115
116impl<T: fmt::Debug + Send + Sync> InspectType for TimeProperty<T> {}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use diagnostics_assertions::{assert_data_tree, AnyProperty, PropertyAssertion};
122    use fuchsia_inspect::{DiagnosticsHierarchyGetter, Inspector};
123    use test_util::assert_lt;
124
125    #[fuchsia::test]
126    fn test_time_metadata_format() {
127        let inspector = Inspector::default();
128
129        let time_property = inspector
130            .root()
131            .create_time_at("time", zx::MonotonicInstant::from_nanos(123_456_700_000));
132        let t1 = validate_inspector_get_time(&inspector, 123_456_700_000i64);
133
134        time_property.set_at(zx::MonotonicInstant::from_nanos(333_005_000_000));
135        let t2 = validate_inspector_get_time(&inspector, 333_005_000_000i64);
136
137        time_property.set_at(zx::MonotonicInstant::from_nanos(333_444_000_000));
138        let t3 = validate_inspector_get_time(&inspector, 333_444_000_000i64);
139
140        assert_lt!(t1, t2);
141        assert_lt!(t2, t3);
142    }
143
144    #[fuchsia::test]
145    fn test_create_time_and_update() {
146        let inspector = Inspector::default();
147        let CreateTimeResult { timestamp: recorded_t1, property: time_property }: CreateTimeResult<
148            zx::MonotonicTimeline,
149        > = inspector.root().create_time("time");
150        let t1 = validate_inspector_get_time(&inspector, AnyProperty);
151        assert_eq!(recorded_t1.into_nanos(), t1);
152
153        time_property.update();
154        let t2 = validate_inspector_get_time(&inspector, AnyProperty);
155
156        time_property.update();
157        let t3 = validate_inspector_get_time(&inspector, AnyProperty);
158
159        assert_lt!(t1, t2);
160        assert_lt!(t2, t3);
161    }
162
163    #[fuchsia::test]
164    fn test_record_time() {
165        let before_time = zx::MonotonicInstant::get().into_nanos();
166        let inspector = Inspector::default();
167        NodeTimeExt::<zx::MonotonicTimeline>::record_time(inspector.root(), "time");
168        let after_time = validate_inspector_get_time(&inspector, AnyProperty);
169        assert_lt!(before_time, after_time);
170    }
171
172    #[fuchsia::test]
173    fn test_create_time_no_executor() {
174        let inspector = Inspector::default();
175        let _: CreateTimeResult<zx::MonotonicTimeline> = inspector.root().create_time("time");
176    }
177
178    #[fuchsia::test]
179    fn test_record_time_no_executor() {
180        let inspector = Inspector::default();
181        NodeTimeExt::<zx::MonotonicTimeline>::record_time(inspector.root(), "time");
182    }
183
184    fn validate_inspector_get_time<T>(inspector: &Inspector, expected: T) -> i64
185    where
186        T: PropertyAssertion<String> + 'static,
187    {
188        let hierarchy = inspector.get_diagnostics_hierarchy();
189        assert_data_tree!(hierarchy, root: { time: expected });
190        hierarchy.get_property("time").and_then(|t| t.int()).unwrap()
191    }
192}