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::{constants, BlockContainer, Container};
10use log::error;
11use std::borrow::Cow;
12use std::cmp::max;
13use std::sync::Arc;
14
15#[cfg(target_os = "fuchsia")]
16use zx::{self as zx, AsHandleRef, 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) -> Option<zx::Vmo> {
86 self.state()?.try_lock().ok().and_then(|mut state| state.frozen_vmo_copy().ok()).flatten()
87 }
88
89 pub fn copy_vmo(&self) -> Option<zx::Vmo> {
93 self.copy_vmo_data().and_then(|data| {
94 if let Ok(vmo) = zx::Vmo::create(data.len() as u64) {
95 vmo.write(&data, 0).ok().map(|_| vmo)
96 } else {
97 None
98 }
99 })
100 }
101
102 pub(crate) fn get_storage_handle(&self) -> Option<Arc<zx::Vmo>> {
103 self.storage.clone()
105 }
106
107 #[cfg(test)]
110 pub fn is_frozen(&self) -> Result<(), u64> {
111 use inspect_format::{BlockAccessorExt, Header};
112 let vmo = self.storage.as_ref().unwrap();
113 let mut buffer: [u8; 16] = [0; 16];
114 vmo.read(&mut buffer, 0).unwrap();
115 let block = buffer.block_at_unchecked::<Header>(inspect_format::BlockIndex::EMPTY);
116 if block.generation_count() == constants::VMO_FROZEN {
117 Ok(())
118 } else {
119 Err(block.generation_count())
120 }
121 }
122}
123
124#[cfg(not(target_os = "fuchsia"))]
125impl Inspector {
126 pub(crate) fn duplicate_vmo(&self) -> Option<<Container as BlockContainer>::Data> {
127 self.copy_vmo_data()
130 }
131
132 pub(crate) fn get_storage_handle(&self) -> Option<Vec<u8>> {
133 self.copy_vmo_data()
135 }
136}
137
138impl Default for Inspector {
139 fn default() -> Self {
140 Inspector::new(InspectorConfig::default())
141 }
142}
143
144impl Inspector {
145 pub fn new(conf: InspectorConfig) -> Self {
148 conf.build()
149 }
150
151 pub fn copy_vmo_data(&self) -> Option<Vec<u8>> {
156 self.root_node.inner.inner_ref().and_then(|inner_ref| inner_ref.state.copy_vmo_bytes())
157 }
158
159 pub fn max_size(&self) -> Option<usize> {
160 self.state()?.try_lock().ok().map(|state| state.stats().maximum_size)
161 }
162
163 pub fn is_valid(&self) -> bool {
165 self.root_node.is_valid()
170 }
171
172 pub fn root(&self) -> &Node {
174 &self.root_node
175 }
176
177 pub fn atomic_update<F, R>(&self, update_fn: F) -> R
180 where
181 F: FnOnce(&Node) -> R,
182 {
183 self.root().atomic_update(update_fn)
184 }
185
186 pub(crate) fn state(&self) -> Option<State> {
187 self.root().inner.inner_ref().map(|inner_ref| inner_ref.state.clone())
188 }
189}
190
191pub struct InspectorConfig {
193 is_no_op: bool,
194 size: usize,
195 storage: Option<Arc<<Container as BlockContainer>::ShareableData>>,
196}
197
198impl Default for InspectorConfig {
199 fn default() -> Self {
206 Self { is_no_op: false, size: constants::DEFAULT_VMO_SIZE_BYTES, storage: None }
207 }
208}
209
210impl InspectorConfig {
211 pub fn no_op(mut self) -> Self {
213 self.is_no_op = true;
214 self
215 }
216
217 pub fn size(mut self, max_size: usize) -> Self {
219 self.size = max_size;
220 self
221 }
222
223 fn create_no_op(self) -> Inspector {
224 Inspector { storage: self.storage, root_node: Arc::new(Node::new_no_op()) }
225 }
226
227 fn adjusted_buffer_size(max_size: usize) -> usize {
228 let mut size = max(constants::MINIMUM_VMO_SIZE_BYTES, max_size);
229 if size % constants::MINIMUM_VMO_SIZE_BYTES != 0 {
231 size =
232 (1 + size / constants::MINIMUM_VMO_SIZE_BYTES) * constants::MINIMUM_VMO_SIZE_BYTES;
233 }
234
235 size
236 }
237}
238
239#[cfg(target_os = "fuchsia")]
240impl InspectorConfig {
241 pub fn vmo(mut self, vmo: zx::Vmo) -> Self {
244 self.storage = Some(Arc::new(vmo));
245 self.no_op()
246 }
247
248 fn build(self) -> Inspector {
249 if self.is_no_op {
250 return self.create_no_op();
251 }
252
253 match Self::new_root(self.size) {
254 Ok((storage, root_node)) => {
255 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
256 }
257 Err(e) => {
258 error!("Failed to create root node. Error: {:?}", e);
259 self.create_no_op()
260 }
261 }
262 }
263
264 fn new_root(
266 max_size: usize,
267 ) -> Result<(Arc<<Container as BlockContainer>::ShareableData>, Node), Error> {
268 let size = Self::adjusted_buffer_size(max_size);
269 let (container, vmo) = Container::read_and_write(size).map_err(Error::AllocateVmo)?;
270 let name = zx::Name::new("InspectHeap").unwrap();
271 vmo.set_name(&name).map_err(Error::AllocateVmo)?;
272 let vmo = Arc::new(vmo);
273 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
274 let state =
275 State::create(heap, vmo.clone()).map_err(|e| Error::CreateState(Box::new(e)))?;
276 Ok((vmo, Node::new_root(state)))
277 }
278}
279
280#[cfg(not(target_os = "fuchsia"))]
281impl InspectorConfig {
282 fn build(self) -> Inspector {
283 if self.is_no_op {
284 return self.create_no_op();
285 }
286
287 match Self::new_root(self.size) {
288 Ok((root_node, storage)) => {
289 Inspector { storage: Some(storage), root_node: Arc::new(root_node) }
290 }
291 Err(e) => {
292 error!("Failed to create root node. Error: {:?}", e);
293 self.create_no_op()
294 }
295 }
296 }
297
298 fn new_root(
299 max_size: usize,
300 ) -> Result<(Node, Arc<<Container as BlockContainer>::ShareableData>), Error> {
301 let size = Self::adjusted_buffer_size(max_size);
302 let (container, storage) = Container::read_and_write(size).unwrap();
303 let heap = Heap::new(container).map_err(|e| Error::CreateHeap(Box::new(e)))?;
304 let state =
305 State::create(heap, Arc::new(storage)).map_err(|e| Error::CreateState(Box::new(e)))?;
306 Ok((Node::new_root(state), Arc::new(storage)))
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use crate::assert_update_is_atomic;
314
315 #[fuchsia::test]
316 fn inspector_new() {
317 let test_object = Inspector::default();
318 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
319 }
320
321 #[fuchsia::test]
322 fn inspector_copy_data() {
323 let test_object = Inspector::default();
324
325 assert_eq!(test_object.max_size().unwrap(), constants::DEFAULT_VMO_SIZE_BYTES);
326
327 assert_eq!(test_object.copy_vmo_data().unwrap().len(), 4096);
329 }
330
331 #[fuchsia::test]
332 fn no_op() {
333 let inspector = Inspector::new(InspectorConfig::default().size(4096));
334 let nodes = (0..84)
336 .map(|i| inspector.root().create_child(format!("test-{i}")))
337 .collect::<Vec<Node>>();
338
339 assert!(nodes.iter().all(|node| node.is_valid()));
340 let no_op_node = inspector.root().create_child("no-op-child");
341 assert!(!no_op_node.is_valid());
342 }
343
344 #[fuchsia::test]
345 fn inspector_new_with_size() {
346 let test_object = Inspector::new(InspectorConfig::default().size(8192));
347 assert_eq!(test_object.max_size().unwrap(), 8192);
348
349 let test_object = Inspector::new(InspectorConfig::default().size(10000));
351 assert_eq!(test_object.max_size().unwrap(), 12288);
352
353 let test_object = Inspector::new(InspectorConfig::default().size(2000));
355 assert_eq!(test_object.max_size().unwrap(), 4096);
356 }
357
358 #[fuchsia::test]
359 async fn atomic_update() {
360 let insp = Inspector::default();
361 assert_update_is_atomic!(insp, |n| {
362 n.record_int("", 1);
363 n.record_int("", 2);
364 n.record_uint("", 3);
365 n.record_string("", "abcd");
366 });
367 }
368}
369
370#[cfg(all(test, target_os = "fuchsia"))]
372mod fuchsia_tests {
373 use super::*;
374
375 #[fuchsia::test]
376 fn inspector_duplicate_vmo() {
377 let test_object = Inspector::default();
378 assert_eq!(
379 test_object.storage.as_ref().unwrap().get_size().unwrap(),
380 constants::DEFAULT_VMO_SIZE_BYTES as u64
381 );
382 assert_eq!(
383 test_object.duplicate_vmo().unwrap().get_size().unwrap(),
384 constants::DEFAULT_VMO_SIZE_BYTES as u64
385 );
386 }
387
388 #[fuchsia::test]
389 fn inspector_new_root() {
390 let (vmo, root_node) = InspectorConfig::new_root(100).unwrap();
392 assert_eq!(vmo.get_size().unwrap(), 4096);
393 let inner = root_node.inner.inner_ref().unwrap();
394 assert_eq!(*inner.block_index, 0);
395 assert_eq!("InspectHeap", vmo.get_name().expect("Has name"));
396 }
397
398 #[fuchsia::test]
399 fn freeze_vmo_works() {
400 let inspector = Inspector::default();
401 let initial =
402 inspector.state().unwrap().with_current_header(|header| header.generation_count());
403 let vmo = inspector.frozen_vmo_copy();
404
405 let is_frozen_result = inspector.is_frozen();
406 assert!(is_frozen_result.is_err());
407
408 assert_eq!(initial + 2, is_frozen_result.err().unwrap());
409 assert!(is_frozen_result.err().unwrap() % 2 == 0);
410
411 let frozen_insp = Inspector::new(InspectorConfig::default().no_op().vmo(vmo.unwrap()));
412 assert!(frozen_insp.is_frozen().is_ok());
413 }
414
415 #[fuchsia::test]
416 fn transactions_block_freezing() {
417 let inspector = Inspector::default();
418 inspector.atomic_update(|_| assert!(inspector.frozen_vmo_copy().is_none()));
419 }
420
421 #[fuchsia::test]
422 fn transactions_block_copying() {
423 let inspector = Inspector::default();
424 inspector.atomic_update(|_| assert!(inspector.copy_vmo().is_none()));
425 inspector.atomic_update(|_| assert!(inspector.copy_vmo_data().is_none()));
426 }
427
428 #[fuchsia::test]
429 fn inspector_new_with_size() {
430 let test_object = Inspector::new(InspectorConfig::default().size(8192));
431 assert_eq!(test_object.max_size().unwrap(), 8192);
432
433 assert_eq!(
434 "InspectHeap",
435 test_object.storage.as_ref().unwrap().get_name().expect("Has name")
436 );
437
438 let test_object = Inspector::new(InspectorConfig::default().size(10000));
440 assert_eq!(test_object.max_size().unwrap(), 12288);
441
442 let test_object = Inspector::new(InspectorConfig::default().size(2000));
444 assert_eq!(test_object.max_size().unwrap(), 4096);
445 }
446}