1// Copyright 2020 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.
45use fuchsia_inspect::{
6 BoolProperty, BytesProperty, DoubleProperty, IntProperty, Node, Property, StringProperty,
7 UintProperty,
8};
9use futures::lock;
10use std::{cell, sync};
11use thiserror::Error;
12use tracing_mutex::lockapi::TracingWrapper;
1314/// AttachError denotes a broken data acquisition invariant, such as when a
15/// mutex is held during attachment.
16#[derive(Error, Debug)]
17#[error("could not attach to inspect: {:?}", .msg)]
18pub struct AttachError {
19 msg: String,
20}
2122impl From<&str> for AttachError {
23fn from(msg: &str) -> Self {
24Self { msg: msg.to_string() }
25 }
26}
2728/// A trait for types that can be inspected using fuchsia_inspect and that
29/// maintain their inspect state during their lifetime. Notably, this trait is
30/// implemented for most fuchsia_inspect properties and the `IOwned` smart
31/// pointers. Generic implementations are also provided for interior mutability
32/// wrappers whose inner type also implement `Inspect`, such as RefCell and
33/// various Mutex types. This method should generally be implemented for a
34/// mutable reference (or a regular reference if the type has interior
35/// mutability).
36// TODO(https://fxbug.dev/42127490): Add a derive-macro to auto generate this trait.
37pub trait Inspect {
38/// Attaches `self` to the inspect tree, under `parent[name]`. Note that:
39 ///
40 /// - If this method is called on a type with interior mutability, it
41 /// will attempt to get exclusive access to the inner data, but will
42 /// not wait for a mutex to unlock. If it fails, an `AttachError` is
43 /// returned.
44 /// - If this method is called on a fuchsia_inspect Property, it is
45 /// reinitialized to its default value.
46 ///
47 /// Therefore it is recommended to attach to inspect immediately after
48 /// initialization, although not within constructors. Whether or not
49 /// to use inspect should usually be a choice of the caller.
50 ///
51 /// NOTE: Implementors should avoid returning AttachError whenever
52 /// possible. It is reserved for irrecoverable invariant violations
53 /// (see above). Invalid data structure invariants are not attachment
54 /// errors and should instead be ignored and optionally logged.
55fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError>;
56}
5758/// Implement `Inspect` for a fuchsia_inspect property.
59macro_rules! impl_inspect_property {
60 ($prop_name:ident, $prop_name_cap:ident) => {
61paste::paste! {
62impl Inspect for &mut [<$prop_name_cap Property>] {
63fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
64let default = <[<$prop_name_cap Property>] as Property<'_>>::Type::default();
65*self = parent.[<create_ $prop_name>](name.as_ref(), default);
66Ok(())
67 }
68 }
69 }
70 };
71}
7273impl_inspect_property!(uint, Uint);
74impl_inspect_property!(int, Int);
75impl_inspect_property!(double, Double);
76impl_inspect_property!(bool, Bool);
77impl_inspect_property!(string, String);
78impl_inspect_property!(bytes, Bytes);
7980// Implement `Inspect` for interior mutability wrapper types.
8182impl<T> Inspect for &cell::RefCell<T>
83where
84 for<'a> &'a mut T: Inspect,
85{
86fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
87match self.try_borrow_mut() {
88Ok(mut inner) => inner.iattach(parent, name),
89Err(_) => Err("could not get exclusive access to cell::RefCell".into()),
90 }
91 }
92}
9394impl<T> Inspect for &sync::Mutex<T>
95where
96 for<'a> &'a mut T: Inspect,
97{
98fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
99match self.try_lock() {
100Ok(mut inner) => inner.iattach(parent, name),
101Err(_) => Err("could not get exclusive access to std::sync::Mutex".into()),
102 }
103 }
104}
105106impl<T> Inspect for &sync::RwLock<T>
107where
108 for<'a> &'a mut T: Inspect,
109{
110fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
111match self.try_write() {
112Ok(mut inner) => inner.iattach(parent, name),
113Err(_) => Err("could not get exclusive access to std::sync::RwLock".into()),
114 }
115 }
116}
117118impl<T> Inspect for &fuchsia_sync::Mutex<T>
119where
120 for<'a> &'a mut T: Inspect,
121{
122fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
123match self.try_lock() {
124Some(mut inner) => inner.iattach(parent, name),
125None => Err("could not get exclusive access to fuchsia_sync::Mutex".into()),
126 }
127 }
128}
129130impl<T> Inspect for &fuchsia_sync::RwLock<T>
131where
132 for<'a> &'a mut T: Inspect,
133{
134fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
135match self.try_write() {
136Some(mut inner) => inner.iattach(parent, name),
137None => Err("could not get exclusive access to fuchsia_sync::RwLock".into()),
138 }
139 }
140}
141142impl<T> Inspect for &lock::Mutex<T>
143where
144 for<'a> &'a mut T: Inspect,
145{
146fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
147match self.try_lock() {
148Some(mut inner) => inner.iattach(parent, name),
149None => Err("could not get exclusive access to futures::lock::Mutex".into()),
150 }
151 }
152}
153154// Underlying type for `lock_api::Mutex`.
155impl<T> Inspect for &lock_api::Mutex<TracingWrapper<fuchsia_sync::RawSyncMutex>, T>
156where
157 for<'a> &'a mut T: Inspect,
158{
159fn iattach(self, parent: &Node, name: impl AsRef<str>) -> Result<(), AttachError> {
160match self.try_lock() {
161Some(mut inner) => inner.iattach(parent, name),
162None => Err("could not get exclusive access to lock_api::Mutex".into()),
163 }
164 }
165}
166167/// Extension trait for types that #[derive(Inspect)] (or implements
168/// `Inspect for &mut T` some other way), providing a convenient way of
169/// attaching to inspect during construction. See the `Inspect` trait for
170/// more details.
171pub trait WithInspect
172where
173Self: Sized,
174{
175/// Attaches self to the inspect tree. It is recommended to invoke this as
176 /// part of construction. For example:
177 ///
178 /// `let yak = Yak::new().with_inspect(inspector.root(), "my_yak")?;`
179fn with_inspect(self, parent: &Node, name: impl AsRef<str>) -> Result<Self, AttachError>;
180}
181182impl<T> WithInspect for T
183where
184 for<'a> &'a mut T: Inspect,
185{
186fn with_inspect(mut self, parent: &Node, name: impl AsRef<str>) -> Result<Self, AttachError> {
187self.iattach(parent, name).map(|()| self)
188 }
189}