fuchsia_inspect/writer/types/
inspector.rs1use crate::writer::private::InspectTypeInternal;
6use crate::writer::state::Stats;
7use crate::writer::{Error, Heap, Node, State};
8use diagnostics_hierarchy::{DiagnosticsHierarchy, DiagnosticsHierarchyGetter};
9use inspect_format::{BlockContainer, Container, constants};
10use log::error;
11use std::borrow::Cow;
12use std::cmp::max;
13use std::sync::Arc;
14
15#[cfg(target_os = "fuchsia")]
16use zx::HandleBased;
17
18#[derive(Clone)]
21pub struct Inspector {
22 root_node: Arc<Node>,
24
25 #[allow(dead_code)] storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
28}
29
30impl DiagnosticsHierarchyGetter<String> for Inspector {
31 async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy>
32 where
33 String: 'a,
34 {
35 let hierarchy = crate::reader::read(self).await.expect("failed to get hierarchy");
36 Cow::Owned(hierarchy)
37 }
38}
39
40pub trait InspectorIntrospectionExt {
41 fn stats(&self) -> Option<Stats>;
42}
43
44impl InspectorIntrospectionExt for Inspector {
45 fn stats(&self) -> Option<Stats> {
46 self.state().and_then(|outer| outer.try_lock().ok().map(|state| state.stats()))
47 }
48}
49
50#[cfg(target_os = "fuchsia")]
51impl Inspector {
52 pub fn duplicate_vmo(&self) -> Option<zx::Vmo> {
56 self.storage.as_ref().and_then(|vmo| {
57 vmo.duplicate_handle(
58 zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP | zx::Rights::GET_PROPERTY,
59 )
60 .ok()
61 })
62 }
63
64 pub fn duplicate_vmo_with_rights(&self, rights: zx::Rights) -> Option<zx::Vmo> {
68 self.storage.as_ref().and_then(|vmo| vmo.duplicate_handle(rights).ok())
69 }
70
71 pub fn frozen_vmo_copy(&self) -> Result<zx::Vmo, Error> {
86 if let Some(state) = self.state() {
87 let mut guard = state.try_lock()?;
88 guard.frozen_vmo_copy()
89 } else {
90 Err(Error::MissingState)
91 }
92 }
93
94 pub fn copy_vmo(&self) -> Option<zx::Vmo> {
98 self.copy_vmo_data().and_then(|data| {
99 if let Ok(vmo) = zx::Vmo::create(data.len() as u64) {
100 vmo.write(&data, 0).ok().map(|_| vmo)
101 } else {
102 None
103 }
104 })
105 }
106
107 pub(crate) fn get_storage_handle(&self) -> Option<Arc<zx::Vmo>> {
108 self.storage.clone()
110 }
111
112 #[cfg(test)]
115 pub fn is_frozen(&self) -> Result<(), u64> {
116 use inspect_format::{BlockAccessorExt, Header};
117 let vmo = self.storage.as_ref().unwrap();
118 let mut buffer: [u8; 16] = [0; 16];
119 vmo.read(&mut buffer, 0).unwrap();
120 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
121 if block.generation_count() == constants::VMO_FROZEN {
122 Ok(())
123 } else {
124 Err(block.generation_count())
125 }
126 }
127}
128
129#[cfg(not(target_os = "fuchsia"))]
130impl Inspector {
131 pub(crate) fn duplicate_vmo(&self) -> Option<<Container as BlockContainer>::Data> {
132 self.copy_vmo_data()
135 }
136
137 pub(crate) fn get_storage_handle(&self) -> Option<Vec<u8>> {
138 self.copy_vmo_data()
140 }
141}
142
143impl Default for Inspector {
144 fn default() -> Self {
145 Inspector::new(InspectorConfig::default())
146 }
147}
148
149impl Inspector {
150 pub fn new(conf: InspectorConfig) -> Self {
153 conf.build()
154 }
155
156 pub fn copy_vmo_data(&self) -> Option<Vec<u8>> {
161 self.root_node.inner.inner_ref().and_then(|inner_ref| inner_ref.state.copy_vmo_bytes())
162 }
163
164 pub fn max_size(&self) -> Option<usize> {
165 self.state()?.try_lock().ok().map(|state| state.stats().maximum_size)
166 }
167
168 pub fn is_valid(&self) -> bool {
170 self.root_node.is_valid()
175 }
176
177 pub fn root(&self) -> &Node {
179 &self.root_node
180 }
181
182 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
185 where
186 F: FnOnce(&Node) -> R,
187 {
188 self.root().atomic_update(update_fn)
189 }
190
191 pub(crate) fn state(&self) -> Option<State> {
192 self.root().inner.inner_ref().map(|inner_ref| inner_ref.state.clone())
193 }
194}
195
196pub struct InspectorConfig {
198 is_no_op: bool,
199 size: usize,
200 storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
201}
202
203impl Default for InspectorConfig {
204 fn default() -> Self {
211 Self { is_no_op: false, size: constants::DEFAULT_VMO_SIZE_BYTES, storage: None }
212 }
213}
214
215impl InspectorConfig {
216 pub fn no_op(mut self) -> Self {
218 self.is_no_op = true;
219 self
220 }
221
222 pub fn size(mut self, max_size: usize) -> Self {
224 self.size = max_size;
225 self
226 }
227
228 fn create_no_op(self) -> Inspector {
229 Inspector { storage: self.storage, root_node: Arc::new(Node::new_no_op()) }
230 }
231
232 fn adjusted_buffer_size(max_size: usize) -> usize {
233 let mut size = max(constants::MINIMUM_VMO_SIZE_BYTES, max_size);
234 if !size.is_multiple_of(constants::MINIMUM_VMO_SIZE_BYTES) {
236 size =
237 (1 + size / constants::MINIMUM_VMO_SIZE_BYTES) * constants::MINIMUM_VMO_SIZE_BYTES;
238 }
239
240 size
241 }
242}
243
244#[cfg(target_os = "fuchsia")]
245impl InspectorConfig {
246 pub fn vmo(mut self, vmo: zx::Vmo) -> Self {
249 self.storage = Some(Arc::new(vmo));
250 self.no_op()
251 }
252
253 fn build(self) -> Inspector {
254 if self.is_no_op {
255 return self.create_no_op();
256 }
257
258 match Self::new_root(self.size) {
259 Ok((storage, root_node)) => {
260 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
261 }
262 Err(e) => {
263 error!("Failed to create root node. Error: {:?}", e);
264 self.create_no_op()
265 }
266 }
267 }
268
269 fn new_root(
271 max_size: usize,
272 ) -> Result<(Arc<<Container as BlockContainer>::ShareableData>, Node), Error> {
273 let size = Self::adjusted_buffer_size(max_size);
274 let (container, vmo) = Container::read_and_write(size).map_err(Error::AllocateVmo)?;
275 let name = zx::Name::new("InspectHeap").unwrap();
276 vmo.set_name(&name).map_err(Error::AllocateVmo)?;
277 let vmo = Arc::new(vmo);
278 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
279 let state =
280 State::create(heap, vmo.clone()).map_err(|e| Error::CreateState(Box::new(e)))?;
281 Ok((vmo, Node::new_root(state)))
282 }
283}
284
285#[cfg(not(target_os = "fuchsia"))]
286impl InspectorConfig {
287 fn build(self) -> Inspector {
288 if self.is_no_op {
289 return self.create_no_op();
290 }
291
292 match Self::new_root(self.size) {
293 Ok((root_node, storage)) => {
294 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
295 }
296 Err(e) => {
297 error!("Failed to create root node. Error: {:?}", e);
298 self.create_no_op()
299 }
300 }
301 }
302
303 fn new_root(
304 max_size: usize,
305 ) -> Result<(Node, Arc<<Container as BlockContainer>::ShareableData>), Error> {
306 let size = Self::adjusted_buffer_size(max_size);
307 let (container, storage) = Container::read_and_write(size).unwrap();
308 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
309 let state =
310 State::create(heap, Arc::new(storage)).map_err(|e| Error::CreateState(Box::new(e)))?;
311 Ok((Node::new_root(state), Arc::new(storage)))
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318 use crate::assert_update_is_atomic;
319
320 #[fuchsia::test]
321 fn inspector_new() {
322 let test_object = Inspector::default();
323 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
324 }
325
326 #[fuchsia::test]
327 fn inspector_copy_data() {
328 let test_object = Inspector::default();
329
330 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
331
332 assert_eq!(test_object.copy_vmo_data().unwrap().len(), 4096);
334 }
335
336 #[fuchsia::test]
337 fn no_op() {
338 let inspector = Inspector::new(InspectorConfig::default().size(4096));
339 let nodes = (0..84)
341 .map(|i| inspector.root().create_child(format!("test-{i}")))
342 .collect::<Vec<Node>>();
343
344 assert!(nodes.iter().all(|node| node.is_valid()));
345 let no_op_node = inspector.root().create_child("no-op-child");
346 assert!(!no_op_node.is_valid());
347 }
348
349 #[fuchsia::test]
350 fn inspector_new_with_size() {
351 let test_object = Inspector::new(InspectorConfig::default().size(8192));
352 assert_eq!(test_object.max_size().unwrap(), 8192);
353
354 let test_object = Inspector::new(InspectorConfig::default().size(10000));
356 assert_eq!(test_object.max_size().unwrap(), 12288);
357
358 let test_object = Inspector::new(InspectorConfig::default().size(2000));
360 assert_eq!(test_object.max_size().unwrap(), 4096);
361 }
362
363 #[fuchsia::test]
364 async fn atomic_update() {
365 let insp = Inspector::default();
366 assert_update_is_atomic!(insp, |n| {
367 n.record_int("", 1);
368 n.record_int("", 2);
369 n.record_uint("", 3);
370 n.record_string("", "abcd");
371 });
372 }
373}
374
375#[cfg(all(test, target_os = "fuchsia"))]
377mod fuchsia_tests {
378 use super::*;
379 use assert_matches::assert_matches;
380
381 #[fuchsia::test]
382 fn inspector_duplicate_vmo() {
383 let test_object = Inspector::default();
384 assert_eq!(
385 test_object.storage.as_ref().unwrap().get_size().unwrap(),
386 constants::DEFAULT_VMO_SIZE_BYTES as u64
387 );
388 assert_eq!(
389 test_object.duplicate_vmo().unwrap().get_size().unwrap(),
390 constants::DEFAULT_VMO_SIZE_BYTES as u64
391 );
392 }
393
394 #[fuchsia::test]
395 fn inspector_new_root() {
396 let (vmo, root_node) = InspectorConfig::new_root(100).unwrap();
398 assert_eq!(vmo.get_size().unwrap(), 4096);
399 let inner = root_node.inner.inner_ref().unwrap();
400 assert_eq!(*inner.block_index, 0);
401 assert_eq!("InspectHeap", vmo.get_name().expect("Has name"));
402 }
403
404 #[fuchsia::test]
405 fn freeze_vmo_works() {
406 let inspector = Inspector::default();
407 let initial =
408 inspector.state().unwrap().with_current_header(|header| header.generation_count());
409 let vmo = inspector.frozen_vmo_copy();
410
411 let is_frozen_result = inspector.is_frozen();
412 assert!(is_frozen_result.is_err());
413
414 assert_eq!(initial + 2, is_frozen_result.err().unwrap());
415 assert!(is_frozen_result.err().unwrap().is_multiple_of(2));
416
417 let frozen_insp = Inspector::new(InspectorConfig::default().no_op().vmo(vmo.unwrap()));
418 assert!(frozen_insp.is_frozen().is_ok());
419 }
420
421 #[fuchsia::test]
422 fn no_op_vmo_fails_to_freeze() {
423 let inspect_vmo = zx::Vmo::create(4096).unwrap();
424 let inspector = Inspector::new(InspectorConfig::default().no_op().vmo(inspect_vmo));
425
426 assert_matches!(inspector.state(), None);
427 assert_matches!(inspector.frozen_vmo_copy(), Err(Error::MissingState));
428 }
429
430 #[fuchsia::test]
431 fn transactions_block_freezing() {
432 let inspector = Inspector::default();
433 inspector.atomic_update(|_| assert!(inspector.frozen_vmo_copy().is_err()));
434 }
435
436 #[fuchsia::test]
437 fn transactions_block_copying() {
438 let inspector = Inspector::default();
439 inspector.atomic_update(|_| assert!(inspector.copy_vmo().is_none()));
440 inspector.atomic_update(|_| assert!(inspector.copy_vmo_data().is_none()));
441 }
442
443 #[fuchsia::test]
444 fn inspector_new_with_size() {
445 let test_object = Inspector::new(InspectorConfig::default().size(8192));
446 assert_eq!(test_object.max_size().unwrap(), 8192);
447
448 assert_eq!(
449 "InspectHeap",
450 test_object.storage.as_ref().unwrap().get_name().expect("Has name")
451 );
452
453 let test_object = Inspector::new(InspectorConfig::default().size(10000));
455 assert_eq!(test_object.max_size().unwrap(), 12288);
456
457 let test_object = Inspector::new(InspectorConfig::default().size(2000));
459 assert_eq!(test_object.max_size().unwrap(), 4096);
460 }
461}