fuchsia_inspect/writer/types/
base.rs1use crate::writer::{Error, Node, State};
6use derivative::Derivative;
7use inspect_format::BlockIndex;
8use private::InspectTypeInternal;
9use std::borrow::Cow;
10use std::fmt::Debug;
11use std::sync::{Arc, Weak};
12
13pub trait InspectType: Send + Sync + Debug {
15 fn into_recorded(self) -> crate::writer::types::RecordedInspectType
16 where
17 Self: Sized + 'static;
18}
19
20pub(crate) mod private {
21 use crate::writer::State;
22 use inspect_format::BlockIndex;
23
24 pub trait InspectTypeInternal {
28 fn is_valid(&self) -> bool;
29 fn block_index(&self) -> Option<BlockIndex>;
30 fn state(&self) -> Option<State>;
31 fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, accessor: F) -> R;
32 }
33}
34
35pub trait InspectTypeReparentable: private::InspectTypeInternal {
40 #[doc(hidden)]
41 fn reparent(&self, new_parent: &Node) -> Result<(), Error> {
45 if let (
46 Some(child_state),
47 Some(child_index),
48 Some(new_parent_state),
49 Some(new_parent_index),
50 ) = (self.state(), self.block_index(), new_parent.state(), new_parent.block_index())
51 {
52 if new_parent_state != child_state {
53 return Err(Error::AdoptionIntoWrongVmo);
54 }
55
56 new_parent_state
57 .try_lock()
58 .and_then(|mut state| state.reparent(child_index, new_parent_index))?;
59 }
60
61 Ok(())
62 }
63}
64
65impl<T: private::InspectTypeInternal> InspectTypeReparentable for T {}
66
67pub trait InspectTypeRenameable: private::InspectTypeInternal {
71 fn rename<'a>(&self, name: impl Into<Cow<'a, str>>) -> Result<(), Error>;
73}
74
75macro_rules! impl_inspect_type_internal {
77 ($type_name:ident) => {
78 impl $type_name {
79 pub(crate) fn new(
80 state: $crate::writer::State,
81 block_index: inspect_format::BlockIndex,
82 ) -> $type_name {
83 $type_name { inner: $crate::writer::types::base::Inner::new(state, block_index) }
84 }
85
86 pub(crate) fn new_no_op() -> $type_name {
87 $type_name { inner: $crate::writer::types::base::Inner::None }
88 }
89
90 pub fn rename<'a>(
92 &self,
93 name: impl Into<std::borrow::Cow<'a, str>>,
94 ) -> Result<(), $crate::writer::Error> {
95 <Self as $crate::writer::InspectTypeRenameable>::rename(self, name)
96 }
97 }
98
99 impl $crate::writer::InspectTypeRenameable for $type_name {
100 fn rename<'a>(
101 &self,
102 name: impl Into<std::borrow::Cow<'a, str>>,
103 ) -> Result<(), $crate::writer::Error> {
104 self.inner.rename(name)
105 }
106 }
107
108 impl $crate::private::InspectTypeInternal for $type_name {
109 fn is_valid(&self) -> bool {
110 self.inner.is_valid()
111 }
112
113 fn state(&self) -> Option<$crate::writer::State> {
114 Some(self.inner.inner_ref()?.state.clone())
115 }
116
117 fn block_index(&self) -> Option<inspect_format::BlockIndex> {
118 if let Some(ref inner_ref) = self.inner.inner_ref() {
119 Some(inner_ref.block_index)
120 } else {
121 None
122 }
123 }
124
125 fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, accessor: F) -> R {
126 match self.inner.inner_ref() {
127 None => {
128 accessor(&self)
131 }
132 Some(inner_ref) => {
133 inner_ref.state.begin_transaction();
137 let result = accessor(&self);
138 inner_ref.state.end_transaction();
139 result
140 }
141 }
142 }
143 }
144 };
145}
146
147pub(crate) use impl_inspect_type_internal;
148
149macro_rules! impl_inspect_type_internal_histogram {
150 ($type_name:ident) => {
151 impl $type_name {
152 pub fn rename<'a>(
154 &self,
155 name: impl Into<std::borrow::Cow<'a, str>>,
156 ) -> Result<(), $crate::writer::Error> {
157 <Self as $crate::writer::InspectTypeRenameable>::rename(self, name)
158 }
159 }
160
161 impl $crate::writer::InspectTypeRenameable for $type_name {
162 fn rename<'a>(
163 &self,
164 name: impl Into<std::borrow::Cow<'a, str>>,
165 ) -> Result<(), $crate::writer::Error> {
166 self.array.rename(name)
167 }
168 }
169
170 impl $crate::private::InspectTypeInternal for $type_name {
171 fn is_valid(&self) -> bool {
172 self.array.is_valid()
173 }
174
175 fn state(&self) -> Option<$crate::writer::State> {
176 self.array.state()
177 }
178
179 fn block_index(&self) -> Option<inspect_format::BlockIndex> {
180 self.array.block_index()
181 }
182
183 fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, accessor: F) -> R {
184 self.array.atomic_access(|_| accessor(self))
185 }
186 }
187 };
188}
189
190pub(crate) use impl_inspect_type_internal_histogram;
191
192#[derive(Debug, Derivative)]
195#[derivative(Default)]
196pub(crate) enum Inner<T: InnerType> {
197 #[derivative(Default)]
199 None,
200
201 Weak(Weak<InnerRef<T>>),
204
205 Strong(Arc<InnerRef<T>>),
207}
208
209impl<T: InnerType> Inner<T> {
210 pub(crate) fn new(state: State, block_index: BlockIndex) -> Self {
212 Self::Strong(Arc::new(InnerRef { state, block_index, data: T::Data::default() }))
213 }
214
215 pub(crate) fn rename<'a>(&self, name: impl Into<Cow<'a, str>>) -> Result<(), Error> {
216 if let Some(inner_ref) = self.inner_ref() {
217 let mut state = inner_ref.state.try_lock()?;
218 if inner_ref.data.is_valid() {
219 state.set_name(inner_ref.block_index, name)?;
220 }
221 }
222 Ok(())
223 }
224
225 pub(crate) fn is_valid(&self) -> bool {
228 match self {
229 Self::None => false,
230 Self::Weak(weak_ref) => match weak_ref.upgrade() {
231 None => false,
232 Some(inner_ref) => inner_ref.data.is_valid(),
233 },
234 Self::Strong(inner_ref) => inner_ref.data.is_valid(),
235 }
236 }
237
238 pub(crate) fn inner_ref(&self) -> Option<Arc<InnerRef<T>>> {
243 match self {
244 Self::None => None,
245 Self::Weak(weak_ref) => {
246 if let Some(inner_ref) = weak_ref.upgrade()
247 && inner_ref.data.is_valid()
248 {
249 return Some(inner_ref);
250 }
251 None
252 }
253 Self::Strong(inner_ref) => {
254 if inner_ref.data.is_valid() {
255 Some(Arc::clone(inner_ref))
256 } else {
257 None
258 }
259 }
260 }
261 }
262
263 pub(crate) fn clone_weak(&self) -> Self {
265 match self {
266 Self::None => Self::None,
267 Self::Weak(weak_ref) => Self::Weak(weak_ref.clone()),
268 Self::Strong(inner_ref) => {
269 if inner_ref.data.is_valid() {
270 Self::Weak(Arc::downgrade(inner_ref))
271 } else {
272 Self::None
273 }
274 }
275 }
276 }
277}
278
279impl<T: InnerType> PartialEq for Inner<T> {
284 fn eq(&self, _other: &Self) -> bool {
285 true
286 }
287}
288
289impl<T: InnerType> Eq for Inner<T> {}
290
291#[derive(Debug)]
295pub(crate) struct InnerRef<T: InnerType> {
296 pub(crate) block_index: BlockIndex,
298
299 pub(crate) state: State,
301
302 pub(crate) data: T::Data,
304}
305
306impl<T: InnerType> Drop for InnerRef<T> {
307 fn drop(&mut self) {
310 if let Err(e) = T::free(&self.state, &self.data, self.block_index) {
311 log::error!("Failed to free InnerRef: {:?}", e);
312 }
313 }
314}
315
316pub(crate) trait InnerType {
318 type Data: Default + Debug + InnerData;
320
321 fn free(state: &State, data: &Self::Data, block_index: BlockIndex) -> Result<(), Error>;
323}
324
325pub(crate) trait InnerData {
326 fn is_valid(&self) -> bool;
327}
328
329impl InnerData for () {
330 fn is_valid(&self) -> bool {
331 true
332 }
333}
334
335#[derive(Default, Debug)]
336pub(crate) struct InnerValueType;
337
338impl InnerType for InnerValueType {
339 type Data = ();
340 fn free(state: &State, _: &Self::Data, block_index: BlockIndex) -> Result<(), Error> {
341 let mut state_lock = state.try_lock()?;
342 state_lock.free_value(block_index).map_err(|err| Error::free("value", block_index, err))
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349 use crate::Inspector;
350 use diagnostics_assertions::assert_data_tree;
351
352 #[fuchsia::test]
353 async fn test_reparent_from_state() {
354 let insp = Inspector::default();
355 let root = insp.root();
356 let a = root.create_child("a");
357 let b = a.create_child("b");
358
359 assert_data_tree!(insp, root: {
360 a: {
361 b: {},
362 },
363 });
364
365 b.reparent(root).unwrap();
366
367 assert_data_tree!(insp, root: {
368 b: {},
369 a: {},
370 });
371 }
372
373 #[fuchsia::test]
374 fn reparent_from_wrong_state() {
375 let insp1 = Inspector::default();
376 let insp2 = Inspector::default();
377
378 assert!(insp1.root().reparent(insp2.root()).is_err());
379
380 let a = insp1.root().create_child("a");
381 let b = insp2.root().create_child("b");
382
383 assert!(a.reparent(&b).is_err());
384 assert!(b.reparent(&a).is_err());
385 }
386
387 #[fuchsia::test]
388 async fn test_rename_nodes_and_properties() {
389 use crate::writer::{ArrayProperty, HistogramProperty};
390 use diagnostics_hierarchy::{LinearHistogram, LinearHistogramParams};
391 use futures::FutureExt;
392
393 let insp = Inspector::default();
394 let root = insp.root();
395 let node = root.create_child("node");
396 let int_prop = node.create_int("int_prop", 42);
397 let str_prop = node.create_string("str_prop", "hello");
398 let array_prop = node.create_int_array("array_prop", 2);
399 array_prop.set(0, 1);
400 array_prop.set(1, 2);
401 let hist_prop = node.create_int_linear_histogram(
402 "hist_prop",
403 LinearHistogramParams { floor: 0, step_size: 10, buckets: 2 },
404 );
405 hist_prop.insert(5);
406 let lazy_node = node.create_lazy_child("lazy_node", || {
407 async move {
408 let lazy_insp = Inspector::default();
409 lazy_insp.root().record_int("val", 99);
410 Ok(lazy_insp)
411 }
412 .boxed()
413 });
414
415 assert_data_tree!(insp, root: {
416 node: {
417 int_prop: 42i64,
418 str_prop: "hello",
419 array_prop: vec![1i64, 2i64],
420 hist_prop: LinearHistogram {
421 floor: 0i64,
422 step: 10,
423 counts: vec![1],
424 indexes: Some(vec![1]),
425 size: 4,
426 },
427 lazy_node: {
428 val: 99i64,
429 },
430 },
431 });
432
433 node.rename("node_renamed").unwrap();
434 int_prop.rename("int_renamed").unwrap();
435 str_prop.rename("str_renamed").unwrap();
436 array_prop.rename("array_renamed").unwrap();
437 hist_prop.rename("hist_renamed").unwrap();
438 lazy_node.rename("lazy_renamed").unwrap();
439
440 assert_data_tree!(insp, root: {
441 node_renamed: {
442 int_renamed: 42i64,
443 str_renamed: "hello",
444 array_renamed: vec![1i64, 2i64],
445 hist_renamed: LinearHistogram {
446 floor: 0i64,
447 step: 10,
448 counts: vec![1],
449 indexes: Some(vec![1]),
450 size: 4,
451 },
452 lazy_renamed: {
453 val: 99i64,
454 },
455 },
456 });
457 }
458
459 #[fuchsia::test]
460 async fn test_rename_root_and_noop_and_weak() {
461 let insp = Inspector::default();
462 assert_eq!(insp.root().rename("new_root"), Err(Error::RenameRoot));
463
464 let noop_node = Node::default();
465 assert_eq!(noop_node.rename("ignored"), Ok(()));
466
467 let node = insp.root().create_child("node");
468 let _child = node.create_child("child");
469 let weak = node.clone_weak();
470 weak.rename("weak_renamed").unwrap();
471 assert_data_tree!(insp, root: {
472 weak_renamed: {
473 child: {},
474 },
475 });
476
477 node.forget();
478 assert_eq!(node.rename("after_forget"), Ok(()));
479 assert_eq!(weak.rename("after_forget"), Ok(()));
480 }
481
482 #[fuchsia::test]
483 fn test_rename_string_reference_lifecycle() {
484 let insp = Inspector::default();
485 let state = insp.state().unwrap();
486
487 let node = insp.root().create_child("unique_old_name");
488 let stats_before = state.try_lock().unwrap().stats();
489
490 node.rename("unique_old_name").unwrap();
492 let stats_same = state.try_lock().unwrap().stats();
493 assert_eq!(stats_before.allocated_blocks, stats_same.allocated_blocks);
494 assert_eq!(stats_before.deallocated_blocks, stats_same.deallocated_blocks);
495
496 node.rename("unique_new_name").unwrap();
499 let stats_after = state.try_lock().unwrap().stats();
500 assert_eq!(stats_after.allocated_blocks, stats_before.allocated_blocks + 1);
501 assert_eq!(stats_after.deallocated_blocks, stats_before.deallocated_blocks + 1);
502 }
503}