1use fuchsia_inspect::Node;
23use std::borrow::Cow;
24
25mod impls;
26mod wrappers;
27
28pub use wrappers::{InspectBytes, InspectList, InspectListClosure, InspectUintArray};
29
30pub trait WriteInspect {
32 fn write_inspect<'a>(&self, writer: &Node, key: impl Into<Cow<'a, str>>);
36}
37
38#[macro_export]
60macro_rules! inspect_log {
61 ($bounded_list_node:expr, $($args:tt)+) => {{
62 #[allow(unused_imports)] use $crate::{inspect_insert, nodes::{NodeTimeExt, BootTimeline}};
64 $bounded_list_node.add_entry(|node| {
65 NodeTimeExt::<BootTimeline>::record_time(node, "@time");
66 inspect_insert!(@internal_inspect_log node, $($args)+);
67 });
68 }};
69}
70
71#[macro_export]
81macro_rules! inspect_insert {
82 (@internal $node_writer:expr,) => {};
83
84 (@internal $node_writer:expr, var $key:ident: { $($sub:tt)+ }) => {{
86 let child_writer = $node_writer.create_child($key);
87 inspect_insert!(@internal child_writer, $($sub)+);
88 $node_writer.record(child_writer);
89 }};
90
91 (@internal $node_writer:expr, var $key:ident: { $($sub:tt)+ }, $($rest:tt)*) => {{
92 inspect_insert!(@internal $node_writer, var $key: { $($sub)+ });
93 inspect_insert!(@internal $node_writer, $($rest)*);
94 }};
95
96 (@internal $node_writer:expr, var $key:ident: $val:expr) => {{
98 $val.write_inspect(&$node_writer, $key);
99 }};
100
101 (@internal $node_writer:expr, var $key:ident: $val:expr, $($rest:tt)*) => {{
102 inspect_insert!(@internal $node_writer, var $key: $val);
103 inspect_insert!(@internal $node_writer, $($rest)*);
104 }};
105
106 (@internal $node_writer:expr, var $key:ident?: $val:expr) => {{
108 match $val {
109 Some(val) => inspect_insert!(@internal $node_writer, var $key: val),
110 None => (),
111 }
112 }};
113
114 (@internal $node_writer:expr, var $key:ident?: $val:expr, $($rest:tt)*) => {{
115 inspect_insert!(@internal $node_writer, var $key?: $val);
116 inspect_insert!(@internal $node_writer, $($rest)*);
117 }};
118
119 (@internal $node_writer:expr, $key:ident: $($rest:tt)+) => {{
121 let key = stringify!($key);
122 inspect_insert!(@internal $node_writer, var key: $($rest)+);
123 }};
124
125 (@internal $node_writer:expr, $key:ident?: $($rest:tt)+) => {{
126 let key = stringify!($key);
127 inspect_insert!(@internal $node_writer, var key?: $($rest)+);
128 }};
129
130 (@internal $node_writer:expr, $key:expr => $($rest:tt)+) => {{
131 let key: std::borrow::Cow<'_, _> = $key.into();
132 inspect_insert!(@internal $node_writer, var key: $($rest)+);
133 }};
134
135 (@internal_inspect_log $node_writer:expr, { $($args:tt)* }) => {{
137 #[allow(unused_imports)]
140 use $crate::log::WriteInspect;
141 inspect_insert!(@internal $node_writer, $($args)*);
142 }};
143
144 (@internal_inspect_log $node_writer:expr, $($args:tt)+) => {{
145 use $crate::log::WriteInspect;
146 inspect_insert!(@internal $node_writer, $($args)+);
147 }};
148
149 ($node_writer:expr, { $($args:tt)+ }) => {{
151 use $crate::log::WriteInspect;
152 inspect_insert!(@internal $node_writer, $($args)+);
153 }};
154
155 ($node_writer:expr, $($args:tt)+) => {{
157 use $crate::log::WriteInspect;
158 inspect_insert!(@internal $node_writer, $($args)+);
159 }};
160}
161
162#[macro_export]
187macro_rules! make_inspect_loggable {
188 ($($args:tt)+) => {{
189 #[allow(unused_imports)] use $crate::inspect_insert;
191 use fuchsia_inspect::Node;
192 use std::borrow::Cow;
193 struct WriteInspectClosure<F>(F);
194 impl<F> WriteInspect for WriteInspectClosure<F>
195 where
196 F: Fn(&Node, String) {
197 fn write_inspect<'b>(&self, writer: &Node, key: impl Into<Cow<'b, str>>) {
198 self.0(writer, key.into().to_string());
199 }
200 }
201 let f = WriteInspectClosure(move |writer: &Node, key| {
202 let child = writer.create_child(key);
203 inspect_insert!(child, $($args)+);
204 writer.record(child);
205 });
206 f
207 }};
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::nodes::BoundedListNode;
214 use diagnostics_assertions::{AnyProperty, assert_data_tree};
215 use fuchsia_inspect::{DiagnosticsHierarchyGetter, Inspector};
216 use fuchsia_sync::Mutex;
217 use test_util::assert_lt;
218
219 #[fuchsia::test]
220 async fn test_inspect_log_basic() {
221 let (inspector, mut node) = inspector_and_list_node();
222
223 inspect_log!(node, k1: "1".to_string(), meaning_of_life: 42u64, k3: 3i64, k4: 4f64);
225
226 inspect_log!(node, small_uint: 1u8, small_int: 2i8, float: 3f32);
228
229 inspect_log!(node, {
231 s: "str",
232 uint: &13u8,
233 });
234
235 inspect_log!(node, {});
237
238 let hierarchy = inspector.get_diagnostics_hierarchy().await;
239 assert_data_tree!(hierarchy, root: {
240 list_node: {
241 "0": { "@time": AnyProperty, k1: "1", meaning_of_life: 42u64, k3: 3i64, k4: 4f64 },
242 "1": { "@time": AnyProperty, small_uint: 1u64, small_int: 2i64, float: 3f64 },
243 "2": { "@time": AnyProperty, s: "str", uint: 13u64 },
244 "3": { "@time": AnyProperty },
245 }
246 });
247
248 let get_time = |index| {
249 hierarchy
250 .get_property_by_path(&["list_node", index, "@time"])
251 .and_then(|p| p.int())
252 .unwrap()
253 };
254 assert_lt!(get_time("0"), get_time("1"));
255 assert_lt!(get_time("1"), get_time("2"));
256 assert_lt!(get_time("2"), get_time("3"));
257 }
258
259 #[fuchsia::test]
260 async fn test_inspect_log_nested() {
261 let (inspector, mut node) = inspector_and_list_node();
262 inspect_log!(node, {
263 k1: {
264 sub1: "subval1",
265 sub2: {
266 subsub1: "subsubval1",
267 },
268 sub3: 3u64,
269 },
270 k2: if true { 10u64 } else { 20 }
271 });
272
273 assert_data_tree!(inspector, root: {
274 list_node: {
275 "0": {
276 "@time": AnyProperty,
277 k1: {
278 sub1: "subval1",
279 sub2: {
280 subsub1: "subsubval1",
281 },
282 sub3: 3u64,
283 },
284 k2: 10u64
285 }
286 }
287 });
288 }
289
290 #[fuchsia::test]
291 async fn test_inspect_log_var_key_syntax() {
292 let (inspector, mut node) = inspector_and_list_node();
293 let key = "@@@";
294 inspect_log!(node, var key: "!!!");
295
296 assert_data_tree!(inspector, root: {
297 list_node: {
298 "0": {
299 "@time": AnyProperty,
300 "@@@": "!!!"
301 }
302 }
303 });
304 }
305
306 #[fuchsia::test]
307 fn test_inspect_log_parsing() {
308 let (_inspector, mut node) = inspector_and_list_node();
310
311 inspect_log!(node, k1: "v1", k2: "v2");
313
314 inspect_log!(node, k1: "v1", k2: "v2",);
316
317 inspect_log!(node, {
319 k1: "v1",
320 k2: "v2"
321 });
322
323 inspect_log!(node, {
325 k1: "v1",
326 k2: "v2",
327 });
328 }
329
330 #[fuchsia::test]
331 fn test_inspect_log_allows_mutex_guard_temporary() {
332 let (_inspector, node) = inspector_and_list_node();
334 let node = Mutex::new(node);
335 inspect_log!(node.lock(), k1: "v1");
336 }
337
338 #[fuchsia::test]
339 fn test_inspect_log_macro_does_not_move_value() {
340 let (_inspector, mut node) = inspector_and_list_node();
342 let s = String::from("s");
343 inspect_log!(node, s: s);
344
345 println!("{s}");
347 }
348
349 #[fuchsia::test]
350 async fn test_log_option() {
351 let (inspector, mut node) = inspector_and_list_node();
352
353 inspect_log!(node, some?: Some("a"));
354
355 inspect_log!(node, none?: None as Option<String>);
356
357 assert_data_tree!(inspector, root: {
358 list_node: {
359 "0": { "@time": AnyProperty, some: "a" },
360 "1": { "@time": AnyProperty },
361 }
362 });
363 }
364
365 #[fuchsia::test]
366 async fn test_log_inspect_bytes() {
367 let (inspector, mut node) = inspector_and_list_node();
368 let bytes = [11u8, 22, 33];
369
370 inspect_log!(node, bytes: InspectBytes(&bytes));
371 inspect_log!(node, bytes: InspectBytes(&bytes[..]));
372 inspect_log!(node, bytes: InspectBytes(bytes));
373
374 assert_data_tree!(inspector, root: {
375 list_node: {
376 "0": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
377 "1": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
378 "2": { "@time": AnyProperty, bytes: vec![11u8, 22, 33] },
379 }
380 });
381 }
382
383 #[fuchsia::test]
384 async fn test_log_inspect_list() {
385 let (inspector, mut node) = inspector_and_list_node();
386 let list = [11u8, 22, 33];
387
388 inspect_log!(node, list: InspectList(&list));
389
390 assert_data_tree!(inspector, root: {
391 list_node: {
392 "0": {
393 "@time": AnyProperty,
394 list: {
395 "0": 11u64,
396 "1": 22u64,
397 "2": 33u64,
398 }
399 }
400 }
401 });
402 }
403
404 #[fuchsia::test]
405 async fn test_log_inspect_list_closure() {
406 let (inspector, mut node) = inspector_and_list_node();
407 let list = [13u32, 17, 29];
408 let list_mapped = InspectListClosure(&list, |node_writer, key, item| {
409 inspect_insert!(node_writer, var key: item * 2);
410 });
411
412 inspect_log!(node, list: list_mapped);
413
414 assert_data_tree!(inspector, root: {
415 list_node: {
416 "0": {
417 "@time": AnyProperty,
418 list: {
419 "0": 26u64,
420 "1": 34u64,
421 "2": 58u64,
422 }
423 }
424 }
425 });
426 }
427
428 #[fuchsia::test]
429 async fn test_log_inspect_uint_array() {
430 let (inspector, mut node) = inspector_and_list_node();
431 let list = [1u32, 2, 3, 4, 5, 6];
432
433 inspect_log!(node, list: InspectUintArray::new(&list));
434
435 assert_data_tree!(inspector, root: {
436 list_node: {
437 "0": {
438 "@time": AnyProperty,
439 list: vec![1u64, 2, 3, 4, 5, 6],
440 }
441 }
442 });
443 }
444
445 #[fuchsia::test]
446 fn test_inspect_insert_parsing() {
447 let (_inspector, mut node) = inspector_and_list_node();
449 let node_writer = node.add_entry(|node_writer| {
450 inspect_insert!(node_writer, k1: "v1".to_string(), k2: if true { 10u64 } else { 20 });
452 });
453
454 inspect_insert!(node_writer, k1: 1i64, k2: 2f64,);
456
457 inspect_insert!(node_writer, {
459 k1: 1u8,
460 k2: 2i8
461 });
462
463 inspect_insert!(node_writer, {
465 k1: &1u64,
466 k2?: Some("v2"),
467 });
468 }
469
470 #[fuchsia::test]
471 async fn test_make_inspect_loggable() {
472 let (inspector, mut node) = inspector_and_list_node();
473
474 let obj = make_inspect_loggable!(k1: "1", k2: 2i64, k3: "3");
475 inspect_log!(node, some_key: obj);
476
477 let point = Some((10i64, 50i64));
478 inspect_log!(node, point?: point.map(|(x, y)| make_inspect_loggable!({
479 x: x,
480 y: y,
481 })));
482
483 assert_data_tree!(inspector, root: {
484 list_node: {
485 "0": {
486 "@time": AnyProperty,
487 some_key: { k1: "1", k2: 2i64, k3: "3" },
488 },
489 "1": {
490 "@time": AnyProperty,
491 point: { x: 10i64, y: 50i64 },
492 },
493 }
494 });
495 }
496
497 #[fuchsia::test]
498 async fn test_log_inspect_string_reference() {
499 let (inspector, mut node) = inspector_and_list_node();
500
501 inspect_log!(node, "foo" => "foo_1");
502 inspect_log!(node, "foo" => "foo_2");
503 inspect_log!(node, "foo" => "foo_3");
504 inspect_log!(node, "foo" => "foo_4");
505
506 assert_data_tree!(inspector, root: {
507 list_node: {
508 "0": { "@time": AnyProperty, foo: "foo_1" },
509 "1": { "@time": AnyProperty, foo: "foo_2" },
510 "2": { "@time": AnyProperty, foo: "foo_3" },
511 "3": { "@time": AnyProperty, foo: "foo_4" },
512 }
513 });
514 }
515
516 fn inspector_and_list_node() -> (Inspector, BoundedListNode) {
517 let inspector = Inspector::default();
518 let list_node = inspector.root().create_child("list_node");
519 let list_node = BoundedListNode::new(list_node, 10);
520 (inspector, list_node)
521 }
522}