1use base64::display::Base64Display;
11use fidl_fuchsia_diagnostics::{
12 PropertySelector, Selector, StringSelector, StringSelectorUnknown, SubtreeSelector,
13 TreeSelector,
14};
15use num_derive::{FromPrimitive, ToPrimitive};
16use num_traits::bounds::Bounded;
17use selectors::ValidateExt;
18use serde::{Deserialize, Serialize};
19use std::borrow::{Borrow, Cow};
20use std::cmp::Ordering;
21use std::collections::BTreeMap;
22use std::fmt::{Display, Formatter, Result as FmtResult};
23use std::ops::{Add, AddAssign, MulAssign};
24use thiserror::Error;
25
26pub mod macros;
27pub mod serialization;
28
29pub const LINEAR_HISTOGRAM_EXTRA_SLOTS: usize = 4;
32
33pub const EXPONENTIAL_HISTOGRAM_EXTRA_SLOTS: usize = 5;
36
37#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, ToPrimitive)]
39#[repr(u8)]
40pub enum ArrayFormat {
41 Default = 0,
43
44 LinearHistogram = 1,
51
52 ExponentialHistogram = 2,
60}
61
62#[derive(Clone, Debug, PartialEq)]
67pub struct DiagnosticsHierarchy<Key = String> {
68 pub name: String,
70
71 pub properties: Vec<Property<Key>>,
73
74 pub children: Vec<DiagnosticsHierarchy<Key>>,
76
77 pub missing: Vec<MissingValue>,
79}
80
81#[derive(Clone, Debug, PartialEq)]
83pub struct MissingValue {
84 pub reason: MissingValueReason,
86
87 pub name: String,
89}
90
91#[derive(Clone, Debug, PartialEq)]
93pub enum MissingValueReason {
94 LinkNotFound,
96
97 LinkParseFailure,
99
100 LinkInvalid,
102
103 LinkNeverExpanded,
105
106 Timeout,
108}
109
110fn name_partial_cmp(a: &str, b: &str) -> Ordering {
113 match (a.parse::<u64>(), b.parse::<u64>()) {
114 (Ok(n), Ok(m)) => n.partial_cmp(&m).unwrap(),
115 _ => a.partial_cmp(b).unwrap(),
116 }
117}
118
119impl<Key> DiagnosticsHierarchy<Key>
120where
121 Key: AsRef<str>,
122{
123 pub fn sort(&mut self) {
125 self.properties.sort_by(|p1, p2| name_partial_cmp(p1.name(), p2.name()));
126 self.children.sort_by(|c1, c2| name_partial_cmp(&c1.name, &c2.name));
127 for child in self.children.iter_mut() {
128 child.sort();
129 }
130 }
131
132 pub fn new_root() -> Self {
134 DiagnosticsHierarchy::new("root", vec![], vec![])
135 }
136
137 pub fn new(
140 name: impl Into<String>,
141 properties: Vec<Property<Key>>,
142 children: Vec<DiagnosticsHierarchy<Key>>,
143 ) -> Self {
144 Self { name: name.into(), properties, children, missing: vec![] }
145 }
146
147 pub fn get_or_add_child_mut<T>(&mut self, name: T) -> &mut DiagnosticsHierarchy<Key>
150 where
151 T: AsRef<str>,
152 {
153 match (0..self.children.len()).find(|&i| self.children[i].name == name.as_ref()) {
158 Some(matching_index) => &mut self.children[matching_index],
159 None => {
160 self.children.push(DiagnosticsHierarchy::new(name.as_ref(), vec![], vec![]));
161 self.children
162 .last_mut()
163 .expect("We just added an entry so we cannot get None here.")
164 }
165 }
166 }
167
168 pub fn add_child(&mut self, insert: DiagnosticsHierarchy<Key>) {
173 self.children.push(insert);
174 }
175
176 pub fn get_or_add_node<T>(&mut self, node_path: &[T]) -> &mut DiagnosticsHierarchy<Key>
186 where
187 T: AsRef<str>,
188 {
189 assert!(!node_path.is_empty());
190 let mut iter = node_path.iter();
191 let first_path_string = iter.next().unwrap().as_ref();
192 assert_eq!(first_path_string, &self.name);
195 let mut curr_node = self;
196 for node_path_entry in iter {
197 curr_node = curr_node.get_or_add_child_mut(node_path_entry);
198 }
199 curr_node
200 }
201
202 pub fn add_property(&mut self, property: Property<Key>) {
204 self.properties.push(property);
205 }
206
207 pub fn add_property_at_path<T>(&mut self, node_path: &[T], property: Property<Key>)
217 where
218 T: AsRef<str>,
219 {
220 self.get_or_add_node(node_path).properties.push(property);
221 }
222
223 pub fn property_iter(&self) -> DiagnosticsHierarchyIterator<'_, Key, PropertyIter> {
225 DiagnosticsHierarchyIterator::new(self)
226 }
227
228 pub fn error_iter(&self) -> DiagnosticsHierarchyIterator<'_, Key, ErrorIter> {
229 DiagnosticsHierarchyIterator::new(self)
230 }
231
232 pub fn add_missing(&mut self, reason: MissingValueReason, name: String) {
234 self.missing.push(MissingValue { reason, name });
235 }
236 pub fn get_property(&self, name: &str) -> Option<&Property<Key>> {
238 self.properties.iter().find(|prop| prop.name() == name)
239 }
240
241 pub fn get_child(&self, name: &str) -> Option<&DiagnosticsHierarchy<Key>> {
243 self.children.iter().find(|node| node.name == name)
244 }
245
246 pub fn get_child_mut(&mut self, name: &str) -> Option<&mut DiagnosticsHierarchy<Key>> {
248 self.children.iter_mut().find(|node| node.name == name)
249 }
250
251 pub fn get_child_by_path(&self, path: &[&str]) -> Option<&DiagnosticsHierarchy<Key>> {
253 let mut result = Some(self);
254 for name in path {
255 result = result.and_then(|node| node.get_child(name));
256 }
257 result
258 }
259
260 pub fn get_child_by_path_mut(
262 &mut self,
263 path: &[&str],
264 ) -> Option<&mut DiagnosticsHierarchy<Key>> {
265 let mut result = Some(self);
266 for name in path {
267 result = result.and_then(|node| node.get_child_mut(name));
268 }
269 result
270 }
271
272 pub fn get_property_by_path(&self, path: &[&str]) -> Option<&Property<Key>> {
274 let node = self.get_child_by_path(&path[..path.len() - 1]);
275 node.and_then(|node| node.get_property(path[path.len() - 1]))
276 }
277}
278
279macro_rules! property_type_getters_ref {
280 ($([$variant:ident, $fn_name:ident, $type:ty]),*) => {
281 paste::item! {
282 impl<Key> Property<Key> {
283 $(
284 #[doc = "Returns the " $variant " value or `None` if the property isn't of that type"]
285 pub fn $fn_name(&self) -> Option<&$type> {
286 match self {
287 Property::$variant(_, value) => Some(value),
288 _ => None,
289 }
290 }
291 )*
292 }
293 }
294 }
295}
296
297macro_rules! property_type_getters_copy {
298 ($([$variant:ident, $fn_name:ident, $type:ty]),*) => {
299 paste::item! {
300 impl<Key> Property<Key> {
301 $(
302 #[doc = "Returns the " $variant " value or `None` if the property isn't of that type"]
303 pub fn $fn_name(&self) -> Option<$type> {
304 match self {
305 Property::$variant(_, value) => Some(*value),
306 _ => None,
307 }
308 }
309 )*
310 }
311 }
312 }
313}
314
315property_type_getters_copy!(
316 [Int, int, i64],
317 [Uint, uint, u64],
318 [Double, double, f64],
319 [Bool, boolean, bool]
320);
321
322property_type_getters_ref!(
323 [String, string, str],
324 [Bytes, bytes, [u8]],
325 [DoubleArray, double_array, ArrayContent<f64>],
326 [IntArray, int_array, ArrayContent<i64>],
327 [UintArray, uint_array, ArrayContent<u64>],
328 [StringList, string_list, [String]]
329);
330
331struct WorkStackEntry<'a, Key> {
332 node: &'a DiagnosticsHierarchy<Key>,
333 key: Vec<&'a str>,
334}
335
336pub struct PropertyIter;
337pub struct ErrorIter;
338
339pub struct DiagnosticsHierarchyIterator<'a, Key, PropOrIterMarker> {
340 work_stack: Vec<WorkStackEntry<'a, Key>>,
341 current_key: Vec<&'a str>,
342 current_node: Option<&'a DiagnosticsHierarchy<Key>>,
343 current_index: usize,
344 phantom: std::marker::PhantomData<PropOrIterMarker>,
345}
346
347enum EndOfTheLine<'a, T, Key> {
348 Yes(Option<T>),
349 No(&'a DiagnosticsHierarchy<Key>),
350}
351
352impl<'a, Key, Marker> DiagnosticsHierarchyIterator<'a, Key, Marker> {
353 fn new(hierarchy: &'a DiagnosticsHierarchy<Key>) -> Self {
355 DiagnosticsHierarchyIterator {
356 work_stack: vec![WorkStackEntry { node: hierarchy, key: vec![&hierarchy.name] }],
357 current_key: vec![],
358 current_node: None,
359 current_index: 0,
360 phantom: std::marker::PhantomData,
361 }
362 }
363
364 fn get_node<T, U: 'a, F: FnOnce(&'a DiagnosticsHierarchy<Key>) -> &Vec<U>>(
366 &mut self,
367 iterable_node_data: F,
368 ) -> EndOfTheLine<'a, (Vec<&'a str>, Option<&'a T>), Key> {
369 match self.current_node {
370 Some(node) => EndOfTheLine::No(node),
372 None => {
373 let Some(WorkStackEntry { node, key }) = self.work_stack.pop() else {
376 return EndOfTheLine::Yes(None);
377 };
378
379 for child in node.children.iter() {
381 let mut child_key = key.clone();
382 child_key.push(&child.name);
383 self.work_stack.push(WorkStackEntry { node: child, key: child_key })
384 }
385
386 if iterable_node_data(node).is_empty() {
389 return EndOfTheLine::Yes(Some((key.clone(), None)));
390 }
391
392 self.current_index = 0;
393 self.current_key = key;
394
395 EndOfTheLine::No(node)
396 }
397 }
398 }
399
400 fn advance_index<T>(
401 &mut self,
402 data: &'a [T],
403 new_current: &'a DiagnosticsHierarchy<Key>,
404 ) -> &'a T {
405 let datum = &data[self.current_index];
406 self.current_index += 1;
407 self.current_node = Some(new_current);
408 datum
409 }
410}
411
412impl<'a, Key> Iterator for DiagnosticsHierarchyIterator<'a, Key, PropertyIter> {
413 type Item = (Vec<&'a str>, Option<&'a Property<Key>>);
418
419 fn next(&mut self) -> Option<Self::Item> {
420 loop {
421 let node = match self.get_node(|node| &node.properties) {
422 EndOfTheLine::Yes(r) => return r,
423 EndOfTheLine::No(n) => n,
424 };
425
426 if self.current_index == node.properties.len() {
428 self.current_node = None;
429 continue;
430 }
431
432 let property = self.advance_index(&node.properties, node);
435
436 return Some((self.current_key.clone(), Some(property)));
437 }
438 }
439}
440
441impl<'a, Key> Iterator for DiagnosticsHierarchyIterator<'a, Key, ErrorIter> {
442 type Item = (Vec<&'a str>, Option<&'a MissingValue>);
447
448 fn next(&mut self) -> Option<Self::Item> {
449 loop {
450 let node = match self.get_node(|node| &node.missing) {
451 EndOfTheLine::Yes(r) => return r,
452 EndOfTheLine::No(n) => n,
453 };
454
455 if self.current_index == node.missing.len() {
457 self.current_node = None;
458 continue;
459 }
460
461 let err = self.advance_index(&node.missing, node);
464 return Some((self.current_key.clone(), Some(err)));
465 }
466 }
467}
468
469#[derive(Debug, PartialEq, Clone)]
475pub enum Property<Key = String> {
476 String(Key, String),
478
479 Bytes(Key, Vec<u8>),
481
482 Int(Key, i64),
484
485 Uint(Key, u64),
487
488 Double(Key, f64),
490
491 Bool(Key, bool),
493
494 DoubleArray(Key, ArrayContent<f64>),
496
497 IntArray(Key, ArrayContent<i64>),
499
500 UintArray(Key, ArrayContent<u64>),
502
503 StringList(Key, Vec<String>),
505}
506
507impl<K> Property<K> {
508 pub fn key(&self) -> &K {
510 match self {
511 Property::String(k, _) => k,
512 Property::Bytes(k, _) => k,
513 Property::Int(k, _) => k,
514 Property::Uint(k, _) => k,
515 Property::Double(k, _) => k,
516 Property::Bool(k, _) => k,
517 Property::DoubleArray(k, _) => k,
518 Property::IntArray(k, _) => k,
519 Property::UintArray(k, _) => k,
520 Property::StringList(k, _) => k,
521 }
522 }
523
524 pub fn discriminant_name(&self) -> &'static str {
527 match self {
528 Property::String(_, _) => "String",
529 Property::Bytes(_, _) => "Bytes",
530 Property::Int(_, _) => "Int",
531 Property::IntArray(_, _) => "IntArray",
532 Property::Uint(_, _) => "Uint",
533 Property::UintArray(_, _) => "UintArray",
534 Property::Double(_, _) => "Double",
535 Property::DoubleArray(_, _) => "DoubleArray",
536 Property::Bool(_, _) => "Bool",
537 Property::StringList(_, _) => "StringList",
538 }
539 }
540
541 pub fn number_as_int(&self) -> Option<i64> {
547 match self {
548 Property::Int(_, i) => Some(*i),
549 Property::Uint(_, u) => i64::try_from(*u).ok(),
550 Property::String(..)
551 | Property::Bytes(..)
552 | Property::Double(..)
553 | Property::Bool(..)
554 | Property::DoubleArray(..)
555 | Property::IntArray(..)
556 | Property::UintArray(..)
557 | Property::StringList(..) => None,
558 }
559 }
560}
561
562impl<K> Display for Property<K>
563where
564 K: AsRef<str>,
565{
566 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
567 macro_rules! pair {
568 ($fmt:literal, $val:expr) => {
569 write!(f, "{}={}", self.key().as_ref(), format_args!($fmt, $val))
570 };
571 }
572 match self {
573 Property::String(_, v) => pair!("{}", v),
574 Property::Bytes(_, v) => {
575 pair!("b64:{}", Base64Display::new(v, &base64::engine::general_purpose::STANDARD))
576 }
577 Property::Int(_, v) => pair!("{}", v),
578 Property::Uint(_, v) => pair!("{}", v),
579 Property::Double(_, v) => pair!("{}", v),
580 Property::Bool(_, v) => pair!("{}", v),
581 Property::DoubleArray(_, v) => pair!("{:?}", v),
582 Property::IntArray(_, v) => pair!("{:?}", v),
583 Property::UintArray(_, v) => pair!("{:?}", v),
584 Property::StringList(_, v) => pair!("{:?}", v),
585 }
586 }
587}
588
589#[derive(Debug, Error)]
591pub enum Error {
592 #[error("Missing elements for {histogram_type:?} histogram. Expected {expected}, got {actual}")]
593 MissingHistogramElements { histogram_type: ArrayFormat, expected: usize, actual: usize },
594
595 #[error("TreeSelector only supports property and subtree selection.")]
596 InvalidTreeSelector,
597
598 #[error(transparent)]
599 Selectors(#[from] selectors::Error),
600
601 #[error(transparent)]
602 InvalidSelector(#[from] selectors::ValidationError),
603}
604
605impl Error {
606 fn missing_histogram_elements(
607 histogram_type: ArrayFormat,
608 actual: usize,
609 expected: usize,
610 ) -> Self {
611 Self::MissingHistogramElements { histogram_type, actual, expected }
612 }
613}
614
615#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
617#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
618pub struct LinearHistogram<T> {
619 pub size: usize,
621
622 pub floor: T,
624
625 pub step: T,
627
628 pub counts: Vec<T>,
630
631 #[serde(skip_serializing_if = "Option::is_none")]
633 pub indexes: Option<Vec<usize>>,
634}
635
636#[cfg_attr(feature = "json_schema", derive(schemars::JsonSchema))]
638#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
639pub struct ExponentialHistogram<T> {
640 pub size: usize,
642
643 pub floor: T,
645
646 pub initial_step: T,
648
649 pub step_multiplier: T,
651
652 pub counts: Vec<T>,
654
655 #[serde(skip_serializing_if = "Option::is_none")]
657 pub indexes: Option<Vec<usize>>,
658}
659
660#[derive(Debug, PartialEq, Clone)]
663pub enum ArrayContent<T> {
664 Values(Vec<T>),
666
667 LinearHistogram(LinearHistogram<T>),
669
670 ExponentialHistogram(ExponentialHistogram<T>),
672}
673
674impl<T> ArrayContent<T>
675where
676 T: Add<Output = T> + num_traits::Zero + AddAssign + Copy + MulAssign + PartialEq + Bounded,
677{
678 pub fn new(values: Vec<T>, format: ArrayFormat) -> Result<Self, Error> {
680 match format {
681 ArrayFormat::Default => Ok(Self::Values(values)),
682 ArrayFormat::LinearHistogram => {
683 if values.len() < 5 {
686 return Err(Error::missing_histogram_elements(
687 ArrayFormat::LinearHistogram,
688 values.len(),
689 5,
690 ));
691 }
692 let original_counts = &values[2..];
693 let (counts, indexes) =
694 match serialization::maybe_condense_histogram(original_counts, &None) {
695 None => (original_counts.to_vec(), None),
696 Some((counts, indexes)) => (counts, Some(indexes)),
697 };
698 Ok(Self::LinearHistogram(LinearHistogram {
699 floor: values[0],
700 step: values[1],
701 counts,
702 indexes,
703 size: values.len() - 2,
704 }))
705 }
706 ArrayFormat::ExponentialHistogram => {
707 if values.len() < 6 {
710 return Err(Error::missing_histogram_elements(
711 ArrayFormat::LinearHistogram,
712 values.len(),
713 5,
714 ));
715 }
716 let original_counts = &values[3..];
717 let (counts, indexes) =
718 match serialization::maybe_condense_histogram(original_counts, &None) {
719 None => (original_counts.to_vec(), None),
720 Some((counts, indexes)) => (counts, Some(indexes)),
721 };
722 Ok(Self::ExponentialHistogram(ExponentialHistogram {
723 floor: values[0],
724 initial_step: values[1],
725 step_multiplier: values[2],
726 counts,
727 indexes,
728 size: values.len() - 3,
729 }))
730 }
731 }
732 }
733
734 pub fn len(&self) -> usize {
736 match self {
737 Self::Values(vals) => vals.len(),
738 Self::LinearHistogram(LinearHistogram { size, .. })
739 | Self::ExponentialHistogram(ExponentialHistogram { size, .. }) => *size,
740 }
741 }
742
743 pub fn is_empty(&self) -> bool {
745 self.len() == 0
746 }
747
748 pub fn raw_values(&self) -> Cow<'_, Vec<T>> {
751 match self {
752 Self::Values(values) => Cow::Borrowed(values),
753 Self::LinearHistogram(LinearHistogram { size, counts, indexes, .. })
754 | Self::ExponentialHistogram(ExponentialHistogram { size, counts, indexes, .. }) => {
755 if let Some(indexes) = indexes {
756 let mut values = vec![T::zero(); *size];
757 for (count, index) in counts.iter().zip(indexes.iter()) {
758 if index <= size {
759 values[*index] = *count;
760 }
761 }
762 Cow::Owned(values)
763 } else {
764 Cow::Borrowed(counts)
765 }
766 }
767 }
768 }
769}
770
771pub mod testing {
772 use crate::ArrayContent;
773 use num_traits::bounds::Bounded;
774 use std::ops::{Add, AddAssign, MulAssign};
775
776 pub trait CondensableOnDemand {
779 fn condense_histogram(&mut self);
780 }
781
782 fn condense_counts<T: num_traits::Zero + Copy + PartialEq>(
783 counts: &[T],
784 ) -> (Vec<T>, Vec<usize>) {
785 let mut condensed_counts = vec![];
786 let mut indexes = vec![];
787 for (index, count) in counts.iter().enumerate() {
788 if *count != T::zero() {
789 condensed_counts.push(*count);
790 indexes.push(index);
791 }
792 }
793 (condensed_counts, indexes)
794 }
795
796 impl<T> CondensableOnDemand for ArrayContent<T>
797 where
798 T: Add<Output = T> + num_traits::Zero + AddAssign + Copy + MulAssign + PartialEq + Bounded,
799 {
800 fn condense_histogram(&mut self) {
801 match self {
802 Self::Values(_) => (),
803 Self::LinearHistogram(histogram) => {
804 if histogram.indexes.is_some() {
805 return;
806 }
807 let (counts, indexes) = condense_counts(&histogram.counts);
808 histogram.counts = counts;
809 histogram.indexes = Some(indexes);
810 }
811 Self::ExponentialHistogram(histogram) => {
812 if histogram.indexes.is_some() {
813 return;
814 }
815 let (counts, indexes) = condense_counts(&histogram.counts);
816 histogram.counts = counts;
817 histogram.indexes = Some(indexes);
818 }
819 }
820 }
821 }
822}
823
824impl<Key> Property<Key>
825where
826 Key: AsRef<str>,
827{
828 pub fn name(&self) -> &str {
830 match self {
831 Property::String(name, _)
832 | Property::Bytes(name, _)
833 | Property::Int(name, _)
834 | Property::IntArray(name, _)
835 | Property::Uint(name, _)
836 | Property::UintArray(name, _)
837 | Property::Double(name, _)
838 | Property::Bool(name, _)
839 | Property::DoubleArray(name, _)
840 | Property::StringList(name, _) => name.as_ref(),
841 }
842 }
843}
844
845impl<T: Borrow<Selector>> TryFrom<&[T]> for HierarchyMatcher {
846 type Error = Error;
847
848 fn try_from(selectors: &[T]) -> Result<Self, Self::Error> {
849 let mut matcher = HierarchyMatcher::default();
852 for selector in selectors {
853 let selector = selector.borrow();
854 selector.validate().map_err(|e| Error::Selectors(e.into()))?;
855
856 match selector.tree_selector.clone().unwrap() {
860 TreeSelector::SubtreeSelector(subtree_selector) => {
861 matcher.insert_subtree(subtree_selector.clone());
862 }
863 TreeSelector::PropertySelector(property_selector) => {
864 matcher.insert_property(property_selector.clone());
865 }
866 _ => return Err(Error::Selectors(selectors::Error::InvalidTreeSelector)),
867 }
868 }
869 Ok(matcher)
870 }
871}
872
873impl<T: Borrow<Selector>> TryFrom<Vec<T>> for HierarchyMatcher {
874 type Error = Error;
875
876 fn try_from(selectors: Vec<T>) -> Result<Self, Self::Error> {
877 selectors[..].try_into()
878 }
879}
880
881#[derive(Debug)]
882struct OrdStringSelector(StringSelector);
883
884impl From<StringSelector> for OrdStringSelector {
885 fn from(selector: StringSelector) -> Self {
886 Self(selector)
887 }
888}
889
890impl Ord for OrdStringSelector {
891 fn cmp(&self, other: &OrdStringSelector) -> Ordering {
892 match (&self.0, &other.0) {
893 (StringSelector::ExactMatch(s), StringSelector::ExactMatch(o)) => s.cmp(o),
894 (StringSelector::StringPattern(s), StringSelector::StringPattern(o)) => s.cmp(o),
895 (StringSelector::ExactMatch(_), StringSelector::StringPattern(_)) => Ordering::Less,
896 (StringSelector::StringPattern(_), StringSelector::ExactMatch(_)) => Ordering::Greater,
897 (StringSelectorUnknown!(), StringSelector::ExactMatch(_)) => Ordering::Less,
898 (StringSelectorUnknown!(), StringSelector::StringPattern(_)) => Ordering::Less,
899 (StringSelectorUnknown!(), StringSelectorUnknown!()) => Ordering::Equal,
900 }
901 }
902}
903
904impl PartialOrd for OrdStringSelector {
905 fn partial_cmp(&self, other: &OrdStringSelector) -> Option<Ordering> {
906 Some(self.cmp(other))
907 }
908}
909
910impl PartialEq for OrdStringSelector {
911 fn eq(&self, other: &OrdStringSelector) -> bool {
912 match (&self.0, &other.0) {
913 (StringSelector::ExactMatch(s), StringSelector::ExactMatch(o)) => s.eq(o),
914 (StringSelector::StringPattern(s), StringSelector::StringPattern(o)) => s.eq(o),
915 (StringSelector::ExactMatch(_), StringSelector::StringPattern(_)) => false,
916 (StringSelector::StringPattern(_), StringSelector::ExactMatch(_)) => false,
917 (StringSelectorUnknown!(), StringSelectorUnknown!()) => true,
918 }
919 }
920}
921
922impl Eq for OrdStringSelector {}
923
924#[derive(Default, Debug)]
925pub struct HierarchyMatcher {
926 nodes: BTreeMap<OrdStringSelector, HierarchyMatcher>,
927 properties: Vec<OrdStringSelector>,
928 subtree: bool,
929}
930
931impl HierarchyMatcher {
932 pub fn new<I>(selectors: I) -> Result<Self, Error>
933 where
934 I: Iterator<Item = Selector>,
935 {
936 let mut matcher = HierarchyMatcher::default();
937 for selector in selectors {
938 selector.validate().map_err(|e| Error::Selectors(e.into()))?;
939
940 match selector.tree_selector.unwrap() {
942 TreeSelector::SubtreeSelector(subtree_selector) => {
943 matcher.insert_subtree(subtree_selector);
944 }
945 TreeSelector::PropertySelector(property_selector) => {
946 matcher.insert_property(property_selector);
947 }
948 _ => return Err(Error::Selectors(selectors::Error::InvalidTreeSelector)),
949 }
950 }
951 Ok(matcher)
952 }
953
954 fn insert_subtree(&mut self, selector: SubtreeSelector) {
955 self.insert(selector.node_path, None);
956 }
957
958 fn insert_property(&mut self, selector: PropertySelector) {
959 self.insert(selector.node_path, Some(selector.target_properties));
960 }
961
962 fn insert(&mut self, node_path: Vec<StringSelector>, property: Option<StringSelector>) {
963 let mut matcher = self;
967 for node in node_path {
968 matcher = matcher.nodes.entry(node.into()).or_default();
969 }
970 match property {
971 Some(property) => {
972 matcher.properties.push(property.into());
973 }
974 None => matcher.subtree = true,
975 }
976 }
977}
978
979#[derive(Debug, PartialEq)]
980pub enum SelectResult<'a, Key> {
981 Properties(Vec<&'a Property<Key>>),
982 Nodes(Vec<&'a DiagnosticsHierarchy<Key>>),
983}
984
985impl<'a, Key> SelectResult<'a, Key> {
986 fn add_property(&mut self, prop: &'a Property<Key>) {
988 let Self::Properties(v) = self else {
989 panic!("must be Self::Properties to call add_property");
990 };
991 v.push(prop);
992 }
993
994 fn add_node(&mut self, node: &'a DiagnosticsHierarchy<Key>) {
996 let Self::Nodes(v) = self else {
997 panic!("must be Self::Nodes to call add_node");
998 };
999 v.push(node);
1000 }
1001}
1002
1003pub fn select_from_hierarchy<'a, 'b, Key>(
1004 root_node: &'a DiagnosticsHierarchy<Key>,
1005 selector: &'b Selector,
1006) -> Result<SelectResult<'a, Key>, Error>
1007where
1008 Key: AsRef<str>,
1009 'a: 'b,
1010{
1011 selector.validate()?;
1012
1013 struct StackEntry<'a, Key> {
1014 node: &'a DiagnosticsHierarchy<Key>,
1015 node_path_index: usize,
1016 explored_path: Vec<&'a str>,
1017 }
1018
1019 let (node_path, property_selector, stack_entry) = match selector.tree_selector.as_ref().unwrap()
1021 {
1022 TreeSelector::SubtreeSelector(ref subtree_selector) => (
1023 &subtree_selector.node_path,
1024 None,
1025 StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1026 ),
1027 TreeSelector::PropertySelector(ref property_selector) => (
1028 &property_selector.node_path,
1029 Some(&property_selector.target_properties),
1030 StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1031 ),
1032 _ => return Err(Error::InvalidTreeSelector),
1033 };
1034
1035 let mut stack = vec![stack_entry];
1036 let mut result = if property_selector.is_some() {
1037 SelectResult::Properties(vec![])
1038 } else {
1039 SelectResult::Nodes(vec![])
1040 };
1041
1042 while let Some(StackEntry { node, node_path_index, mut explored_path }) = stack.pop() {
1043 if !selectors::match_string(&node_path[node_path_index], &node.name) {
1045 continue;
1046 }
1047 explored_path.push(&node.name);
1048
1049 if node_path_index != node_path.len() - 1 {
1052 for child in node.children.iter() {
1054 stack.push(StackEntry {
1055 node: child,
1056 node_path_index: node_path_index + 1,
1057 explored_path: explored_path.clone(),
1058 });
1059 }
1060 } else if let Some(s) = property_selector {
1061 for property in &node.properties {
1063 if selectors::match_string(s, property.key()) {
1064 result.add_property(property);
1065 }
1066 }
1067 } else {
1068 result.add_node(node);
1071 }
1072 }
1073
1074 Ok(result)
1075}
1076
1077pub fn filter_tree<Key>(
1079 root_node: DiagnosticsHierarchy<Key>,
1080 selectors: &[TreeSelector],
1081) -> Option<DiagnosticsHierarchy<Key>>
1082where
1083 Key: AsRef<str>,
1084{
1085 let mut matcher = HierarchyMatcher::default();
1086 for selector in selectors {
1087 match selector {
1088 TreeSelector::SubtreeSelector(subtree_selector) => {
1089 matcher.insert_subtree(subtree_selector.clone());
1090 }
1091 TreeSelector::PropertySelector(property_selector) => {
1092 matcher.insert_property(property_selector.clone());
1093 }
1094 _ => {}
1095 }
1096 }
1097 filter_hierarchy(root_node, &matcher)
1098}
1099
1100pub fn filter_hierarchy<Key>(
1106 mut root_node: DiagnosticsHierarchy<Key>,
1107 hierarchy_matcher: &HierarchyMatcher,
1108) -> Option<DiagnosticsHierarchy<Key>>
1109where
1110 Key: AsRef<str>,
1111{
1112 let starts_empty = root_node.children.is_empty() && root_node.properties.is_empty();
1113 if filter_hierarchy_helper(&mut root_node, &[hierarchy_matcher]) {
1114 if !starts_empty && root_node.children.is_empty() && root_node.properties.is_empty() {
1115 return None;
1116 }
1117 return Some(root_node);
1118 }
1119 None
1120}
1121
1122fn filter_hierarchy_helper<Key>(
1123 node: &mut DiagnosticsHierarchy<Key>,
1124 hierarchy_matchers: &[&HierarchyMatcher],
1125) -> bool
1126where
1127 Key: AsRef<str>,
1128{
1129 let child_matchers = eval_matchers_on_node_name(&node.name, hierarchy_matchers);
1130 if child_matchers.is_empty() {
1131 node.children.clear();
1132 node.properties.clear();
1133 return false;
1134 }
1135
1136 if child_matchers.iter().any(|m| m.subtree) {
1137 return true;
1138 }
1139
1140 node.children.retain_mut(|child| filter_hierarchy_helper(child, &child_matchers));
1141 node.properties.retain_mut(|prop| eval_matchers_on_property(prop.name(), &child_matchers));
1142
1143 !(node.children.is_empty() && node.properties.is_empty())
1144}
1145
1146fn eval_matchers_on_node_name<'a>(
1147 node_name: &'a str,
1148 matchers: &'a [&'a HierarchyMatcher],
1149) -> Vec<&'a HierarchyMatcher> {
1150 let mut result = vec![];
1151 for matcher in matchers {
1152 for (node_pattern, tree_matcher) in matcher.nodes.iter() {
1153 if selectors::match_string(&node_pattern.0, node_name) {
1154 result.push(tree_matcher);
1155 }
1156 }
1157 }
1158 result
1159}
1160
1161fn eval_matchers_on_property(property_name: &str, matchers: &[&HierarchyMatcher]) -> bool {
1162 matchers.iter().any(|matcher| {
1163 matcher
1164 .properties
1165 .iter()
1166 .any(|property_pattern| selectors::match_string(&property_pattern.0, property_name))
1167 })
1168}
1169
1170#[derive(Clone)]
1172pub struct ExponentialHistogramParams<T: Clone> {
1173 pub floor: T,
1175
1176 pub initial_step: T,
1178
1179 pub step_multiplier: T,
1181
1182 pub buckets: usize,
1185}
1186
1187#[derive(Clone)]
1189pub struct LinearHistogramParams<T: Clone> {
1190 pub floor: T,
1192
1193 pub step_size: T,
1195
1196 pub buckets: usize,
1199}
1200
1201pub trait DiagnosticsHierarchyGetter<K: Clone> {
1204 fn get_diagnostics_hierarchy<'a>(
1205 &'a self,
1206 ) -> impl std::future::Future<Output = Cow<'_, DiagnosticsHierarchy<K>>>
1207 where
1208 K: 'a;
1209}
1210
1211impl<K: Clone> DiagnosticsHierarchyGetter<K> for DiagnosticsHierarchy<K> {
1212 async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy<K>>
1213 where
1214 K: 'a,
1215 {
1216 Cow::Borrowed(self)
1217 }
1218}
1219
1220#[cfg(test)]
1221mod tests {
1222 use super::*;
1223 use crate::testing::CondensableOnDemand;
1224 use test_case::test_case;
1225
1226 use assert_matches::assert_matches;
1227 use selectors::VerboseError;
1228 use std::sync::Arc;
1229
1230 fn validate_hierarchy_iteration(
1231 mut results_vec: Vec<(Vec<String>, Option<Property>)>,
1232 test_hierarchy: DiagnosticsHierarchy,
1233 ) {
1234 let expected_num_entries = results_vec.len();
1235 let mut num_entries = 0;
1236 for (key, val) in test_hierarchy.property_iter() {
1237 num_entries += 1;
1238 let (expected_key, expected_property) = results_vec.pop().unwrap();
1239 assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1240 assert_eq!(val, expected_property.as_ref());
1241 }
1242
1243 assert_eq!(num_entries, expected_num_entries);
1244 }
1245
1246 fn validate_hierarchy_error_iteration(
1247 mut results_vec: Vec<(Vec<String>, Option<MissingValue>)>,
1248 test_hierarchy: DiagnosticsHierarchy,
1249 ) {
1250 let expected_num_entries = results_vec.len();
1251 let mut num_entries = 0;
1252 for (key, reason) in test_hierarchy.error_iter() {
1253 num_entries += 1;
1254 let (expected_key, expected_reason) = results_vec.pop().unwrap();
1255 assert_eq!(reason, expected_reason.as_ref());
1256 assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1257 }
1258
1259 assert_eq!(num_entries, expected_num_entries);
1260 }
1261
1262 #[fuchsia::test]
1263 fn test_diagnostics_hierarchy_property_iteration() {
1264 let double_array_data = vec![-1.2, 2.3, 3.4, 4.5, -5.6];
1265 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
1266 let string_data = chars.iter().cycle().take(6000).collect::<String>();
1267 let bytes_data = (0u8..=9u8).cycle().take(5000).collect::<Vec<u8>>();
1268
1269 let test_hierarchy = DiagnosticsHierarchy::new(
1270 "root".to_string(),
1271 vec![
1272 Property::Int("int-root".to_string(), 3),
1273 Property::DoubleArray(
1274 "property-double-array".to_string(),
1275 ArrayContent::Values(double_array_data.clone()),
1276 ),
1277 ],
1278 vec![DiagnosticsHierarchy::new(
1279 "child-1".to_string(),
1280 vec![
1281 Property::Uint("property-uint".to_string(), 10),
1282 Property::Double("property-double".to_string(), -3.4),
1283 Property::String("property-string".to_string(), string_data.clone()),
1284 Property::IntArray(
1285 "property-int-array".to_string(),
1286 ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1287 .unwrap(),
1288 ),
1289 ],
1290 vec![DiagnosticsHierarchy::new(
1291 "child-1-1".to_string(),
1292 vec![
1293 Property::Int("property-int".to_string(), -9),
1294 Property::Bytes("property-bytes".to_string(), bytes_data.clone()),
1295 Property::UintArray(
1296 "property-uint-array".to_string(),
1297 ArrayContent::new(
1298 vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1299 ArrayFormat::ExponentialHistogram,
1300 )
1301 .unwrap(),
1302 ),
1303 ],
1304 vec![],
1305 )],
1306 )],
1307 );
1308
1309 let results_vec = vec![
1310 (
1311 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1312 Some(Property::UintArray(
1313 "property-uint-array".to_string(),
1314 ArrayContent::new(
1315 vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1316 ArrayFormat::ExponentialHistogram,
1317 )
1318 .unwrap(),
1319 )),
1320 ),
1321 (
1322 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1323 Some(Property::Bytes("property-bytes".to_string(), bytes_data)),
1324 ),
1325 (
1326 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1327 Some(Property::Int("property-int".to_string(), -9)),
1328 ),
1329 (
1330 vec!["root".to_string(), "child-1".to_string()],
1331 Some(Property::IntArray(
1332 "property-int-array".to_string(),
1333 ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1334 .unwrap(),
1335 )),
1336 ),
1337 (
1338 vec!["root".to_string(), "child-1".to_string()],
1339 Some(Property::String("property-string".to_string(), string_data)),
1340 ),
1341 (
1342 vec!["root".to_string(), "child-1".to_string()],
1343 Some(Property::Double("property-double".to_string(), -3.4)),
1344 ),
1345 (
1346 vec!["root".to_string(), "child-1".to_string()],
1347 Some(Property::Uint("property-uint".to_string(), 10)),
1348 ),
1349 (
1350 vec!["root".to_string()],
1351 Some(Property::DoubleArray(
1352 "property-double-array".to_string(),
1353 ArrayContent::Values(double_array_data),
1354 )),
1355 ),
1356 (vec!["root".to_string()], Some(Property::Int("int-root".to_string(), 3))),
1357 ];
1358
1359 validate_hierarchy_iteration(results_vec, test_hierarchy);
1360 }
1361
1362 #[fuchsia::test]
1363 fn test_diagnostics_hierarchy_error_iteration() {
1364 let mut test_hierarchy = DiagnosticsHierarchy::new(
1365 "root".to_string(),
1366 vec![],
1367 vec![
1368 DiagnosticsHierarchy::new(
1369 "child-1".to_string(),
1370 vec![],
1371 vec![DiagnosticsHierarchy::new("child-1-1".to_string(), vec![], vec![])],
1372 ),
1373 DiagnosticsHierarchy::new("child-2".to_string(), vec![], vec![]),
1374 ],
1375 );
1376
1377 test_hierarchy.add_missing(MissingValueReason::LinkInvalid, "root".to_string());
1378 test_hierarchy.children[0]
1379 .add_missing(MissingValueReason::LinkNeverExpanded, "child-1".to_string());
1380 test_hierarchy.children[0].children[0]
1381 .add_missing(MissingValueReason::Timeout, "child-1-1".to_string());
1382
1383 let results_vec = vec![
1384 (
1385 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1386 Some(MissingValue {
1387 reason: MissingValueReason::Timeout,
1388 name: "child-1-1".to_string(),
1389 }),
1390 ),
1391 (
1392 vec!["root".to_string(), "child-1".to_string()],
1393 Some(MissingValue {
1394 reason: MissingValueReason::LinkNeverExpanded,
1395 name: "child-1".to_string(),
1396 }),
1397 ),
1398 (vec!["root".to_string(), "child-2".to_string()], None),
1399 (
1400 vec!["root".to_string()],
1401 Some(MissingValue {
1402 reason: MissingValueReason::LinkInvalid,
1403 name: "root".to_string(),
1404 }),
1405 ),
1406 ];
1407
1408 validate_hierarchy_error_iteration(results_vec, test_hierarchy);
1409 }
1410
1411 #[fuchsia::test]
1412 fn test_getters() {
1413 let a_prop = Property::Int("a".to_string(), 1);
1414 let b_prop = Property::Uint("b".to_string(), 2);
1415 let child2 = DiagnosticsHierarchy::new("child2".to_string(), vec![], vec![]);
1416 let child = DiagnosticsHierarchy::new(
1417 "child".to_string(),
1418 vec![b_prop.clone()],
1419 vec![child2.clone()],
1420 );
1421 let mut hierarchy = DiagnosticsHierarchy::new(
1422 "root".to_string(),
1423 vec![a_prop.clone()],
1424 vec![child.clone()],
1425 );
1426 assert_matches!(hierarchy.get_child("child"), Some(node) if *node == child);
1427 assert_matches!(hierarchy.get_child_mut("child"), Some(node) if *node == child);
1428 assert_matches!(hierarchy.get_child_by_path(&["child", "child2"]),
1429 Some(node) if *node == child2);
1430 assert_matches!(hierarchy.get_child_by_path_mut(&["child", "child2"]),
1431 Some(node) if *node == child2);
1432 assert_matches!(hierarchy.get_property("a"), Some(prop) if *prop == a_prop);
1433 assert_matches!(hierarchy.get_property_by_path(&["child", "b"]),
1434 Some(prop) if *prop == b_prop);
1435 }
1436
1437 #[fuchsia::test]
1438 fn test_edge_case_hierarchy_iteration() {
1439 let root_only_with_one_property_hierarchy = DiagnosticsHierarchy::new(
1440 "root".to_string(),
1441 vec![Property::Int("property-int".to_string(), -9)],
1442 vec![],
1443 );
1444
1445 let results_vec =
1446 vec![(vec!["root".to_string()], Some(Property::Int("property-int".to_string(), -9)))];
1447
1448 validate_hierarchy_iteration(results_vec, root_only_with_one_property_hierarchy);
1449
1450 let empty_hierarchy = DiagnosticsHierarchy::new("root".to_string(), vec![], vec![]);
1451
1452 let results_vec = vec![(vec!["root".to_string()], None)];
1453
1454 validate_hierarchy_iteration(results_vec, empty_hierarchy);
1455
1456 let empty_root_populated_child = DiagnosticsHierarchy::new(
1457 "root",
1458 vec![],
1459 vec![DiagnosticsHierarchy::new(
1460 "foo",
1461 vec![Property::Int("11".to_string(), -4)],
1462 vec![],
1463 )],
1464 );
1465
1466 let results_vec = vec![
1467 (
1468 vec!["root".to_string(), "foo".to_string()],
1469 Some(Property::Int("11".to_string(), -4)),
1470 ),
1471 (vec!["root".to_string()], None),
1472 ];
1473
1474 validate_hierarchy_iteration(results_vec, empty_root_populated_child);
1475
1476 let empty_root_empty_child = DiagnosticsHierarchy::new(
1477 "root",
1478 vec![],
1479 vec![DiagnosticsHierarchy::new("foo", vec![], vec![])],
1480 );
1481
1482 let results_vec = vec![
1483 (vec!["root".to_string(), "foo".to_string()], None),
1484 (vec!["root".to_string()], None),
1485 ];
1486
1487 validate_hierarchy_iteration(results_vec, empty_root_empty_child);
1488 }
1489
1490 #[fuchsia::test]
1491 fn array_value() {
1492 let values = vec![1, 2, 5, 7, 9, 11, 13];
1493 let array = ArrayContent::<u64>::new(values.clone(), ArrayFormat::Default);
1494 assert_matches!(array, Ok(ArrayContent::Values(vals)) if vals == values);
1495 }
1496
1497 #[fuchsia::test]
1498 fn linear_histogram_array_value() {
1499 let values = vec![1, 2, 5, 7, 9, 11, 13];
1500 let array = ArrayContent::<i64>::new(values, ArrayFormat::LinearHistogram);
1501 assert_matches!(array, Ok(ArrayContent::LinearHistogram(hist))
1502 if hist == LinearHistogram {
1503 floor: 1,
1504 step: 2,
1505 counts: vec![5, 7, 9, 11, 13],
1506 indexes: None,
1507 size: 5,
1508 }
1509 );
1510 }
1511
1512 #[fuchsia::test]
1513 fn exponential_histogram_array_value() {
1514 let values = vec![1.0, 2.0, 5.0, 7.0, 9.0, 11.0, 15.0];
1515 let array = ArrayContent::<f64>::new(values, ArrayFormat::ExponentialHistogram);
1516 assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1517 if hist == ExponentialHistogram {
1518 floor: 1.0,
1519 initial_step: 2.0,
1520 step_multiplier: 5.0,
1521 counts: vec![7.0, 9.0, 11.0, 15.0],
1522 indexes: None,
1523 size: 4,
1524 }
1525 );
1526 }
1527
1528 #[fuchsia::test]
1529 fn deserialize_linear_int_histogram() -> Result<(), serde_json::Error> {
1530 let json = r#"{
1531 "root": {
1532 "histogram": {
1533 "floor": -2,
1534 "step": 3,
1535 "counts": [4, 5, 6],
1536 "size": 3
1537 }
1538 }
1539 }"#;
1540 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1541 let expected = DiagnosticsHierarchy::new(
1542 "root".to_string(),
1543 vec![Property::IntArray(
1544 "histogram".to_string(),
1545 ArrayContent::new(vec![-2, 3, 4, 5, 6], ArrayFormat::LinearHistogram).unwrap(),
1546 )],
1547 vec![],
1548 );
1549 assert_eq!(parsed, expected);
1550 Ok(())
1551 }
1552
1553 #[fuchsia::test]
1554 fn deserialize_exponential_int_histogram() -> Result<(), serde_json::Error> {
1555 let json = r#"{
1556 "root": {
1557 "histogram": {
1558 "floor": 1,
1559 "initial_step": 3,
1560 "step_multiplier": 2,
1561 "counts": [4, 5, 6],
1562 "size": 3
1563 }
1564 }
1565 }"#;
1566 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1567 let expected = DiagnosticsHierarchy::new(
1568 "root".to_string(),
1569 vec![Property::IntArray(
1570 "histogram".to_string(),
1571 ArrayContent::new(vec![1, 3, 2, 4, 5, 6], ArrayFormat::ExponentialHistogram)
1572 .unwrap(),
1573 )],
1574 vec![],
1575 );
1576 assert_eq!(parsed, expected);
1577 Ok(())
1578 }
1579
1580 #[fuchsia::test]
1581 fn deserialize_linear_uint_histogram() -> Result<(), serde_json::Error> {
1582 let json = r#"{
1583 "root": {
1584 "histogram": {
1585 "floor": 2,
1586 "step": 3,
1587 "counts": [4, 9223372036854775808, 6],
1588 "size": 3
1589 }
1590 }
1591 }"#;
1592 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1593 let expected = DiagnosticsHierarchy::new(
1594 "root".to_string(),
1595 vec![Property::UintArray(
1596 "histogram".to_string(),
1597 ArrayContent::new(
1598 vec![2, 3, 4, 9_223_372_036_854_775_808, 6],
1599 ArrayFormat::LinearHistogram,
1600 )
1601 .unwrap(),
1602 )],
1603 vec![],
1604 );
1605 assert_eq!(parsed, expected);
1606 Ok(())
1607 }
1608
1609 #[fuchsia::test]
1610 fn deserialize_linear_double_histogram() -> Result<(), serde_json::Error> {
1611 let json = r#"{
1612 "root": {
1613 "histogram": {
1614 "floor": 2.0,
1615 "step": 3.0,
1616 "counts": [4.0, 5.0, 6.0],
1617 "size": 3
1618 }
1619 }
1620 }"#;
1621 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1622 let expected = DiagnosticsHierarchy::new(
1623 "root".to_string(),
1624 vec![Property::DoubleArray(
1625 "histogram".to_string(),
1626 ArrayContent::new(vec![2.0, 3.0, 4.0, 5.0, 6.0], ArrayFormat::LinearHistogram)
1627 .unwrap(),
1628 )],
1629 vec![],
1630 );
1631 assert_eq!(parsed, expected);
1632 Ok(())
1633 }
1634
1635 #[fuchsia::test]
1636 fn deserialize_sparse_histogram() -> Result<(), serde_json::Error> {
1637 let json = r#"{
1638 "root": {
1639 "histogram": {
1640 "floor": 2,
1641 "step": 3,
1642 "counts": [4, 5, 6],
1643 "indexes": [1, 2, 4],
1644 "size": 8
1645 }
1646 }
1647 }"#;
1648 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1649
1650 let mut histogram =
1651 ArrayContent::new(vec![2, 3, 0, 4, 5, 0, 6, 0, 0, 0], ArrayFormat::LinearHistogram)
1652 .unwrap();
1653 histogram.condense_histogram();
1654 let expected = DiagnosticsHierarchy::new(
1655 "root".to_string(),
1656 vec![Property::IntArray("histogram".to_string(), histogram)],
1657 vec![],
1658 );
1659 assert_eq!(parsed, expected);
1660 Ok(())
1661 }
1662
1663 #[fuchsia::test]
1668 fn reject_histogram_incompatible_values() -> Result<(), serde_json::Error> {
1669 let json = r#"{
1670 "root": {
1671 "histogram": {
1672 "floor": -2,
1673 "step": 3,
1674 "counts": [4, 9223372036854775808, 6],
1675 "size": 3
1676 }
1677 }
1678 }"#;
1679 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1680 assert_eq!(parsed.children.len(), 1);
1681 assert_eq!(&parsed.children[0].name, "histogram");
1682 Ok(())
1683 }
1684
1685 #[fuchsia::test]
1686 fn reject_histogram_bad_sparse_list() -> Result<(), serde_json::Error> {
1687 let json = r#"{
1688 "root": {
1689 "histogram": {
1690 "floor": -2,
1691 "step": 3,
1692 "counts": [4, 5, 6],
1693 "indexes": [0, 1, 2, 3],
1694 "size": 8
1695 }
1696 }
1697 }"#;
1698 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1699 assert_eq!(parsed.children.len(), 1);
1700 assert_eq!(&parsed.children[0].name, "histogram");
1701 Ok(())
1702 }
1703
1704 #[fuchsia::test]
1705 fn reject_histogram_bad_index() -> Result<(), serde_json::Error> {
1706 let json = r#"{
1707 "root": {
1708 "histogram": {
1709 "floor": -2,
1710 "step": 3,
1711 "counts": [4, 5, 6],
1712 "indexes": [0, 1, 4],
1713 "size": 4
1714 }
1715 }
1716 }"#;
1717 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1718 assert_eq!(parsed.children.len(), 1);
1719 assert_eq!(&parsed.children[0].name, "histogram");
1720 Ok(())
1721 }
1722
1723 #[fuchsia::test]
1724 fn reject_histogram_wrong_field() -> Result<(), serde_json::Error> {
1725 let json = r#"{
1726 "root": {
1727 "histogram": {
1728 "floor": 2,
1729 "step": 3,
1730 "counts": [4, 5, 6],
1731 "incorrect": [0, 1, 3],
1732 "size": 4
1733 }
1734 }
1735 }"#;
1736 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1737 assert_eq!(parsed.children.len(), 1);
1738 assert_eq!(&parsed.children[0].name, "histogram");
1739 Ok(())
1740 }
1741
1742 #[fuchsia::test]
1743 fn exponential_histogram() {
1744 let values = vec![0, 2, 4, 0, 1, 2, 3, 4, 5];
1745 let array = ArrayContent::new(values, ArrayFormat::ExponentialHistogram);
1746 assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1747 if hist == ExponentialHistogram {
1748 floor: 0,
1749 initial_step: 2,
1750 step_multiplier: 4,
1751 counts: vec![0, 1, 2, 3, 4, 5],
1752 indexes: None,
1753 size: 6,
1754 }
1755 );
1756 }
1757
1758 #[fuchsia::test]
1759 fn add_to_hierarchy() {
1760 let mut hierarchy = DiagnosticsHierarchy::new_root();
1761 let prop_1 = Property::String("x".to_string(), "foo".to_string());
1762 let path_1 = vec!["root", "one"];
1763 let prop_2 = Property::Uint("c".to_string(), 3);
1764 let path_2 = vec!["root", "two"];
1765 let prop_2_prime = Property::Int("z".to_string(), -4);
1766 hierarchy.add_property_at_path(&path_1, prop_1.clone());
1767 hierarchy.add_property_at_path(&path_2.clone(), prop_2.clone());
1768 hierarchy.add_property_at_path(&path_2, prop_2_prime.clone());
1769
1770 assert_eq!(
1771 hierarchy,
1772 DiagnosticsHierarchy {
1773 name: "root".to_string(),
1774 children: vec![
1775 DiagnosticsHierarchy {
1776 name: "one".to_string(),
1777 properties: vec![prop_1],
1778 children: vec![],
1779 missing: vec![],
1780 },
1781 DiagnosticsHierarchy {
1782 name: "two".to_string(),
1783 properties: vec![prop_2, prop_2_prime],
1784 children: vec![],
1785 missing: vec![],
1786 }
1787 ],
1788 properties: vec![],
1789 missing: vec![],
1790 }
1791 );
1792 }
1793
1794 #[fuchsia::test]
1795 fn string_lists() {
1796 let mut hierarchy = DiagnosticsHierarchy::new_root();
1797 let prop_1 =
1798 Property::StringList("x".to_string(), vec!["foo".to_string(), "bar".to_string()]);
1799 let path_1 = vec!["root", "one"];
1800 hierarchy.add_property_at_path(&path_1, prop_1.clone());
1801
1802 assert_eq!(
1803 hierarchy,
1804 DiagnosticsHierarchy {
1805 name: "root".to_string(),
1806 children: vec![DiagnosticsHierarchy {
1807 name: "one".to_string(),
1808 properties: vec![prop_1],
1809 children: vec![],
1810 missing: vec![],
1811 },],
1812 properties: vec![],
1813 missing: vec![],
1814 }
1815 );
1816 }
1817
1818 #[fuchsia::test]
1819 #[cfg_attr(feature = "variant_asan", ignore)]
1821 #[cfg_attr(feature = "variant_hwasan", ignore)]
1822 #[should_panic]
1823 fn no_empty_paths_allowed() {
1825 let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1826 let path_1: Vec<&String> = vec![];
1827 hierarchy.get_or_add_node(&path_1);
1828 }
1829
1830 #[fuchsia::test]
1831 #[should_panic]
1832 fn path_must_start_at_self() {
1835 let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1836 let path_1 = vec!["not_root", "a"];
1837 hierarchy.get_or_add_node(&path_1);
1838 }
1839
1840 #[fuchsia::test]
1841 fn sort_hierarchy() {
1842 let mut hierarchy = DiagnosticsHierarchy::new(
1843 "root",
1844 vec![
1845 Property::String("x".to_string(), "foo".to_string()),
1846 Property::Uint("c".to_string(), 3),
1847 Property::Int("z".to_string(), -4),
1848 ],
1849 vec![
1850 DiagnosticsHierarchy::new(
1851 "foo",
1852 vec![
1853 Property::Int("11".to_string(), -4),
1854 Property::Bytes("123".to_string(), "foo".bytes().collect()),
1855 Property::Double("0".to_string(), 8.1),
1856 ],
1857 vec![],
1858 ),
1859 DiagnosticsHierarchy::new("bar", vec![], vec![]),
1860 ],
1861 );
1862
1863 hierarchy.sort();
1864
1865 let sorted_hierarchy = DiagnosticsHierarchy::new(
1866 "root",
1867 vec![
1868 Property::Uint("c".to_string(), 3),
1869 Property::String("x".to_string(), "foo".to_string()),
1870 Property::Int("z".to_string(), -4),
1871 ],
1872 vec![
1873 DiagnosticsHierarchy::new("bar", vec![], vec![]),
1874 DiagnosticsHierarchy::new(
1875 "foo",
1876 vec![
1877 Property::Double("0".to_string(), 8.1),
1878 Property::Int("11".to_string(), -4),
1879 Property::Bytes("123".to_string(), "foo".bytes().collect()),
1880 ],
1881 vec![],
1882 ),
1883 ],
1884 );
1885 assert_eq!(sorted_hierarchy, hierarchy);
1886 }
1887
1888 fn parse_selectors_and_filter_hierarchy(
1889 hierarchy: DiagnosticsHierarchy,
1890 test_selectors: Vec<&str>,
1891 ) -> Option<DiagnosticsHierarchy> {
1892 let parsed_test_selectors = test_selectors
1893 .into_iter()
1894 .map(|selector_string| {
1895 Arc::new(
1896 selectors::parse_selector::<VerboseError>(selector_string)
1897 .expect("All test selectors are valid and parsable."),
1898 )
1899 })
1900 .collect::<Vec<Arc<Selector>>>();
1901
1902 let hierarchy_matcher: HierarchyMatcher = parsed_test_selectors.try_into().unwrap();
1903
1904 filter_hierarchy(hierarchy, &hierarchy_matcher).map(|mut hierarchy| {
1905 hierarchy.sort();
1906 hierarchy
1907 })
1908 }
1909
1910 fn get_test_hierarchy() -> DiagnosticsHierarchy {
1911 DiagnosticsHierarchy::new(
1912 "root",
1913 vec![
1914 Property::String("x".to_string(), "foo".to_string()),
1915 Property::Uint("c".to_string(), 3),
1916 Property::Int("z".to_string(), -4),
1917 ],
1918 vec![
1919 make_foo(),
1920 DiagnosticsHierarchy::new(
1921 "bar",
1922 vec![Property::Int("12".to_string(), -4)],
1923 vec![DiagnosticsHierarchy::new(
1924 "zed",
1925 vec![Property::Int("13/:".to_string(), -4)],
1926 vec![],
1927 )],
1928 ),
1929 ],
1930 )
1931 }
1932
1933 fn make_all_foo_props() -> Vec<Property> {
1934 vec![
1935 Property::Int("11".to_string(), -4),
1936 Property::Bytes("123".to_string(), b"foo".to_vec()),
1937 Property::Double("0".to_string(), 8.1),
1938 ]
1939 }
1940
1941 fn make_zed() -> Vec<DiagnosticsHierarchy> {
1942 vec![DiagnosticsHierarchy::new("zed", vec![Property::Int("13".to_string(), -4)], vec![])]
1943 }
1944
1945 fn make_foo() -> DiagnosticsHierarchy {
1946 DiagnosticsHierarchy::new("foo", make_all_foo_props(), make_zed())
1947 }
1948
1949 #[fuchsia::test]
1950 fn test_filter_hierarchy() {
1951 let test_selectors = vec!["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#];
1952
1953 assert_eq!(
1954 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1955 Some(DiagnosticsHierarchy::new(
1956 "root",
1957 vec![Property::Int("z".to_string(), -4),],
1958 vec![
1959 DiagnosticsHierarchy::new(
1960 "bar",
1961 vec![],
1962 vec![DiagnosticsHierarchy::new(
1963 "zed",
1964 vec![Property::Int("13/:".to_string(), -4)],
1965 vec![],
1966 )],
1967 ),
1968 DiagnosticsHierarchy::new(
1969 "foo",
1970 vec![Property::Int("11".to_string(), -4),],
1971 vec![],
1972 )
1973 ],
1974 ))
1975 );
1976
1977 let test_selectors = vec!["*:root"];
1978 let mut sorted_expected = get_test_hierarchy();
1979 sorted_expected.sort();
1980 assert_eq!(
1981 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1982 Some(sorted_expected)
1983 );
1984 }
1985
1986 #[fuchsia::test]
1987 fn test_filter_does_not_include_empty_node() {
1988 let test_selectors = vec!["*:root/foo:blorg"];
1989
1990 assert_eq!(
1991 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1992 None,
1993 );
1994 }
1995
1996 #[fuchsia::test]
1997 fn test_filter_empty_hierarchy() {
1998 let test_selectors = vec!["*:root"];
1999
2000 assert_eq!(
2001 parse_selectors_and_filter_hierarchy(
2002 DiagnosticsHierarchy::new("root", vec![], vec![]),
2003 test_selectors
2004 ),
2005 Some(DiagnosticsHierarchy::new("root", vec![], vec![])),
2006 );
2007 }
2008
2009 #[fuchsia::test]
2010 fn test_full_filtering() {
2011 let test_selectors = vec!["*:non-existent-root"];
2013 assert_eq!(
2014 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2015 None,
2016 );
2017
2018 let test_selectors = vec!["*:root/i-dont-exist:foo"];
2020 assert_eq!(
2021 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2022 None,
2023 );
2024
2025 let test_selectors = vec!["*:root:i-dont-exist"];
2028 assert_eq!(
2029 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2030 None,
2031 );
2032 }
2033
2034 #[fuchsia::test]
2035 fn test_subtree_selection_includes_empty_nodes() {
2036 let test_selectors = vec!["*:root"];
2037 let mut empty_hierarchy = DiagnosticsHierarchy::new(
2038 "root",
2039 vec![],
2040 vec![
2041 DiagnosticsHierarchy::new(
2042 "foo",
2043 vec![],
2044 vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2045 ),
2046 DiagnosticsHierarchy::new(
2047 "bar",
2048 vec![],
2049 vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2050 ),
2051 ],
2052 );
2053
2054 empty_hierarchy.sort();
2055
2056 assert_eq!(
2057 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), test_selectors),
2058 Some(empty_hierarchy)
2059 );
2060 }
2061
2062 #[fuchsia::test]
2063 fn test_empty_tree_filtering() {
2064 let mut empty_hierarchy = DiagnosticsHierarchy::new("root", vec![], vec![]);
2066 empty_hierarchy.sort();
2067
2068 let subtree_selector = vec!["*:root"];
2069 assert_eq!(
2070 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), subtree_selector),
2071 Some(empty_hierarchy.clone())
2072 );
2073
2074 let fake_property_selector = vec!["*:root:blorp"];
2076 assert_eq!(
2077 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), fake_property_selector),
2078 None,
2079 );
2080 }
2081
2082 #[test_case(vec![Property::Int("11".to_string(), -4)], "root/foo:11" ; "specific_property")]
2083 #[test_case(make_all_foo_props(), "root/foo:*" ; "many_properties")]
2084 #[test_case(vec![], "root/foo:none" ; "property_not_there")]
2085 #[fuchsia::test]
2086 fn test_select_from_hierarchy_property_selectors(expected: Vec<Property>, tree_selector: &str) {
2087 let hierarchy = get_test_hierarchy();
2088 let parsed_selector =
2089 selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2090 .expect("All test selectors are valid and parsable.");
2091 let Ok(SelectResult::Properties(mut property_entry_vec)) =
2092 select_from_hierarchy(&hierarchy, &parsed_selector)
2093 else {
2094 panic!("must be properties");
2095 };
2096
2097 property_entry_vec.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2098 let mut expected = expected.iter().map(Borrow::borrow).collect::<Vec<_>>();
2099 expected.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2100
2101 assert_eq!(property_entry_vec, expected);
2102 }
2103
2104 #[test_case(vec![], "root/none" ; "node_not_there")]
2105 #[test_case(make_zed(), "root/foo/zed" ; "properties_only")]
2106 #[test_case(vec![make_foo()], "root/foo" ; "nodes_and_properties")]
2107 #[test_case(vec![get_test_hierarchy()], "root" ; "select_root")]
2108 #[fuchsia::test]
2109 fn test_select_from_hierarchy_tree_selectors(
2110 expected: Vec<DiagnosticsHierarchy>,
2111 tree_selector: &str,
2112 ) {
2113 let hierarchy = get_test_hierarchy();
2114 let parsed_selector =
2115 selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2116 .expect("All test selectors are valid and parsable.");
2117 let Ok(SelectResult::Nodes(node_vec)) = select_from_hierarchy(&hierarchy, &parsed_selector)
2118 else {
2119 panic!("must be nodes");
2120 };
2121
2122 let expected = expected.iter().map(Borrow::borrow).collect::<Vec<_>>();
2123
2124 assert_eq!(node_vec, expected);
2125 }
2126
2127 #[fuchsia::test]
2128 fn sort_numerical_value() {
2129 let mut diagnostics_hierarchy = DiagnosticsHierarchy::new(
2130 "root",
2131 vec![
2132 Property::Double("2".to_string(), 2.3),
2133 Property::Int("0".to_string(), -4),
2134 Property::Uint("10".to_string(), 3),
2135 Property::String("1".to_string(), "test".to_string()),
2136 ],
2137 vec![
2138 DiagnosticsHierarchy::new("123", vec![], vec![]),
2139 DiagnosticsHierarchy::new("34", vec![], vec![]),
2140 DiagnosticsHierarchy::new("4", vec![], vec![]),
2141 DiagnosticsHierarchy::new("023", vec![], vec![]),
2142 DiagnosticsHierarchy::new("12", vec![], vec![]),
2143 DiagnosticsHierarchy::new("1", vec![], vec![]),
2144 ],
2145 );
2146 diagnostics_hierarchy.sort();
2147 assert_eq!(
2148 diagnostics_hierarchy,
2149 DiagnosticsHierarchy::new(
2150 "root",
2151 vec![
2152 Property::Int("0".to_string(), -4),
2153 Property::String("1".to_string(), "test".to_string()),
2154 Property::Double("2".to_string(), 2.3),
2155 Property::Uint("10".to_string(), 3),
2156 ],
2157 vec![
2158 DiagnosticsHierarchy::new("1", vec![], vec![]),
2159 DiagnosticsHierarchy::new("4", vec![], vec![]),
2160 DiagnosticsHierarchy::new("12", vec![], vec![]),
2161 DiagnosticsHierarchy::new("023", vec![], vec![]),
2162 DiagnosticsHierarchy::new("34", vec![], vec![]),
2163 DiagnosticsHierarchy::new("123", vec![], vec![]),
2164 ]
2165 )
2166 );
2167 }
2168
2169 #[fuchsia::test]
2170 fn filter_hierarchy_doesnt_return_partial_matches() {
2171 let hierarchy = DiagnosticsHierarchy::new(
2172 "root",
2173 vec![],
2174 vec![DiagnosticsHierarchy::new("session_started_at", vec![], vec![])],
2175 );
2176 let test_selectors = vec!["*:root/session_started_at/0"];
2177 assert_eq!(parse_selectors_and_filter_hierarchy(hierarchy, test_selectors), None);
2178 }
2179
2180 #[fuchsia::test]
2181 fn test_filter_tree() {
2182 let test_selectors = vec!["root/foo:11", "root:z", r#"root/bar/zed:13\/\:"#];
2183 let parsed_test_selectors = test_selectors
2184 .into_iter()
2185 .map(|s| {
2186 selectors::parse_tree_selector::<VerboseError>(s)
2187 .expect("All test selectors are valid and parsable.")
2188 })
2189 .collect::<Vec<_>>();
2190
2191 let result =
2192 filter_tree(get_test_hierarchy(), &parsed_test_selectors).map(|mut hierarchy| {
2193 hierarchy.sort();
2194 hierarchy
2195 });
2196 assert_eq!(
2197 result,
2198 Some(DiagnosticsHierarchy::new(
2199 "root",
2200 vec![Property::Int("z".to_string(), -4),],
2201 vec![
2202 DiagnosticsHierarchy::new(
2203 "bar",
2204 vec![],
2205 vec![DiagnosticsHierarchy::new(
2206 "zed",
2207 vec![Property::Int("13/:".to_string(), -4)],
2208 vec![],
2209 )],
2210 ),
2211 DiagnosticsHierarchy::new(
2212 "foo",
2213 vec![Property::Int("11".to_string(), -4),],
2214 vec![],
2215 )
2216 ],
2217 ))
2218 );
2219 }
2220
2221 #[fuchsia::test]
2222 fn test_matcher_from_iterator() {
2223 let matcher = HierarchyMatcher::new(
2224 ["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#].into_iter().map(|s| {
2225 selectors::parse_selector::<VerboseError>(s)
2226 .expect("All test selectors are valid and parsable.")
2227 }),
2228 )
2229 .expect("create matcher from iterator of selectors");
2230 let result = filter_hierarchy(get_test_hierarchy(), &matcher).map(|mut hierarchy| {
2231 hierarchy.sort();
2232 hierarchy
2233 });
2234 assert_eq!(
2235 result,
2236 Some(DiagnosticsHierarchy::new(
2237 "root",
2238 vec![Property::Int("z".to_string(), -4),],
2239 vec![
2240 DiagnosticsHierarchy::new(
2241 "bar",
2242 vec![],
2243 vec![DiagnosticsHierarchy::new(
2244 "zed",
2245 vec![Property::Int("13/:".to_string(), -4)],
2246 vec![],
2247 )],
2248 ),
2249 DiagnosticsHierarchy::new(
2250 "foo",
2251 vec![Property::Int("11".to_string(), -4),],
2252 vec![],
2253 )
2254 ],
2255 ))
2256 );
2257 }
2258}