fuchsia_inspect/writer/types/
inspector.rs
1use crate::writer::private::InspectTypeInternal;
6use crate::writer::state::Stats;
7use crate::writer::{Error, Heap, Node, State};
8use diagnostics_hierarchy::{DiagnosticsHierarchy, DiagnosticsHierarchyGetter};
9use inspect_format::{constants, BlockContainer, Container};
10use log::error;
11use std::borrow::Cow;
12use std::cmp::max;
13use std::fmt;
14use std::sync::Arc;
15
16#[cfg(target_os = "fuchsia")]
17use zx::{self as zx, AsHandleRef, HandleBased};
18
19#[derive(Clone)]
22pub struct Inspector {
23 root_node: Arc<Node>,
25
26 #[allow(dead_code)] storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
29}
30
31impl fmt::Debug for Inspector {
32 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
33 let tree = self.get_diagnostics_hierarchy();
34 if fmt.alternate() {
35 write!(fmt, "{:#?}", tree)
36 } else {
37 write!(fmt, "{:?}", tree)
38 }
39 }
40}
41
42impl DiagnosticsHierarchyGetter<String> for Inspector {
43 fn get_diagnostics_hierarchy(&self) -> Cow<'_, DiagnosticsHierarchy> {
44 let hierarchy = futures::executor::block_on(async move { crate::reader::read(self).await })
45 .expect("failed to get hierarchy");
46 Cow::Owned(hierarchy)
47 }
48}
49
50pub trait InspectorIntrospectionExt {
51 fn stats(&self) -> Option<Stats>;
52}
53
54impl InspectorIntrospectionExt for Inspector {
55 fn stats(&self) -> Option<Stats> {
56 self.state().and_then(|outer| outer.try_lock().ok().map(|state| state.stats()))
57 }
58}
59
60#[cfg(target_os = "fuchsia")]
61impl Inspector {
62 pub fn duplicate_vmo(&self) -> Option<zx::Vmo> {
66 self.storage.as_ref().and_then(|vmo| {
67 vmo.duplicate_handle(
68 zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP | zx::Rights::GET_PROPERTY,
69 )
70 .ok()
71 })
72 }
73
74 pub fn duplicate_vmo_with_rights(&self, rights: zx::Rights) -> Option<zx::Vmo> {
78 self.storage.as_ref().and_then(|vmo| vmo.duplicate_handle(rights).ok())
79 }
80
81 pub fn frozen_vmo_copy(&self) -> Option<zx::Vmo> {
96 self.state()?.try_lock().ok().and_then(|mut state| state.frozen_vmo_copy().ok()).flatten()
97 }
98
99 pub fn copy_vmo(&self) -> Option<zx::Vmo> {
103 self.copy_vmo_data().and_then(|data| {
104 if let Ok(vmo) = zx::Vmo::create(data.len() as u64) {
105 vmo.write(&data, 0).ok().map(|_| vmo)
106 } else {
107 None
108 }
109 })
110 }
111
112 pub(crate) fn get_storage_handle(&self) -> Option<Arc<zx::Vmo>> {
113 self.storage.clone()
115 }
116
117 #[cfg(test)]
120 pub fn is_frozen(&self) -> Result<(), u64> {
121 use inspect_format::{BlockAccessorExt, Header};
122 let vmo = self.storage.as_ref().unwrap();
123 let mut buffer: [u8; 16] = [0; 16];
124 vmo.read(&mut buffer, 0).unwrap();
125 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
126 if block.generation_count() == constants::VMO_FROZEN {
127 Ok(())
128 } else {
129 Err(block.generation_count())
130 }
131 }
132}
133
134#[cfg(not(target_os = "fuchsia"))]
135impl Inspector {
136 pub(crate) fn duplicate_vmo(&self) -> Option<<Container as BlockContainer>::Data> {
137 self.copy_vmo_data()
140 }
141
142 pub(crate) fn get_storage_handle(&self) -> Option<Vec<u8>> {
143 self.copy_vmo_data()
145 }
146}
147
148impl Default for Inspector {
149 fn default() -> Self {
150 Inspector::new(InspectorConfig::default())
151 }
152}
153
154impl Inspector {
155 pub fn new(conf: InspectorConfig) -> Self {
158 conf.build()
159 }
160
161 pub fn copy_vmo_data(&self) -> Option<Vec<u8>> {
166 self.root_node.inner.inner_ref().and_then(|inner_ref| inner_ref.state.copy_vmo_bytes())
167 }
168
169 pub fn max_size(&self) -> Option<usize> {
170 self.state()?.try_lock().ok().map(|state| state.stats().maximum_size)
171 }
172
173 pub fn is_valid(&self) -> bool {
175 self.root_node.is_valid()
180 }
181
182 pub fn root(&self) -> &Node {
184 &self.root_node
185 }
186
187 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
190 where
191 F: FnOnce(&Node) -> R,
192 {
193 self.root().atomic_update(update_fn)
194 }
195
196 pub(crate) fn state(&self) -> Option<State> {
197 self.root().inner.inner_ref().map(|inner_ref| inner_ref.state.clone())
198 }
199}
200
201pub struct InspectorConfig {
203 is_no_op: bool,
204 size: usize,
205 storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
206}
207
208impl Default for InspectorConfig {
209 fn default() -> Self {
216 Self { is_no_op: false, size: constants::DEFAULT_VMO_SIZE_BYTES, storage: None }
217 }
218}
219
220impl InspectorConfig {
221 pub fn no_op(mut self) -> Self {
223 self.is_no_op = true;
224 self
225 }
226
227 pub fn size(mut self, max_size: usize) -> Self {
229 self.size = max_size;
230 self
231 }
232
233 fn create_no_op(self) -> Inspector {
234 Inspector { storage: self.storage, root_node: Arc::new(Node::new_no_op()) }
235 }
236
237 fn adjusted_buffer_size(max_size: usize) -> usize {
238 let mut size = max(constants::MINIMUM_VMO_SIZE_BYTES, max_size);
239 if size % constants::MINIMUM_VMO_SIZE_BYTES != 0 {
241 size =
242 (1 + size / constants::MINIMUM_VMO_SIZE_BYTES) * constants::MINIMUM_VMO_SIZE_BYTES;
243 }
244
245 size
246 }
247}
248
249#[cfg(target_os = "fuchsia")]
250impl InspectorConfig {
251 pub fn vmo(mut self, vmo: zx::Vmo) -> Self {
254 self.storage = Some(Arc::new(vmo));
255 self.no_op()
256 }
257
258 fn build(self) -> Inspector {
259 if self.is_no_op {
260 return self.create_no_op();
261 }
262
263 match Self::new_root(self.size) {
264 Ok((storage, root_node)) => {
265 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
266 }
267 Err(e) => {
268 error!("Failed to create root node. Error: {:?}", e);
269 self.create_no_op()
270 }
271 }
272 }
273
274 fn new_root(
276 max_size: usize,
277 ) -> Result<(Arc<<Container as BlockContainer>::ShareableData>, Node), Error> {
278 let size = Self::adjusted_buffer_size(max_size);
279 let (container, vmo) = Container::read_and_write(size).map_err(Error::AllocateVmo)?;
280 let name = zx::Name::new("InspectHeap").unwrap();
281 vmo.set_name(&name).map_err(Error::AllocateVmo)?;
282 let vmo = Arc::new(vmo);
283 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
284 let state =
285 State::create(heap, vmo.clone()).map_err(|e| Error::CreateState(Box::new(e)))?;
286 Ok((vmo, Node::new_root(state)))
287 }
288}
289
290#[cfg(not(target_os = "fuchsia"))]
291impl InspectorConfig {
292 fn build(self) -> Inspector {
293 if self.is_no_op {
294 return self.create_no_op();
295 }
296
297 match Self::new_root(self.size) {
298 Ok((root_node, storage)) => {
299 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
300 }
301 Err(e) => {
302 error!("Failed to create root node. Error: {:?}", e);
303 self.create_no_op()
304 }
305 }
306 }
307
308 fn new_root(
309 max_size: usize,
310 ) -> Result<(Node, Arc<<Container as BlockContainer>::ShareableData>), Error> {
311 let size = Self::adjusted_buffer_size(max_size);
312 let (container, storage) = Container::read_and_write(size).unwrap();
313 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
314 let state =
315 State::create(heap, Arc::new(storage)).map_err(|e| Error::CreateState(Box::new(e)))?;
316 Ok((Node::new_root(state), Arc::new(storage)))
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323 use crate::assert_update_is_atomic;
324 use futures::FutureExt;
325
326 #[fuchsia::test]
327 fn debug_impl() {
328 let inspector = Inspector::default();
329 inspector.root().record_int("name", 5);
330
331 assert_eq!(
332 format!("{:?}", &inspector),
333 "DiagnosticsHierarchy { name: \
334 \"root\", properties: [Int(\"name\", 5)], children: [], missing: [] }"
335 );
336
337 let pretty = r#"DiagnosticsHierarchy {
338 name: "root",
339 properties: [
340 Int(
341 "name",
342 5,
343 ),
344 ],
345 children: [],
346 missing: [],
347}"#;
348 assert_eq!(format!("{:#?}", &inspector), pretty);
349
350 let two = inspector.root().create_child("two");
351 two.record_lazy_child("two_child", || {
352 let insp = Inspector::default();
353 insp.root().record_double("double", 1.0);
354
355 async move { Ok(insp) }.boxed()
356 });
357
358 let pretty = r#"DiagnosticsHierarchy {
359 name: "root",
360 properties: [
361 Int(
362 "name",
363 5,
364 ),
365 ],
366 children: [
367 DiagnosticsHierarchy {
368 name: "two",
369 properties: [],
370 children: [
371 DiagnosticsHierarchy {
372 name: "two_child",
373 properties: [
374 Double(
375 "double",
376 1.0,
377 ),
378 ],
379 children: [],
380 missing: [],
381 },
382 ],
383 missing: [],
384 },
385 ],
386 missing: [],
387}"#;
388 assert_eq!(format!("{:#?}", &inspector), pretty);
389 }
390
391 #[fuchsia::test]
392 fn inspector_new() {
393 let test_object = Inspector::default();
394 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
395 }
396
397 #[fuchsia::test]
398 fn inspector_copy_data() {
399 let test_object = Inspector::default();
400
401 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
402
403 assert_eq!(test_object.copy_vmo_data().unwrap().len(), 4096);
405 }
406
407 #[fuchsia::test]
408 fn no_op() {
409 let inspector = Inspector::new(InspectorConfig::default().size(4096));
410 let nodes = (0..84)
412 .map(|i| inspector.root().create_child(format!("test-{}", i)))
413 .collect::<Vec<Node>>();
414
415 assert!(nodes.iter().all(|node| node.is_valid()));
416 let no_op_node = inspector.root().create_child("no-op-child");
417 assert!(!no_op_node.is_valid());
418 }
419
420 #[fuchsia::test]
421 fn inspector_new_with_size() {
422 let test_object = Inspector::new(InspectorConfig::default().size(8192));
423 assert_eq!(test_object.max_size().unwrap(), 8192);
424
425 let test_object = Inspector::new(InspectorConfig::default().size(10000));
427 assert_eq!(test_object.max_size().unwrap(), 12288);
428
429 let test_object = Inspector::new(InspectorConfig::default().size(2000));
431 assert_eq!(test_object.max_size().unwrap(), 4096);
432 }
433
434 #[fuchsia::test]
435 async fn atomic_update() {
436 let insp = Inspector::default();
437 assert_update_is_atomic!(insp, |n| {
438 n.record_int("", 1);
439 n.record_int("", 2);
440 n.record_uint("", 3);
441 n.record_string("", "abcd");
442 });
443 }
444}
445
446#[cfg(all(test, target_os = "fuchsia"))]
448mod fuchsia_tests {
449 use super::*;
450
451 #[fuchsia::test]
452 fn inspector_duplicate_vmo() {
453 let test_object = Inspector::default();
454 assert_eq!(
455 test_object.storage.as_ref().unwrap().get_size().unwrap(),
456 constants::DEFAULT_VMO_SIZE_BYTES as u64
457 );
458 assert_eq!(
459 test_object.duplicate_vmo().unwrap().get_size().unwrap(),
460 constants::DEFAULT_VMO_SIZE_BYTES as u64
461 );
462 }
463
464 #[fuchsia::test]
465 fn inspector_new_root() {
466 let (vmo, root_node) = InspectorConfig::new_root(100).unwrap();
468 assert_eq!(vmo.get_size().unwrap(), 4096);
469 let inner = root_node.inner.inner_ref().unwrap();
470 assert_eq!(*inner.block_index, 0);
471 assert_eq!("InspectHeap", vmo.get_name().expect("Has name"));
472 }
473
474 #[fuchsia::test]
475 fn freeze_vmo_works() {
476 let inspector = Inspector::default();
477 let initial =
478 inspector.state().unwrap().with_current_header(|header| header.generation_count());
479 let vmo = inspector.frozen_vmo_copy();
480
481 let is_frozen_result = inspector.is_frozen();
482 assert!(is_frozen_result.is_err());
483
484 assert_eq!(initial + 2, is_frozen_result.err().unwrap());
485 assert!(is_frozen_result.err().unwrap() % 2 == 0);
486
487 let frozen_insp = Inspector::new(InspectorConfig::default().no_op().vmo(vmo.unwrap()));
488 assert!(frozen_insp.is_frozen().is_ok());
489 }
490
491 #[fuchsia::test]
492 fn transactions_block_freezing() {
493 let inspector = Inspector::default();
494 inspector.atomic_update(|_| assert!(inspector.frozen_vmo_copy().is_none()));
495 }
496
497 #[fuchsia::test]
498 fn transactions_block_copying() {
499 let inspector = Inspector::default();
500 inspector.atomic_update(|_| assert!(inspector.copy_vmo().is_none()));
501 inspector.atomic_update(|_| assert!(inspector.copy_vmo_data().is_none()));
502 }
503
504 #[fuchsia::test]
505 fn inspector_new_with_size() {
506 let test_object = Inspector::new(InspectorConfig::default().size(8192));
507 assert_eq!(test_object.max_size().unwrap(), 8192);
508
509 assert_eq!(
510 "InspectHeap",
511 test_object.storage.as_ref().unwrap().get_name().expect("Has name")
512 );
513
514 let test_object = Inspector::new(InspectorConfig::default().size(10000));
516 assert_eq!(test_object.max_size().unwrap(), 12288);
517
518 let test_object = Inspector::new(InspectorConfig::default().size(2000));
520 assert_eq!(test_object.max_size().unwrap(), 4096);
521 }
522}