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