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
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 if let Some(vmo) = self.storage.as_ref() {
90 if is_vmo_frozen(vmo) {
91 vmo.duplicate_handle(
93 zx::Rights::BASIC
96 | zx::Rights::READ
97 | zx::Rights::MAP
98 | zx::Rights::GET_PROPERTY,
99 )
100 .map_err(Error::DuplicateVmoFailed)
101 } else {
102 Err(Error::VmoNotFrozen)
105 }
106 } else {
107 Err(Error::VmoMissing)
108 }
109 }
110
111 pub fn copy_vmo(&self) -> Option<zx::Vmo> {
115 self.copy_vmo_data().and_then(|data| {
116 if let Ok(vmo) = zx::Vmo::create(data.len() as u64) {
117 vmo.write(&data, 0).ok().map(|_| vmo)
118 } else {
119 None
120 }
121 })
122 }
123
124 pub(crate) fn get_storage_handle(&self) -> Option<Arc<zx::Vmo>> {
125 self.storage.clone()
127 }
128
129 #[cfg(test)]
130 pub(crate) fn is_frozen(&self) -> bool {
131 if let Some(vmo) = self.storage.as_ref() {
132 return is_vmo_frozen(vmo);
133 }
134 false
135 }
136}
137
138#[cfg(target_os = "fuchsia")]
139fn is_vmo_frozen(vmo: impl AsRef<zx::Vmo>) -> bool {
140 let mut buffer: [u8; 32] = [0; 32];
141 if vmo.as_ref().read(&mut buffer, 0).is_ok() {
142 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
143 return block.generation_count() == inspect_format::constants::VMO_FROZEN;
144 }
145 false
146}
147
148#[cfg(not(target_os = "fuchsia"))]
149impl Inspector {
150 pub(crate) fn duplicate_vmo(&self) -> Option<<Container as BlockContainer>::Data> {
151 self.copy_vmo_data()
154 }
155
156 pub(crate) fn get_storage_handle(&self) -> Option<Vec<u8>> {
157 self.copy_vmo_data()
159 }
160}
161
162impl Default for Inspector {
163 fn default() -> Self {
164 Inspector::new(InspectorConfig::default())
165 }
166}
167
168impl Inspector {
169 pub fn new(conf: InspectorConfig) -> Self {
172 conf.build()
173 }
174
175 pub fn copy_vmo_data(&self) -> Option<Vec<u8>> {
180 self.root_node.inner.inner_ref().and_then(|inner_ref| inner_ref.state.copy_vmo_bytes())
181 }
182
183 pub fn max_size(&self) -> Option<usize> {
184 self.state()?.try_lock().ok().map(|state| state.stats().maximum_size)
185 }
186
187 pub fn is_valid(&self) -> bool {
189 self.root_node.is_valid()
194 }
195
196 pub fn root(&self) -> &Node {
198 &self.root_node
199 }
200
201 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
204 where
205 F: FnOnce(&Node) -> R,
206 {
207 self.root().atomic_update(update_fn)
208 }
209
210 pub(crate) fn state(&self) -> Option<State> {
211 self.root().inner.inner_ref().map(|inner_ref| inner_ref.state.clone())
212 }
213}
214
215pub struct InspectorConfig {
217 is_no_op: bool,
218 size: usize,
219 storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
220}
221
222impl Default for InspectorConfig {
223 fn default() -> Self {
230 Self { is_no_op: false, size: constants::DEFAULT_VMO_SIZE_BYTES, storage: None }
231 }
232}
233
234impl InspectorConfig {
235 pub fn no_op(mut self) -> Self {
237 self.is_no_op = true;
238 self
239 }
240
241 pub fn size(mut self, max_size: usize) -> Self {
243 self.size = max_size;
244 self
245 }
246
247 fn create_no_op(self) -> Inspector {
248 Inspector { storage: self.storage, root_node: Arc::new(Node::new_no_op()) }
249 }
250
251 fn adjusted_buffer_size(max_size: usize) -> usize {
252 let mut size = max(constants::MINIMUM_VMO_SIZE_BYTES, max_size);
253 if !size.is_multiple_of(constants::MINIMUM_VMO_SIZE_BYTES) {
255 size =
256 (1 + size / constants::MINIMUM_VMO_SIZE_BYTES) * constants::MINIMUM_VMO_SIZE_BYTES;
257 }
258
259 size
260 }
261}
262
263#[cfg(target_os = "fuchsia")]
264impl InspectorConfig {
265 pub fn vmo(mut self, vmo: zx::Vmo) -> Self {
268 self.storage = Some(Arc::new(vmo));
269 self.no_op()
270 }
271
272 fn build(self) -> Inspector {
273 if self.is_no_op {
274 return self.create_no_op();
275 }
276
277 match Self::new_root(self.size) {
278 Ok((storage, root_node)) => {
279 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
280 }
281 Err(e) => {
282 error!("Failed to create root node. Error: {:?}", e);
283 self.create_no_op()
284 }
285 }
286 }
287
288 fn new_root(
290 max_size: usize,
291 ) -> Result<(Arc<<Container as BlockContainer>::ShareableData>, Node), Error> {
292 let size = Self::adjusted_buffer_size(max_size);
293 let (container, vmo) = Container::read_and_write(size).map_err(Error::AllocateVmo)?;
294 let name = zx::Name::new("InspectHeap").unwrap();
295 vmo.set_name(&name).map_err(Error::AllocateVmo)?;
296 let vmo = Arc::new(vmo);
297 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
298 let state =
299 State::create(heap, vmo.clone()).map_err(|e| Error::CreateState(Box::new(e)))?;
300 Ok((vmo, Node::new_root(state)))
301 }
302}
303
304#[cfg(not(target_os = "fuchsia"))]
305impl InspectorConfig {
306 fn build(self) -> Inspector {
307 if self.is_no_op {
308 return self.create_no_op();
309 }
310
311 match Self::new_root(self.size) {
312 Ok((root_node, storage)) => {
313 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
314 }
315 Err(e) => {
316 error!("Failed to create root node. Error: {:?}", e);
317 self.create_no_op()
318 }
319 }
320 }
321
322 fn new_root(
323 max_size: usize,
324 ) -> Result<(Node, Arc<<Container as BlockContainer>::ShareableData>), Error> {
325 let size = Self::adjusted_buffer_size(max_size);
326 let (container, storage) = Container::read_and_write(size).unwrap();
327 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
328 let state =
329 State::create(heap, Arc::new(storage)).map_err(|e| Error::CreateState(Box::new(e)))?;
330 Ok((Node::new_root(state), Arc::new(storage)))
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use crate::assert_update_is_atomic;
338
339 #[fuchsia::test]
340 fn inspector_new() {
341 let test_object = Inspector::default();
342 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
343 }
344
345 #[fuchsia::test]
346 fn inspector_copy_data() {
347 let test_object = Inspector::default();
348
349 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
350
351 assert_eq!(test_object.copy_vmo_data().unwrap().len(), 4096);
353 }
354
355 #[fuchsia::test]
356 fn no_op() {
357 let inspector = Inspector::new(InspectorConfig::default().size(4096));
358 let nodes = (0..84)
360 .map(|i| inspector.root().create_child(format!("test-{i}")))
361 .collect::<Vec<Node>>();
362
363 assert!(nodes.iter().all(|node| node.is_valid()));
364 let no_op_node = inspector.root().create_child("no-op-child");
365 assert!(!no_op_node.is_valid());
366 }
367
368 #[fuchsia::test]
369 fn inspector_new_with_size() {
370 let test_object = Inspector::new(InspectorConfig::default().size(8192));
371 assert_eq!(test_object.max_size().unwrap(), 8192);
372
373 let test_object = Inspector::new(InspectorConfig::default().size(10000));
375 assert_eq!(test_object.max_size().unwrap(), 12288);
376
377 let test_object = Inspector::new(InspectorConfig::default().size(2000));
379 assert_eq!(test_object.max_size().unwrap(), 4096);
380 }
381
382 #[fuchsia::test]
383 async fn atomic_update() {
384 let insp = Inspector::default();
385 assert_update_is_atomic!(insp, |n| {
386 n.record_int("", 1);
387 n.record_int("", 2);
388 n.record_uint("", 3);
389 n.record_string("", "abcd");
390 });
391 }
392}
393
394#[cfg(all(test, target_os = "fuchsia"))]
396mod fuchsia_tests {
397 use super::*;
398 use assert_matches::assert_matches;
399
400 #[fuchsia::test]
401 fn inspector_duplicate_vmo() {
402 let test_object = Inspector::default();
403 assert_eq!(
404 test_object.storage.as_ref().unwrap().get_size().unwrap(),
405 constants::DEFAULT_VMO_SIZE_BYTES as u64
406 );
407 assert_eq!(
408 test_object.duplicate_vmo().unwrap().get_size().unwrap(),
409 constants::DEFAULT_VMO_SIZE_BYTES as u64
410 );
411 }
412
413 #[fuchsia::test]
414 fn inspector_new_root() {
415 let (vmo, root_node) = InspectorConfig::new_root(100).unwrap();
417 assert_eq!(vmo.get_size().unwrap(), 4096);
418 let inner = root_node.inner.inner_ref().unwrap();
419 assert_eq!(*inner.block_index, 0);
420 assert_eq!("InspectHeap", vmo.get_name().expect("Has name"));
421 }
422
423 #[fuchsia::test]
424 fn freeze_vmo_works() {
425 let inspector = Inspector::default();
426 let initial =
427 inspector.state().unwrap().with_current_header(|header| header.generation_count());
428 let vmo = inspector.frozen_vmo_copy();
429
430 assert!(!inspector.is_frozen());
431
432 let mut buffer: [u8; 32] = [0; 32];
433 inspector.storage.as_ref().unwrap().read(&mut buffer, 0).unwrap();
434 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
435 let gen_count = block.generation_count();
436
437 assert_eq!(initial + 2, gen_count);
438 assert!(gen_count.is_multiple_of(2));
439
440 let frozen_insp = Inspector::new(InspectorConfig::default().no_op().vmo(vmo.unwrap()));
441 assert!(frozen_insp.is_frozen());
442 }
443
444 #[fuchsia::test]
445 fn no_op_inspector_with_frozen_vmo_can_be_frozen() {
446 const VMO_SIZE: u64 = 4096;
447
448 let original_inspector = Inspector::new(InspectorConfig::default().size(VMO_SIZE as usize));
449 let frozen_vmo = original_inspector.frozen_vmo_copy().unwrap();
450 let inspector = Inspector::new(InspectorConfig::default().no_op().vmo(frozen_vmo));
451
452 assert_matches!(inspector.state(), None);
453 assert_matches!(inspector.frozen_vmo_copy(), Ok(copy) => {
454 assert_eq!(copy.get_size().unwrap(), VMO_SIZE);
455 });
456 }
457
458 #[fuchsia::test]
459 fn no_op_inspector_with_non_frozen_vmo_cannot_be_frozen() {
460 let inspect_vmo = zx::Vmo::create(4096).unwrap();
461 let inspector = Inspector::new(InspectorConfig::default().no_op().vmo(inspect_vmo));
462
463 assert_matches!(inspector.state(), None);
464 assert_matches!(inspector.frozen_vmo_copy(), Err(Error::VmoNotFrozen));
465 }
466
467 #[fuchsia::test]
468 fn transactions_block_freezing() {
469 let inspector = Inspector::default();
470 inspector.atomic_update(|_| assert!(inspector.frozen_vmo_copy().is_err()));
471 }
472
473 #[fuchsia::test]
474 fn transactions_block_copying() {
475 let inspector = Inspector::default();
476 inspector.atomic_update(|_| assert!(inspector.copy_vmo().is_none()));
477 inspector.atomic_update(|_| assert!(inspector.copy_vmo_data().is_none()));
478 }
479
480 #[fuchsia::test]
481 fn inspector_new_with_size() {
482 let test_object = Inspector::new(InspectorConfig::default().size(8192));
483 assert_eq!(test_object.max_size().unwrap(), 8192);
484
485 assert_eq!(
486 "InspectHeap",
487 test_object.storage.as_ref().unwrap().get_name().expect("Has name")
488 );
489
490 let test_object = Inspector::new(InspectorConfig::default().size(10000));
492 assert_eq!(test_object.max_size().unwrap(), 12288);
493
494 let test_object = Inspector::new(InspectorConfig::default().size(2000));
496 assert_eq!(test_object.max_size().unwrap(), 4096);
497 }
498}