fuchsia_inspect_contrib/nodes/
mod.rs1use 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
18pub 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
35pub struct CreateTimeResult<T> {
37 pub timestamp: zx::Instant<T>,
39 pub property: TimeProperty<T>,
41}
42
43pub trait NodeTimeExt<T: zx::Timeline> {
45 fn create_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> CreateTimeResult<T>;
48
49 fn create_time_at<'a>(
51 &self,
52 name: impl Into<Cow<'a, str>>,
53 timestamp: zx::Instant<T>,
54 ) -> TimeProperty<T>;
55
56 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#[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 pub fn update(&self) {
101 self.set_at(T::get_current_time());
102 }
103
104 pub fn set_at(&self, timestamp: zx::Instant<T>) {
106 Property::set(&self.inner, timestamp.into_nanos());
107 }
108}
109
110pub type BootTimeProperty = TimeProperty<zx::BootTimeline>;
112
113pub 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}