1use fuchsia_inspect::Property;
6use fuchsia_sync::Mutex;
7use std::borrow::Cow;
8use std::collections::HashMap;
9use std::collections::hash_map::Entry;
10use std::fmt::{Debug, Error, Formatter};
11use std::ops::Deref;
12use std::sync::Arc;
13
14pub struct RootDiagnosticNode {
16 inspect: Arc<fuchsia_inspect::Node>,
17 count: std::sync::atomic::AtomicU64,
18}
19
20impl RootDiagnosticNode {
21 pub fn new(inspect: fuchsia_inspect::Node) -> Self {
23 Self { inspect: Arc::new(inspect), count: std::sync::atomic::AtomicU64::new(0) }
24 }
25
26 #[allow(dead_code)]
28 pub(crate) fn persistent_child(&self) -> DiagnosticNode {
29 let name =
30 format!("id-{:?}", self.count.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
31 DiagnosticNode::new_set_persistence(name, self.inspect.clone(), true)
32 }
33
34 pub(crate) fn child(&self) -> DiagnosticNode {
36 let name =
37 format!("id-{:?}", self.count.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
38 DiagnosticNode::new(name, self.inspect.clone())
39 }
40}
41
42pub(crate) struct DiagnosticNode {
49 inner: Arc<DiagnosticNodeInner>,
50}
51
52#[derive(Clone)]
53enum Parent {
54 Root(Arc<fuchsia_inspect::Node>),
55 Node(Arc<DiagnosticNodeInner>),
56}
57
58struct DiagnosticNodeInner {
59 name: Cow<'static, str>,
60 properties: Mutex<HashMap<&'static str, (Cow<'static, str>, fuchsia_inspect::StringProperty)>>,
61 parent: Parent,
62 inspect: fuchsia_inspect::Node,
63 persistent: bool,
64}
65
66impl DiagnosticNode {
67 pub(crate) fn new(
69 name: impl Into<Cow<'static, str>>,
70 inspect_parent: Arc<fuchsia_inspect::Node>,
71 ) -> Self {
72 Self::new_set_persistence(name, inspect_parent, false)
73 }
74
75 fn new_set_persistence(
76 name: impl Into<Cow<'static, str>>,
77 inspect_parent: Arc<fuchsia_inspect::Node>,
78 persistent: bool,
79 ) -> Self {
80 let cow_name = name.into();
81 Self {
82 inner: Arc::new(DiagnosticNodeInner {
83 inspect: inspect_parent.create_child(cow_name.deref()),
84 parent: Parent::Root(inspect_parent),
85 name: cow_name,
86 properties: Mutex::new(HashMap::new()),
87 persistent,
88 }),
89 }
90 }
91
92 pub(crate) fn child(&self, name: impl Into<Cow<'static, str>>) -> Self {
94 let cow_name = name.into();
95 Self {
96 inner: Arc::new(DiagnosticNodeInner {
97 inspect: self.inner.inspect.create_child(cow_name.deref()),
98 name: cow_name,
99 properties: Mutex::new(HashMap::new()),
100 parent: Parent::Node(self.inner.clone()),
101 persistent: self.inner.persistent,
102 }),
103 }
104 }
105
106 pub(crate) fn set_property(&self, key: &'static str, value: impl Into<Cow<'static, str>>) {
108 let mut prop_lock = self.inner.properties.lock();
109 let cow_val = value.into();
110 match prop_lock.entry(key) {
111 Entry::Occupied(mut entry) => {
112 let val = entry.get_mut();
113 val.1.set(cow_val.deref());
114 val.0 = cow_val;
115 }
116 Entry::Vacant(entry) => {
117 let prop = self.inner.inspect.create_string(key, cow_val.deref());
118 entry.insert((cow_val, prop));
119 }
120 }
121 }
122
123 #[allow(dead_code)]
125 pub(crate) fn set_flag(&self, key: &'static str) {
126 self.set_property(key, "true")
127 }
128
129 fn ancestors_and_self(&self) -> Vec<Arc<DiagnosticNodeInner>> {
130 let mut ancestors = vec![self.inner.clone()];
131 let mut next_parent = self.inner.parent.clone();
132 while let Parent::Node(parent) = next_parent.clone() {
133 next_parent = parent.parent.clone();
134 ancestors.push(parent);
135 }
136 ancestors.reverse();
137 ancestors
138 }
139}
140
141impl std::ops::Drop for DiagnosticNodeInner {
142 fn drop(&mut self) {
143 if self.persistent {
144 let parent_inspect = match &self.parent {
145 Parent::Node(inner) => &inner.inspect,
146 Parent::Root(inspect) => &*inspect,
147 };
148 let inspect = std::mem::take(&mut self.inspect);
149 for (_, val) in self.properties.lock().drain() {
150 inspect.record(val.1);
151 }
152 inspect.record_bool("_dropped", true);
153 parent_inspect.record(inspect);
154 }
155 }
156}
157
158impl Debug for DiagnosticNode {
159 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
160 let ancestors = self.ancestors_and_self();
161 f.debug_list().entries(ancestors).finish()
162 }
163}
164
165impl Debug for DiagnosticNodeInner {
166 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
167 let mut debug_struct = f.debug_struct(self.name.deref());
168 for (key, value) in self.properties.lock().iter() {
169 debug_struct.field(key, &value.0.deref());
170 }
171 debug_struct.finish()
172 }
173}
174
175#[cfg(test)]
176mod test {
177 use super::*;
178 use diagnostics_assertions::assert_data_tree;
179 use fuchsia_inspect::Inspector;
180
181 #[fuchsia::test]
182 async fn inspect_lifetimes() {
183 let inspector = Inspector::default();
184 let root_node = DiagnosticNode::new("root", Arc::new(inspector.root().clone_weak()));
185
186 assert_data_tree!(
187 inspector,
188 root: {
189 root: {}
190 }
191 );
192
193 root_node.set_property("property", "value");
194
195 assert_data_tree!(
196 inspector,
197 root: {
198 root: {
199 property: "value",
200 }
201 }
202 );
203
204 let child_node = root_node.child("child");
205 assert_data_tree!(
206 inspector,
207 root: {
208 root: {
209 property: "value",
210 child: {}
211 }
212 }
213 );
214
215 drop(child_node);
216 assert_data_tree!(
217 inspector,
218 root: {
219 root: {
220 property: "value",
221 }
222 }
223 );
224
225 drop(root_node);
226 assert_data_tree!(
227 inspector,
228 root: {}
229 );
230 }
231
232 #[fuchsia::test]
233 fn debug_fmt_hierarchy() {
234 let inspector = Inspector::default();
235 let root_node = DiagnosticNode::new("root", Arc::new(inspector.root().clone_weak()));
236
237 assert!(format!("{:?}", root_node).contains("root"));
238
239 let child_node = root_node.child("child");
240
241 assert!(format!("{:?}", child_node).contains("child"));
243 assert!(format!("{:?}", child_node).contains("root"));
244
245 let grandchild = child_node.child("grand");
247 assert!(format!("{:?}", grandchild).contains("grand"));
248 assert!(format!("{:?}", grandchild).contains("child"));
249 assert!(format!("{:?}", grandchild).contains("root"));
250
251 drop(root_node);
253 drop(child_node);
254 assert!(format!("{:?}", grandchild).contains("grand"));
255 assert!(format!("{:?}", grandchild).contains("child"));
256 assert!(format!("{:?}", grandchild).contains("root"));
257 }
258
259 #[fuchsia::test]
260 fn debug_fmt_properties() {
261 let inspector = Inspector::default();
262 let root_node = DiagnosticNode::new("root", Arc::new(inspector.root().clone_weak()));
263
264 assert!(format!("{:?}", root_node).contains("root"));
265
266 root_node.set_property("property", "value");
267 assert!(format!("{:?}", root_node).contains("root"));
268 assert!(format!("{:?}", root_node).contains("property"));
269 assert!(format!("{:?}", root_node).contains("value"));
270 }
271}