Skip to main content

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