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 inspect_format::{BlockAccessorExt, Header};
17#[cfg(target_os = "fuchsia")]
18use zx::HandleBased;
19
20#[derive(Clone)]
23pub struct Inspector {
24 root_node: Arc<Node>,
26
27 #[allow(dead_code)] storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
30}
31
32impl DiagnosticsHierarchyGetter<String> for Inspector {
33 async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy>
34 where
35 String: 'a,
36 {
37 let hierarchy = crate::reader::read(self).await.expect("failed to get hierarchy");
38 Cow::Owned(hierarchy)
39 }
40}
41
42pub trait InspectorIntrospectionExt {
43 fn stats(&self) -> Option<Stats>;
44}
45
46impl InspectorIntrospectionExt for Inspector {
47 fn stats(&self) -> Option<Stats> {
48 self.state().and_then(|outer| outer.try_lock().ok().map(|state| state.stats()))
49 }
50}
51
52#[cfg(target_os = "fuchsia")]
53impl Inspector {
54 pub fn duplicate_vmo(&self) -> Option<zx::Vmo> {
58 self.storage.as_ref().and_then(|vmo| {
59 vmo.duplicate_handle(
60 zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP | zx::Rights::GET_PROPERTY,
61 )
62 .ok()
63 })
64 }
65
66 pub fn duplicate_vmo_with_rights(&self, rights: zx::Rights) -> Option<zx::Vmo> {
70 self.storage.as_ref().and_then(|vmo| vmo.duplicate_handle(rights).ok())
71 }
72
73 pub fn frozen_vmo_copy(&self) -> Result<zx::Vmo, Error> {
88 if let Some(state) = self.state() {
89 let mut guard = state.try_lock()?;
90 guard.frozen_vmo_copy()
91 } else if let Some(vmo) = self.storage.as_ref() {
92 if is_vmo_frozen(vmo) {
93 vmo.duplicate_handle(
95 zx::Rights::BASIC
98 | zx::Rights::READ
99 | zx::Rights::MAP
100 | zx::Rights::GET_PROPERTY,
101 )
102 .map_err(Error::DuplicateVmoFailed)
103 } else {
104 Err(Error::VmoNotFrozen)
107 }
108 } else {
109 Err(Error::VmoMissing)
110 }
111 }
112
113 pub fn copy_vmo(&self) -> Option<zx::Vmo> {
117 self.copy_vmo_data().and_then(|data| {
118 if let Ok(vmo) = zx::Vmo::create(data.len() as u64) {
119 vmo.write(&data, 0).ok().map(|_| vmo)
120 } else {
121 None
122 }
123 })
124 }
125
126 pub(crate) fn get_storage_handle(&self) -> Option<Arc<zx::Vmo>> {
127 self.storage.clone()
129 }
130
131 #[cfg(test)]
132 pub(crate) fn is_frozen(&self) -> bool {
133 if let Some(vmo) = self.storage.as_ref() {
134 return is_vmo_frozen(vmo);
135 }
136 false
137 }
138}
139
140#[cfg(target_os = "fuchsia")]
141fn is_vmo_frozen(vmo: impl AsRef<zx::Vmo>) -> bool {
142 let mut buffer: [u8; 32] = [0; 32];
143 if vmo.as_ref().read(&mut buffer, 0).is_ok() {
144 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
145 return block.generation_count() == inspect_format::constants::VMO_FROZEN;
146 }
147 false
148}
149
150#[cfg(not(target_os = "fuchsia"))]
151impl Inspector {
152 pub(crate) fn duplicate_vmo(&self) -> Option<<Container as BlockContainer>::Data> {
153 self.copy_vmo_data()
156 }
157
158 pub(crate) fn get_storage_handle(&self) -> Option<Vec<u8>> {
159 self.copy_vmo_data()
161 }
162}
163
164impl Default for Inspector {
165 fn default() -> Self {
166 Inspector::new(InspectorConfig::default())
167 }
168}
169
170impl Inspector {
171 pub fn new(conf: InspectorConfig) -> Self {
174 conf.build()
175 }
176
177 pub fn copy_vmo_data(&self) -> Option<Vec<u8>> {
182 self.root_node.inner.inner_ref().and_then(|inner_ref| inner_ref.state.copy_vmo_bytes())
183 }
184
185 pub fn max_size(&self) -> Option<usize> {
186 self.state()?.try_lock().ok().map(|state| state.stats().maximum_size)
187 }
188
189 pub fn is_valid(&self) -> bool {
191 self.root_node.is_valid()
196 }
197
198 pub fn root(&self) -> &Node {
200 &self.root_node
201 }
202
203 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
206 where
207 F: FnOnce(&Node) -> R,
208 {
209 self.root().atomic_update(update_fn)
210 }
211
212 pub(crate) fn state(&self) -> Option<State> {
213 self.root().inner.inner_ref().map(|inner_ref| inner_ref.state.clone())
214 }
215}
216
217pub struct InspectorConfig {
219 is_no_op: bool,
220 size: usize,
221 storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
222}
223
224impl Default for InspectorConfig {
225 fn default() -> Self {
232 Self { is_no_op: false, size: constants::DEFAULT_VMO_SIZE_BYTES, storage: None }
233 }
234}
235
236impl InspectorConfig {
237 pub fn no_op(mut self) -> Self {
239 self.is_no_op = true;
240 self
241 }
242
243 pub fn size(mut self, max_size: usize) -> Self {
245 self.size = max_size;
246 self
247 }
248
249 fn create_no_op(self) -> Inspector {
250 Inspector { storage: self.storage, root_node: Arc::new(Node::new_no_op()) }
251 }
252
253 fn adjusted_buffer_size(max_size: usize) -> usize {
254 let mut size = max(constants::MINIMUM_VMO_SIZE_BYTES, max_size);
255 if !size.is_multiple_of(constants::MINIMUM_VMO_SIZE_BYTES) {
257 size =
258 (1 + size / constants::MINIMUM_VMO_SIZE_BYTES) * constants::MINIMUM_VMO_SIZE_BYTES;
259 }
260
261 size
262 }
263}
264
265#[cfg(target_os = "fuchsia")]
266impl InspectorConfig {
267 pub fn vmo(mut self, vmo: zx::Vmo) -> Self {
270 self.storage = Some(Arc::new(vmo));
271 self.no_op()
272 }
273
274 fn build(self) -> Inspector {
275 if self.is_no_op {
276 return self.create_no_op();
277 }
278
279 match Self::new_root(self.size) {
280 Ok((storage, root_node)) => {
281 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
282 }
283 Err(e) => {
284 error!("Failed to create root node. Error: {:?}", e);
285 self.create_no_op()
286 }
287 }
288 }
289
290 fn new_root(
292 max_size: usize,
293 ) -> Result<(Arc<<Container as BlockContainer>::ShareableData>, Node), Error> {
294 let size = Self::adjusted_buffer_size(max_size);
295 let (container, vmo) = Container::read_and_write(size).map_err(Error::AllocateVmo)?;
296 let name = zx::Name::new("InspectHeap").unwrap();
297 vmo.set_name(&name).map_err(Error::AllocateVmo)?;
298 let vmo = Arc::new(vmo);
299 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
300 let state =
301 State::create(heap, vmo.clone()).map_err(|e| Error::CreateState(Box::new(e)))?;
302 Ok((vmo, Node::new_root(state)))
303 }
304}
305
306#[cfg(not(target_os = "fuchsia"))]
307impl InspectorConfig {
308 fn build(self) -> Inspector {
309 if self.is_no_op {
310 return self.create_no_op();
311 }
312
313 match Self::new_root(self.size) {
314 Ok((root_node, storage)) => {
315 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
316 }
317 Err(e) => {
318 error!("Failed to create root node. Error: {:?}", e);
319 self.create_no_op()
320 }
321 }
322 }
323
324 fn new_root(
325 max_size: usize,
326 ) -> Result<(Node, Arc<<Container as BlockContainer>::ShareableData>), Error> {
327 let size = Self::adjusted_buffer_size(max_size);
328 let (container, storage) = Container::read_and_write(size).unwrap();
329 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
330 let state =
331 State::create(heap, Arc::new(storage)).map_err(|e| Error::CreateState(Box::new(e)))?;
332 Ok((Node::new_root(state), Arc::new(storage)))
333 }
334}
335
336#[cfg(test)]
337mod tests {
338 use super::*;
339 use crate::assert_update_is_atomic;
340
341 #[fuchsia::test]
342 fn inspector_new() {
343 let test_object = Inspector::default();
344 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
345 }
346
347 #[fuchsia::test]
348 fn inspector_copy_data() {
349 let test_object = Inspector::default();
350
351 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
352
353 assert_eq!(test_object.copy_vmo_data().unwrap().len(), 4096);
355 }
356
357 #[fuchsia::test]
358 fn no_op() {
359 let inspector = Inspector::new(InspectorConfig::default().size(4096));
360 let nodes = (0..84)
362 .map(|i| inspector.root().create_child(format!("test-{i}")))
363 .collect::<Vec<Node>>();
364
365 assert!(nodes.iter().all(|node| node.is_valid()));
366 let no_op_node = inspector.root().create_child("no-op-child");
367 assert!(!no_op_node.is_valid());
368 }
369
370 #[fuchsia::test]
371 fn inspector_new_with_size() {
372 let test_object = Inspector::new(InspectorConfig::default().size(8192));
373 assert_eq!(test_object.max_size().unwrap(), 8192);
374
375 let test_object = Inspector::new(InspectorConfig::default().size(10000));
377 assert_eq!(test_object.max_size().unwrap(), 12288);
378
379 let test_object = Inspector::new(InspectorConfig::default().size(2000));
381 assert_eq!(test_object.max_size().unwrap(), 4096);
382 }
383
384 #[fuchsia::test]
385 async fn atomic_update() {
386 let insp = Inspector::default();
387 assert_update_is_atomic!(insp, |n| {
388 n.record_int("", 1);
389 n.record_int("", 2);
390 n.record_uint("", 3);
391 n.record_string("", "abcd");
392 });
393 }
394}
395
396#[cfg(all(test, target_os = "fuchsia"))]
398mod fuchsia_tests {
399 use super::*;
400 use assert_matches::assert_matches;
401
402 #[fuchsia::test]
403 fn inspector_duplicate_vmo() {
404 let test_object = Inspector::default();
405 assert_eq!(
406 test_object.storage.as_ref().unwrap().get_size().unwrap(),
407 constants::DEFAULT_VMO_SIZE_BYTES as u64
408 );
409 assert_eq!(
410 test_object.duplicate_vmo().unwrap().get_size().unwrap(),
411 constants::DEFAULT_VMO_SIZE_BYTES as u64
412 );
413 }
414
415 #[fuchsia::test]
416 fn inspector_new_root() {
417 let (vmo, root_node) = InspectorConfig::new_root(100).unwrap();
419 assert_eq!(vmo.get_size().unwrap(), 4096);
420 let inner = root_node.inner.inner_ref().unwrap();
421 assert_eq!(*inner.block_index, 0);
422 assert_eq!("InspectHeap", vmo.get_name().expect("Has name"));
423 }
424
425 #[fuchsia::test]
426 fn freeze_vmo_works() {
427 let inspector = Inspector::default();
428 let initial =
429 inspector.state().unwrap().with_current_header(|header| header.generation_count());
430 let vmo = inspector.frozen_vmo_copy();
431
432 assert!(!inspector.is_frozen());
433
434 let mut buffer: [u8; 32] = [0; 32];
435 inspector.storage.as_ref().unwrap().read(&mut buffer, 0).unwrap();
436 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
437 let gen_count = block.generation_count();
438
439 assert_eq!(initial + 2, gen_count);
440 assert!(gen_count.is_multiple_of(2));
441
442 let frozen_insp = Inspector::new(InspectorConfig::default().no_op().vmo(vmo.unwrap()));
443 assert!(frozen_insp.is_frozen());
444 }
445
446 #[fuchsia::test]
447 fn no_op_inspector_with_frozen_vmo_can_be_frozen() {
448 const VMO_SIZE: u64 = 4096;
449
450 let original_inspector = Inspector::new(InspectorConfig::default().size(VMO_SIZE as usize));
451 let frozen_vmo = original_inspector.frozen_vmo_copy().unwrap();
452 let inspector = Inspector::new(InspectorConfig::default().no_op().vmo(frozen_vmo));
453
454 assert_matches!(inspector.state(), None);
455 assert_matches!(inspector.frozen_vmo_copy(), Ok(copy) => {
456 assert_eq!(copy.get_size().unwrap(), VMO_SIZE);
457 });
458 }
459
460 #[fuchsia::test]
461 fn no_op_inspector_with_non_frozen_vmo_cannot_be_frozen() {
462 let inspect_vmo = zx::Vmo::create(4096).unwrap();
463 let inspector = Inspector::new(InspectorConfig::default().no_op().vmo(inspect_vmo));
464
465 assert_matches!(inspector.state(), None);
466 assert_matches!(inspector.frozen_vmo_copy(), Err(Error::VmoNotFrozen));
467 }
468
469 #[fuchsia::test]
470 fn transactions_block_freezing() {
471 let inspector = Inspector::default();
472 inspector.atomic_update(|_| assert!(inspector.frozen_vmo_copy().is_err()));
473 }
474
475 #[fuchsia::test]
476 fn transactions_block_copying() {
477 let inspector = Inspector::default();
478 inspector.atomic_update(|_| assert!(inspector.copy_vmo().is_none()));
479 inspector.atomic_update(|_| assert!(inspector.copy_vmo_data().is_none()));
480 }
481
482 #[fuchsia::test]
483 fn inspector_new_with_size() {
484 let test_object = Inspector::new(InspectorConfig::default().size(8192));
485 assert_eq!(test_object.max_size().unwrap(), 8192);
486
487 assert_eq!(
488 "InspectHeap",
489 test_object.storage.as_ref().unwrap().get_name().expect("Has name")
490 );
491
492 let test_object = Inspector::new(InspectorConfig::default().size(10000));
494 assert_eq!(test_object.max_size().unwrap(), 12288);
495
496 let test_object = Inspector::new(InspectorConfig::default().size(2000));
498 assert_eq!(test_object.max_size().unwrap(), 4096);
499 }
500}