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 dedupe_log;
13mod list;
14mod lru_cache;
15
16pub use dedupe_log::DedupeLogNode;
17pub use list::BoundedListNode;
18pub use lru_cache::LruCacheNode;
19
20pub 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
37pub struct CreateTimeResult<T> {
39 pub timestamp: zx::Instant<T>,
41 pub property: TimeProperty<T>,
43}
44
45pub trait NodeTimeExt<T: zx::Timeline> {
47 fn create_time<'a>(&self, name: impl Into<Cow<'a, str>>) -> CreateTimeResult<T>;
50
51 fn create_time_at<'a>(
53 &self,
54 name: impl Into<Cow<'a, str>>,
55 timestamp: zx::Instant<T>,
56 ) -> TimeProperty<T>;
57
58 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#[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 pub fn update(&self) {
103 self.set_at(T::get_current_time());
104 }
105
106 pub fn set_at(&self, timestamp: zx::Instant<T>) {
108 Property::set(&self.inner, timestamp.into_nanos());
109 }
110}
111
112pub type BootTimeProperty = TimeProperty<zx::BootTimeline>;
114
115pub type MonotonicTimeProperty = TimeProperty<zx::MonotonicTimeline>;
117
118impl<T: fmt::Debug + Send + Sync + 'static> InspectType for TimeProperty<T> {
119 fn into_recorded(self) -> fuchsia_inspect::RecordedInspectType {
120 fuchsia_inspect::RecordedInspectType::IntProperty(self.inner)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use diagnostics_assertions::{AnyProperty, PropertyAssertion, assert_data_tree};
128 use fuchsia_inspect::{DiagnosticsHierarchyGetter, Inspector};
129 use test_util::assert_lt;
130
131 #[fuchsia::test]
132 async fn test_time_metadata_format() {
133 let inspector = Inspector::default();
134
135 let time_property = inspector
136 .root()
137 .create_time_at("time", zx::MonotonicInstant::from_nanos(123_456_700_000));
138 let t1 = validate_inspector_get_time(&inspector, 123_456_700_000i64).await;
139
140 time_property.set_at(zx::MonotonicInstant::from_nanos(333_005_000_000));
141 let t2 = validate_inspector_get_time(&inspector, 333_005_000_000i64).await;
142
143 time_property.set_at(zx::MonotonicInstant::from_nanos(333_444_000_000));
144 let t3 = validate_inspector_get_time(&inspector, 333_444_000_000i64).await;
145
146 assert_lt!(t1, t2);
147 assert_lt!(t2, t3);
148 }
149
150 #[fuchsia::test]
151 async fn test_create_time_and_update() {
152 let inspector = Inspector::default();
153 let CreateTimeResult { timestamp: recorded_t1, property: time_property }: CreateTimeResult<
154 zx::MonotonicTimeline,
155 > = inspector.root().create_time("time");
156 let t1 = validate_inspector_get_time(&inspector, AnyProperty).await;
157 assert_eq!(recorded_t1.into_nanos(), t1);
158
159 time_property.update();
160 let t2 = validate_inspector_get_time(&inspector, AnyProperty).await;
161
162 time_property.update();
163 let t3 = validate_inspector_get_time(&inspector, AnyProperty).await;
164
165 assert_lt!(t1, t2);
166 assert_lt!(t2, t3);
167 }
168
169 #[fuchsia::test]
170 async fn test_record_time() {
171 let before_time = zx::MonotonicInstant::get().into_nanos();
172 let inspector = Inspector::default();
173 NodeTimeExt::<zx::MonotonicTimeline>::record_time(inspector.root(), "time");
174 let after_time = validate_inspector_get_time(&inspector, AnyProperty).await;
175 assert_lt!(before_time, after_time);
176 }
177
178 #[fuchsia::test]
179 fn test_create_time_no_executor() {
180 let inspector = Inspector::default();
181 let _: CreateTimeResult<zx::MonotonicTimeline> = inspector.root().create_time("time");
182 }
183
184 #[fuchsia::test]
185 fn test_record_time_no_executor() {
186 let inspector = Inspector::default();
187 NodeTimeExt::<zx::MonotonicTimeline>::record_time(inspector.root(), "time");
188 }
189
190 async fn validate_inspector_get_time<T>(inspector: &Inspector, expected: T) -> i64
191 where
192 T: PropertyAssertion<String> + 'static,
193 {
194 let hierarchy = inspector.get_diagnostics_hierarchy().await;
195 assert_data_tree!(hierarchy, root: { time: expected });
196 hierarchy.get_property("time").and_then(|t| t.int()).unwrap()
197 }
198}