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: Clone> {
981 Properties(Vec<Cow<'a, Property<Key>>>),
982 Nodes(Vec<Cow<'a, DiagnosticsHierarchy<Key>>>),
983}
984
985impl<'a, Key: Clone> SelectResult<'a, Key> {
986 pub fn into_owned(self) -> SelectResult<'static, Key> {
987 match self {
988 Self::Properties(v) => SelectResult::Properties(
989 v.into_iter().map(|v| Cow::Owned(v.into_owned())).collect(),
990 ),
991 Self::Nodes(v) => {
992 SelectResult::Nodes(v.into_iter().map(|v| Cow::Owned(v.into_owned())).collect())
993 }
994 }
995 }
996
997 fn add_property(&mut self, prop: &'a Property<Key>) {
999 let Self::Properties(v) = self else {
1000 panic!("must be Self::Properties to call add_property");
1001 };
1002 v.push(Cow::Borrowed(prop));
1003 }
1004
1005 fn add_node(&mut self, node: &'a DiagnosticsHierarchy<Key>) {
1007 let Self::Nodes(v) = self else {
1008 panic!("must be Self::Nodes to call add_node");
1009 };
1010 v.push(Cow::Borrowed(node));
1011 }
1012}
1013
1014pub fn select_from_hierarchy<'a, 'b, Key>(
1015 root_node: &'a DiagnosticsHierarchy<Key>,
1016 selector: &'b Selector,
1017) -> Result<SelectResult<'a, Key>, Error>
1018where
1019 Key: AsRef<str> + Clone,
1020 'a: 'b,
1021{
1022 selector.validate()?;
1023
1024 struct StackEntry<'a, Key> {
1025 node: &'a DiagnosticsHierarchy<Key>,
1026 node_path_index: usize,
1027 explored_path: Vec<&'a str>,
1028 }
1029
1030 let (node_path, property_selector, stack_entry) = match selector.tree_selector.as_ref().unwrap()
1032 {
1033 TreeSelector::SubtreeSelector(ref subtree_selector) => (
1034 &subtree_selector.node_path,
1035 None,
1036 StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1037 ),
1038 TreeSelector::PropertySelector(ref property_selector) => (
1039 &property_selector.node_path,
1040 Some(&property_selector.target_properties),
1041 StackEntry { node: root_node, node_path_index: 0, explored_path: vec![] },
1042 ),
1043 _ => return Err(Error::InvalidTreeSelector),
1044 };
1045
1046 let mut stack = vec![stack_entry];
1047 let mut result = if property_selector.is_some() {
1048 SelectResult::Properties(vec![])
1049 } else {
1050 SelectResult::Nodes(vec![])
1051 };
1052
1053 while let Some(StackEntry { node, node_path_index, mut explored_path }) = stack.pop() {
1054 if !selectors::match_string(&node_path[node_path_index], &node.name) {
1056 continue;
1057 }
1058 explored_path.push(&node.name);
1059
1060 if node_path_index != node_path.len() - 1 {
1063 for child in node.children.iter() {
1065 stack.push(StackEntry {
1066 node: child,
1067 node_path_index: node_path_index + 1,
1068 explored_path: explored_path.clone(),
1069 });
1070 }
1071 } else if let Some(s) = property_selector {
1072 for property in &node.properties {
1074 if selectors::match_string(s, property.key()) {
1075 result.add_property(property);
1076 }
1077 }
1078 } else {
1079 result.add_node(node);
1082 }
1083 }
1084
1085 Ok(result)
1086}
1087
1088pub fn filter_tree<'a, Key, I: IntoIterator<Item = &'a TreeSelector>>(
1090 root_node: DiagnosticsHierarchy<Key>,
1091 selectors: I,
1092) -> Option<DiagnosticsHierarchy<Key>>
1093where
1094 Key: AsRef<str>,
1095{
1096 let mut matcher = HierarchyMatcher::default();
1097 for selector in selectors.into_iter() {
1098 match selector {
1099 TreeSelector::SubtreeSelector(subtree_selector) => {
1100 matcher.insert_subtree(subtree_selector.clone());
1101 }
1102 TreeSelector::PropertySelector(property_selector) => {
1103 matcher.insert_property(property_selector.clone());
1104 }
1105 _ => {}
1106 }
1107 }
1108 filter_hierarchy(root_node, &matcher)
1109}
1110
1111pub fn filter_hierarchy<Key>(
1117 mut root_node: DiagnosticsHierarchy<Key>,
1118 hierarchy_matcher: &HierarchyMatcher,
1119) -> Option<DiagnosticsHierarchy<Key>>
1120where
1121 Key: AsRef<str>,
1122{
1123 let starts_empty = root_node.children.is_empty() && root_node.properties.is_empty();
1124 if filter_hierarchy_helper(&mut root_node, &[hierarchy_matcher]) {
1125 if !starts_empty && root_node.children.is_empty() && root_node.properties.is_empty() {
1126 return None;
1127 }
1128 return Some(root_node);
1129 }
1130 None
1131}
1132
1133fn filter_hierarchy_helper<Key>(
1134 node: &mut DiagnosticsHierarchy<Key>,
1135 hierarchy_matchers: &[&HierarchyMatcher],
1136) -> bool
1137where
1138 Key: AsRef<str>,
1139{
1140 let child_matchers = eval_matchers_on_node_name(&node.name, hierarchy_matchers);
1141 if child_matchers.is_empty() {
1142 node.children.clear();
1143 node.properties.clear();
1144 return false;
1145 }
1146
1147 if child_matchers.iter().any(|m| m.subtree) {
1148 return true;
1149 }
1150
1151 node.children.retain_mut(|child| filter_hierarchy_helper(child, &child_matchers));
1152 node.properties.retain_mut(|prop| eval_matchers_on_property(prop.name(), &child_matchers));
1153
1154 !(node.children.is_empty() && node.properties.is_empty())
1155}
1156
1157fn eval_matchers_on_node_name<'a>(
1158 node_name: &'a str,
1159 matchers: &'a [&'a HierarchyMatcher],
1160) -> Vec<&'a HierarchyMatcher> {
1161 let mut result = vec![];
1162 for matcher in matchers {
1163 for (node_pattern, tree_matcher) in matcher.nodes.iter() {
1164 if selectors::match_string(&node_pattern.0, node_name) {
1165 result.push(tree_matcher);
1166 }
1167 }
1168 }
1169 result
1170}
1171
1172fn eval_matchers_on_property(property_name: &str, matchers: &[&HierarchyMatcher]) -> bool {
1173 matchers.iter().any(|matcher| {
1174 matcher
1175 .properties
1176 .iter()
1177 .any(|property_pattern| selectors::match_string(&property_pattern.0, property_name))
1178 })
1179}
1180
1181#[derive(Clone)]
1183pub struct ExponentialHistogramParams<T: Clone> {
1184 pub floor: T,
1186
1187 pub initial_step: T,
1189
1190 pub step_multiplier: T,
1192
1193 pub buckets: usize,
1196}
1197
1198#[derive(Clone)]
1200pub struct LinearHistogramParams<T: Clone> {
1201 pub floor: T,
1203
1204 pub step_size: T,
1206
1207 pub buckets: usize,
1210}
1211
1212pub trait DiagnosticsHierarchyGetter<K: Clone> {
1215 fn get_diagnostics_hierarchy<'a>(
1216 &'a self,
1217 ) -> impl std::future::Future<Output = Cow<'_, DiagnosticsHierarchy<K>>>
1218 where
1219 K: 'a;
1220}
1221
1222impl<K: Clone> DiagnosticsHierarchyGetter<K> for DiagnosticsHierarchy<K> {
1223 async fn get_diagnostics_hierarchy<'a>(&'a self) -> Cow<'_, DiagnosticsHierarchy<K>>
1224 where
1225 K: 'a,
1226 {
1227 Cow::Borrowed(self)
1228 }
1229}
1230
1231#[cfg(test)]
1232mod tests {
1233 use super::*;
1234 use crate::testing::CondensableOnDemand;
1235 use test_case::test_case;
1236
1237 use assert_matches::assert_matches;
1238 use selectors::VerboseError;
1239 use std::sync::Arc;
1240
1241 fn validate_hierarchy_iteration(
1242 mut results_vec: Vec<(Vec<String>, Option<Property>)>,
1243 test_hierarchy: DiagnosticsHierarchy,
1244 ) {
1245 let expected_num_entries = results_vec.len();
1246 let mut num_entries = 0;
1247 for (key, val) in test_hierarchy.property_iter() {
1248 num_entries += 1;
1249 let (expected_key, expected_property) = results_vec.pop().unwrap();
1250 assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1251 assert_eq!(val, expected_property.as_ref());
1252 }
1253
1254 assert_eq!(num_entries, expected_num_entries);
1255 }
1256
1257 fn validate_hierarchy_error_iteration(
1258 mut results_vec: Vec<(Vec<String>, Option<MissingValue>)>,
1259 test_hierarchy: DiagnosticsHierarchy,
1260 ) {
1261 let expected_num_entries = results_vec.len();
1262 let mut num_entries = 0;
1263 for (key, reason) in test_hierarchy.error_iter() {
1264 num_entries += 1;
1265 let (expected_key, expected_reason) = results_vec.pop().unwrap();
1266 assert_eq!(reason, expected_reason.as_ref());
1267 assert_eq!(key.to_vec().join("/"), expected_key.to_vec().join("/"));
1268 }
1269
1270 assert_eq!(num_entries, expected_num_entries);
1271 }
1272
1273 #[fuchsia::test]
1274 fn test_diagnostics_hierarchy_property_iteration() {
1275 let double_array_data = vec![-1.2, 2.3, 3.4, 4.5, -5.6];
1276 let chars = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
1277 let string_data = chars.iter().cycle().take(6000).collect::<String>();
1278 let bytes_data = (0u8..=9u8).cycle().take(5000).collect::<Vec<u8>>();
1279
1280 let test_hierarchy = DiagnosticsHierarchy::new(
1281 "root".to_string(),
1282 vec![
1283 Property::Int("int-root".to_string(), 3),
1284 Property::DoubleArray(
1285 "property-double-array".to_string(),
1286 ArrayContent::Values(double_array_data.clone()),
1287 ),
1288 ],
1289 vec![DiagnosticsHierarchy::new(
1290 "child-1".to_string(),
1291 vec![
1292 Property::Uint("property-uint".to_string(), 10),
1293 Property::Double("property-double".to_string(), -3.4),
1294 Property::String("property-string".to_string(), string_data.clone()),
1295 Property::IntArray(
1296 "property-int-array".to_string(),
1297 ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1298 .unwrap(),
1299 ),
1300 ],
1301 vec![DiagnosticsHierarchy::new(
1302 "child-1-1".to_string(),
1303 vec![
1304 Property::Int("property-int".to_string(), -9),
1305 Property::Bytes("property-bytes".to_string(), bytes_data.clone()),
1306 Property::UintArray(
1307 "property-uint-array".to_string(),
1308 ArrayContent::new(
1309 vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1310 ArrayFormat::ExponentialHistogram,
1311 )
1312 .unwrap(),
1313 ),
1314 ],
1315 vec![],
1316 )],
1317 )],
1318 );
1319
1320 let results_vec = vec![
1321 (
1322 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1323 Some(Property::UintArray(
1324 "property-uint-array".to_string(),
1325 ArrayContent::new(
1326 vec![1, 1, 2, 0, 1, 1, 2, 0, 0],
1327 ArrayFormat::ExponentialHistogram,
1328 )
1329 .unwrap(),
1330 )),
1331 ),
1332 (
1333 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1334 Some(Property::Bytes("property-bytes".to_string(), bytes_data)),
1335 ),
1336 (
1337 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1338 Some(Property::Int("property-int".to_string(), -9)),
1339 ),
1340 (
1341 vec!["root".to_string(), "child-1".to_string()],
1342 Some(Property::IntArray(
1343 "property-int-array".to_string(),
1344 ArrayContent::new(vec![1, 2, 1, 1, 1, 1, 1], ArrayFormat::LinearHistogram)
1345 .unwrap(),
1346 )),
1347 ),
1348 (
1349 vec!["root".to_string(), "child-1".to_string()],
1350 Some(Property::String("property-string".to_string(), string_data)),
1351 ),
1352 (
1353 vec!["root".to_string(), "child-1".to_string()],
1354 Some(Property::Double("property-double".to_string(), -3.4)),
1355 ),
1356 (
1357 vec!["root".to_string(), "child-1".to_string()],
1358 Some(Property::Uint("property-uint".to_string(), 10)),
1359 ),
1360 (
1361 vec!["root".to_string()],
1362 Some(Property::DoubleArray(
1363 "property-double-array".to_string(),
1364 ArrayContent::Values(double_array_data),
1365 )),
1366 ),
1367 (vec!["root".to_string()], Some(Property::Int("int-root".to_string(), 3))),
1368 ];
1369
1370 validate_hierarchy_iteration(results_vec, test_hierarchy);
1371 }
1372
1373 #[fuchsia::test]
1374 fn test_diagnostics_hierarchy_error_iteration() {
1375 let mut test_hierarchy = DiagnosticsHierarchy::new(
1376 "root".to_string(),
1377 vec![],
1378 vec![
1379 DiagnosticsHierarchy::new(
1380 "child-1".to_string(),
1381 vec![],
1382 vec![DiagnosticsHierarchy::new("child-1-1".to_string(), vec![], vec![])],
1383 ),
1384 DiagnosticsHierarchy::new("child-2".to_string(), vec![], vec![]),
1385 ],
1386 );
1387
1388 test_hierarchy.add_missing(MissingValueReason::LinkInvalid, "root".to_string());
1389 test_hierarchy.children[0]
1390 .add_missing(MissingValueReason::LinkNeverExpanded, "child-1".to_string());
1391 test_hierarchy.children[0].children[0]
1392 .add_missing(MissingValueReason::Timeout, "child-1-1".to_string());
1393
1394 let results_vec = vec![
1395 (
1396 vec!["root".to_string(), "child-1".to_string(), "child-1-1".to_string()],
1397 Some(MissingValue {
1398 reason: MissingValueReason::Timeout,
1399 name: "child-1-1".to_string(),
1400 }),
1401 ),
1402 (
1403 vec!["root".to_string(), "child-1".to_string()],
1404 Some(MissingValue {
1405 reason: MissingValueReason::LinkNeverExpanded,
1406 name: "child-1".to_string(),
1407 }),
1408 ),
1409 (vec!["root".to_string(), "child-2".to_string()], None),
1410 (
1411 vec!["root".to_string()],
1412 Some(MissingValue {
1413 reason: MissingValueReason::LinkInvalid,
1414 name: "root".to_string(),
1415 }),
1416 ),
1417 ];
1418
1419 validate_hierarchy_error_iteration(results_vec, test_hierarchy);
1420 }
1421
1422 #[fuchsia::test]
1423 fn test_getters() {
1424 let a_prop = Property::Int("a".to_string(), 1);
1425 let b_prop = Property::Uint("b".to_string(), 2);
1426 let child2 = DiagnosticsHierarchy::new("child2".to_string(), vec![], vec![]);
1427 let child = DiagnosticsHierarchy::new(
1428 "child".to_string(),
1429 vec![b_prop.clone()],
1430 vec![child2.clone()],
1431 );
1432 let mut hierarchy = DiagnosticsHierarchy::new(
1433 "root".to_string(),
1434 vec![a_prop.clone()],
1435 vec![child.clone()],
1436 );
1437 assert_matches!(hierarchy.get_child("child"), Some(node) if *node == child);
1438 assert_matches!(hierarchy.get_child_mut("child"), Some(node) if *node == child);
1439 assert_matches!(hierarchy.get_child_by_path(&["child", "child2"]),
1440 Some(node) if *node == child2);
1441 assert_matches!(hierarchy.get_child_by_path_mut(&["child", "child2"]),
1442 Some(node) if *node == child2);
1443 assert_matches!(hierarchy.get_property("a"), Some(prop) if *prop == a_prop);
1444 assert_matches!(hierarchy.get_property_by_path(&["child", "b"]),
1445 Some(prop) if *prop == b_prop);
1446 }
1447
1448 #[fuchsia::test]
1449 fn test_edge_case_hierarchy_iteration() {
1450 let root_only_with_one_property_hierarchy = DiagnosticsHierarchy::new(
1451 "root".to_string(),
1452 vec![Property::Int("property-int".to_string(), -9)],
1453 vec![],
1454 );
1455
1456 let results_vec =
1457 vec![(vec!["root".to_string()], Some(Property::Int("property-int".to_string(), -9)))];
1458
1459 validate_hierarchy_iteration(results_vec, root_only_with_one_property_hierarchy);
1460
1461 let empty_hierarchy = DiagnosticsHierarchy::new("root".to_string(), vec![], vec![]);
1462
1463 let results_vec = vec![(vec!["root".to_string()], None)];
1464
1465 validate_hierarchy_iteration(results_vec, empty_hierarchy);
1466
1467 let empty_root_populated_child = DiagnosticsHierarchy::new(
1468 "root",
1469 vec![],
1470 vec![DiagnosticsHierarchy::new(
1471 "foo",
1472 vec![Property::Int("11".to_string(), -4)],
1473 vec![],
1474 )],
1475 );
1476
1477 let results_vec = vec![
1478 (
1479 vec!["root".to_string(), "foo".to_string()],
1480 Some(Property::Int("11".to_string(), -4)),
1481 ),
1482 (vec!["root".to_string()], None),
1483 ];
1484
1485 validate_hierarchy_iteration(results_vec, empty_root_populated_child);
1486
1487 let empty_root_empty_child = DiagnosticsHierarchy::new(
1488 "root",
1489 vec![],
1490 vec![DiagnosticsHierarchy::new("foo", vec![], vec![])],
1491 );
1492
1493 let results_vec = vec![
1494 (vec!["root".to_string(), "foo".to_string()], None),
1495 (vec!["root".to_string()], None),
1496 ];
1497
1498 validate_hierarchy_iteration(results_vec, empty_root_empty_child);
1499 }
1500
1501 #[fuchsia::test]
1502 fn array_value() {
1503 let values = vec![1, 2, 5, 7, 9, 11, 13];
1504 let array = ArrayContent::<u64>::new(values.clone(), ArrayFormat::Default);
1505 assert_matches!(array, Ok(ArrayContent::Values(vals)) if vals == values);
1506 }
1507
1508 #[fuchsia::test]
1509 fn linear_histogram_array_value() {
1510 let values = vec![1, 2, 5, 7, 9, 11, 13];
1511 let array = ArrayContent::<i64>::new(values, ArrayFormat::LinearHistogram);
1512 assert_matches!(array, Ok(ArrayContent::LinearHistogram(hist))
1513 if hist == LinearHistogram {
1514 floor: 1,
1515 step: 2,
1516 counts: vec![5, 7, 9, 11, 13],
1517 indexes: None,
1518 size: 5,
1519 }
1520 );
1521 }
1522
1523 #[fuchsia::test]
1524 fn exponential_histogram_array_value() {
1525 let values = vec![1.0, 2.0, 5.0, 7.0, 9.0, 11.0, 15.0];
1526 let array = ArrayContent::<f64>::new(values, ArrayFormat::ExponentialHistogram);
1527 assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1528 if hist == ExponentialHistogram {
1529 floor: 1.0,
1530 initial_step: 2.0,
1531 step_multiplier: 5.0,
1532 counts: vec![7.0, 9.0, 11.0, 15.0],
1533 indexes: None,
1534 size: 4,
1535 }
1536 );
1537 }
1538
1539 #[fuchsia::test]
1540 fn deserialize_linear_int_histogram() -> Result<(), serde_json::Error> {
1541 let json = r#"{
1542 "root": {
1543 "histogram": {
1544 "floor": -2,
1545 "step": 3,
1546 "counts": [4, 5, 6],
1547 "size": 3
1548 }
1549 }
1550 }"#;
1551 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1552 let expected = DiagnosticsHierarchy::new(
1553 "root".to_string(),
1554 vec![Property::IntArray(
1555 "histogram".to_string(),
1556 ArrayContent::new(vec![-2, 3, 4, 5, 6], ArrayFormat::LinearHistogram).unwrap(),
1557 )],
1558 vec![],
1559 );
1560 assert_eq!(parsed, expected);
1561 Ok(())
1562 }
1563
1564 #[fuchsia::test]
1565 fn deserialize_exponential_int_histogram() -> Result<(), serde_json::Error> {
1566 let json = r#"{
1567 "root": {
1568 "histogram": {
1569 "floor": 1,
1570 "initial_step": 3,
1571 "step_multiplier": 2,
1572 "counts": [4, 5, 6],
1573 "size": 3
1574 }
1575 }
1576 }"#;
1577 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1578 let expected = DiagnosticsHierarchy::new(
1579 "root".to_string(),
1580 vec![Property::IntArray(
1581 "histogram".to_string(),
1582 ArrayContent::new(vec![1, 3, 2, 4, 5, 6], ArrayFormat::ExponentialHistogram)
1583 .unwrap(),
1584 )],
1585 vec![],
1586 );
1587 assert_eq!(parsed, expected);
1588 Ok(())
1589 }
1590
1591 #[fuchsia::test]
1592 fn deserialize_linear_uint_histogram() -> Result<(), serde_json::Error> {
1593 let json = r#"{
1594 "root": {
1595 "histogram": {
1596 "floor": 2,
1597 "step": 3,
1598 "counts": [4, 9223372036854775808, 6],
1599 "size": 3
1600 }
1601 }
1602 }"#;
1603 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1604 let expected = DiagnosticsHierarchy::new(
1605 "root".to_string(),
1606 vec![Property::UintArray(
1607 "histogram".to_string(),
1608 ArrayContent::new(
1609 vec![2, 3, 4, 9_223_372_036_854_775_808, 6],
1610 ArrayFormat::LinearHistogram,
1611 )
1612 .unwrap(),
1613 )],
1614 vec![],
1615 );
1616 assert_eq!(parsed, expected);
1617 Ok(())
1618 }
1619
1620 #[fuchsia::test]
1621 fn deserialize_linear_double_histogram() -> Result<(), serde_json::Error> {
1622 let json = r#"{
1623 "root": {
1624 "histogram": {
1625 "floor": 2.0,
1626 "step": 3.0,
1627 "counts": [4.0, 5.0, 6.0],
1628 "size": 3
1629 }
1630 }
1631 }"#;
1632 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1633 let expected = DiagnosticsHierarchy::new(
1634 "root".to_string(),
1635 vec![Property::DoubleArray(
1636 "histogram".to_string(),
1637 ArrayContent::new(vec![2.0, 3.0, 4.0, 5.0, 6.0], ArrayFormat::LinearHistogram)
1638 .unwrap(),
1639 )],
1640 vec![],
1641 );
1642 assert_eq!(parsed, expected);
1643 Ok(())
1644 }
1645
1646 #[fuchsia::test]
1647 fn deserialize_sparse_histogram() -> Result<(), serde_json::Error> {
1648 let json = r#"{
1649 "root": {
1650 "histogram": {
1651 "floor": 2,
1652 "step": 3,
1653 "counts": [4, 5, 6],
1654 "indexes": [1, 2, 4],
1655 "size": 8
1656 }
1657 }
1658 }"#;
1659 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1660
1661 let mut histogram =
1662 ArrayContent::new(vec![2, 3, 0, 4, 5, 0, 6, 0, 0, 0], ArrayFormat::LinearHistogram)
1663 .unwrap();
1664 histogram.condense_histogram();
1665 let expected = DiagnosticsHierarchy::new(
1666 "root".to_string(),
1667 vec![Property::IntArray("histogram".to_string(), histogram)],
1668 vec![],
1669 );
1670 assert_eq!(parsed, expected);
1671 Ok(())
1672 }
1673
1674 #[fuchsia::test]
1679 fn reject_histogram_incompatible_values() -> Result<(), serde_json::Error> {
1680 let json = r#"{
1681 "root": {
1682 "histogram": {
1683 "floor": -2,
1684 "step": 3,
1685 "counts": [4, 9223372036854775808, 6],
1686 "size": 3
1687 }
1688 }
1689 }"#;
1690 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1691 assert_eq!(parsed.children.len(), 1);
1692 assert_eq!(&parsed.children[0].name, "histogram");
1693 Ok(())
1694 }
1695
1696 #[fuchsia::test]
1697 fn reject_histogram_bad_sparse_list() -> Result<(), serde_json::Error> {
1698 let json = r#"{
1699 "root": {
1700 "histogram": {
1701 "floor": -2,
1702 "step": 3,
1703 "counts": [4, 5, 6],
1704 "indexes": [0, 1, 2, 3],
1705 "size": 8
1706 }
1707 }
1708 }"#;
1709 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1710 assert_eq!(parsed.children.len(), 1);
1711 assert_eq!(&parsed.children[0].name, "histogram");
1712 Ok(())
1713 }
1714
1715 #[fuchsia::test]
1716 fn reject_histogram_bad_index() -> Result<(), serde_json::Error> {
1717 let json = r#"{
1718 "root": {
1719 "histogram": {
1720 "floor": -2,
1721 "step": 3,
1722 "counts": [4, 5, 6],
1723 "indexes": [0, 1, 4],
1724 "size": 4
1725 }
1726 }
1727 }"#;
1728 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1729 assert_eq!(parsed.children.len(), 1);
1730 assert_eq!(&parsed.children[0].name, "histogram");
1731 Ok(())
1732 }
1733
1734 #[fuchsia::test]
1735 fn reject_histogram_wrong_field() -> Result<(), serde_json::Error> {
1736 let json = r#"{
1737 "root": {
1738 "histogram": {
1739 "floor": 2,
1740 "step": 3,
1741 "counts": [4, 5, 6],
1742 "incorrect": [0, 1, 3],
1743 "size": 4
1744 }
1745 }
1746 }"#;
1747 let parsed: DiagnosticsHierarchy = serde_json::from_str(json)?;
1748 assert_eq!(parsed.children.len(), 1);
1749 assert_eq!(&parsed.children[0].name, "histogram");
1750 Ok(())
1751 }
1752
1753 #[fuchsia::test]
1754 fn exponential_histogram() {
1755 let values = vec![0, 2, 4, 0, 1, 2, 3, 4, 5];
1756 let array = ArrayContent::new(values, ArrayFormat::ExponentialHistogram);
1757 assert_matches!(array, Ok(ArrayContent::ExponentialHistogram(hist))
1758 if hist == ExponentialHistogram {
1759 floor: 0,
1760 initial_step: 2,
1761 step_multiplier: 4,
1762 counts: vec![0, 1, 2, 3, 4, 5],
1763 indexes: None,
1764 size: 6,
1765 }
1766 );
1767 }
1768
1769 #[fuchsia::test]
1770 fn add_to_hierarchy() {
1771 let mut hierarchy = DiagnosticsHierarchy::new_root();
1772 let prop_1 = Property::String("x".to_string(), "foo".to_string());
1773 let path_1 = vec!["root", "one"];
1774 let prop_2 = Property::Uint("c".to_string(), 3);
1775 let path_2 = vec!["root", "two"];
1776 let prop_2_prime = Property::Int("z".to_string(), -4);
1777 hierarchy.add_property_at_path(&path_1, prop_1.clone());
1778 hierarchy.add_property_at_path(&path_2.clone(), prop_2.clone());
1779 hierarchy.add_property_at_path(&path_2, prop_2_prime.clone());
1780
1781 assert_eq!(
1782 hierarchy,
1783 DiagnosticsHierarchy {
1784 name: "root".to_string(),
1785 children: vec![
1786 DiagnosticsHierarchy {
1787 name: "one".to_string(),
1788 properties: vec![prop_1],
1789 children: vec![],
1790 missing: vec![],
1791 },
1792 DiagnosticsHierarchy {
1793 name: "two".to_string(),
1794 properties: vec![prop_2, prop_2_prime],
1795 children: vec![],
1796 missing: vec![],
1797 }
1798 ],
1799 properties: vec![],
1800 missing: vec![],
1801 }
1802 );
1803 }
1804
1805 #[fuchsia::test]
1806 fn string_lists() {
1807 let mut hierarchy = DiagnosticsHierarchy::new_root();
1808 let prop_1 =
1809 Property::StringList("x".to_string(), vec!["foo".to_string(), "bar".to_string()]);
1810 let path_1 = vec!["root", "one"];
1811 hierarchy.add_property_at_path(&path_1, prop_1.clone());
1812
1813 assert_eq!(
1814 hierarchy,
1815 DiagnosticsHierarchy {
1816 name: "root".to_string(),
1817 children: vec![DiagnosticsHierarchy {
1818 name: "one".to_string(),
1819 properties: vec![prop_1],
1820 children: vec![],
1821 missing: vec![],
1822 },],
1823 properties: vec![],
1824 missing: vec![],
1825 }
1826 );
1827 }
1828
1829 #[fuchsia::test]
1830 #[cfg_attr(feature = "variant_asan", ignore)]
1832 #[cfg_attr(feature = "variant_hwasan", ignore)]
1833 #[should_panic]
1834 fn no_empty_paths_allowed() {
1836 let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1837 let path_1: Vec<&String> = vec![];
1838 hierarchy.get_or_add_node(&path_1);
1839 }
1840
1841 #[fuchsia::test]
1842 #[should_panic]
1843 fn path_must_start_at_self() {
1846 let mut hierarchy = DiagnosticsHierarchy::<String>::new_root();
1847 let path_1 = vec!["not_root", "a"];
1848 hierarchy.get_or_add_node(&path_1);
1849 }
1850
1851 #[fuchsia::test]
1852 fn sort_hierarchy() {
1853 let mut hierarchy = DiagnosticsHierarchy::new(
1854 "root",
1855 vec![
1856 Property::String("x".to_string(), "foo".to_string()),
1857 Property::Uint("c".to_string(), 3),
1858 Property::Int("z".to_string(), -4),
1859 ],
1860 vec![
1861 DiagnosticsHierarchy::new(
1862 "foo",
1863 vec![
1864 Property::Int("11".to_string(), -4),
1865 Property::Bytes("123".to_string(), "foo".bytes().collect()),
1866 Property::Double("0".to_string(), 8.1),
1867 ],
1868 vec![],
1869 ),
1870 DiagnosticsHierarchy::new("bar", vec![], vec![]),
1871 ],
1872 );
1873
1874 hierarchy.sort();
1875
1876 let sorted_hierarchy = DiagnosticsHierarchy::new(
1877 "root",
1878 vec![
1879 Property::Uint("c".to_string(), 3),
1880 Property::String("x".to_string(), "foo".to_string()),
1881 Property::Int("z".to_string(), -4),
1882 ],
1883 vec![
1884 DiagnosticsHierarchy::new("bar", vec![], vec![]),
1885 DiagnosticsHierarchy::new(
1886 "foo",
1887 vec![
1888 Property::Double("0".to_string(), 8.1),
1889 Property::Int("11".to_string(), -4),
1890 Property::Bytes("123".to_string(), "foo".bytes().collect()),
1891 ],
1892 vec![],
1893 ),
1894 ],
1895 );
1896 assert_eq!(sorted_hierarchy, hierarchy);
1897 }
1898
1899 fn parse_selectors_and_filter_hierarchy(
1900 hierarchy: DiagnosticsHierarchy,
1901 test_selectors: Vec<&str>,
1902 ) -> Option<DiagnosticsHierarchy> {
1903 let parsed_test_selectors = test_selectors
1904 .into_iter()
1905 .map(|selector_string| {
1906 Arc::new(
1907 selectors::parse_selector::<VerboseError>(selector_string)
1908 .expect("All test selectors are valid and parsable."),
1909 )
1910 })
1911 .collect::<Vec<Arc<Selector>>>();
1912
1913 let hierarchy_matcher: HierarchyMatcher = parsed_test_selectors.try_into().unwrap();
1914
1915 filter_hierarchy(hierarchy, &hierarchy_matcher).map(|mut hierarchy| {
1916 hierarchy.sort();
1917 hierarchy
1918 })
1919 }
1920
1921 fn get_test_hierarchy() -> DiagnosticsHierarchy {
1922 DiagnosticsHierarchy::new(
1923 "root",
1924 vec![
1925 Property::String("x".to_string(), "foo".to_string()),
1926 Property::Uint("c".to_string(), 3),
1927 Property::Int("z".to_string(), -4),
1928 ],
1929 vec![
1930 make_foo(),
1931 DiagnosticsHierarchy::new(
1932 "bar",
1933 vec![Property::Int("12".to_string(), -4)],
1934 vec![DiagnosticsHierarchy::new(
1935 "zed",
1936 vec![Property::Int("13/:".to_string(), -4)],
1937 vec![],
1938 )],
1939 ),
1940 ],
1941 )
1942 }
1943
1944 fn make_all_foo_props() -> Vec<Property> {
1945 vec![
1946 Property::Int("11".to_string(), -4),
1947 Property::Bytes("123".to_string(), b"foo".to_vec()),
1948 Property::Double("0".to_string(), 8.1),
1949 ]
1950 }
1951
1952 fn make_zed() -> Vec<DiagnosticsHierarchy> {
1953 vec![DiagnosticsHierarchy::new("zed", vec![Property::Int("13".to_string(), -4)], vec![])]
1954 }
1955
1956 fn make_foo() -> DiagnosticsHierarchy {
1957 DiagnosticsHierarchy::new("foo", make_all_foo_props(), make_zed())
1958 }
1959
1960 #[fuchsia::test]
1961 fn test_filter_hierarchy() {
1962 let test_selectors = vec!["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#];
1963
1964 assert_eq!(
1965 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1966 Some(DiagnosticsHierarchy::new(
1967 "root",
1968 vec![Property::Int("z".to_string(), -4),],
1969 vec![
1970 DiagnosticsHierarchy::new(
1971 "bar",
1972 vec![],
1973 vec![DiagnosticsHierarchy::new(
1974 "zed",
1975 vec![Property::Int("13/:".to_string(), -4)],
1976 vec![],
1977 )],
1978 ),
1979 DiagnosticsHierarchy::new(
1980 "foo",
1981 vec![Property::Int("11".to_string(), -4),],
1982 vec![],
1983 )
1984 ],
1985 ))
1986 );
1987
1988 let test_selectors = vec!["*:root"];
1989 let mut sorted_expected = get_test_hierarchy();
1990 sorted_expected.sort();
1991 assert_eq!(
1992 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
1993 Some(sorted_expected)
1994 );
1995 }
1996
1997 #[fuchsia::test]
1998 fn test_filter_does_not_include_empty_node() {
1999 let test_selectors = vec!["*:root/foo:blorg"];
2000
2001 assert_eq!(
2002 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2003 None,
2004 );
2005 }
2006
2007 #[fuchsia::test]
2008 fn test_filter_empty_hierarchy() {
2009 let test_selectors = vec!["*:root"];
2010
2011 assert_eq!(
2012 parse_selectors_and_filter_hierarchy(
2013 DiagnosticsHierarchy::new("root", vec![], vec![]),
2014 test_selectors
2015 ),
2016 Some(DiagnosticsHierarchy::new("root", vec![], vec![])),
2017 );
2018 }
2019
2020 #[fuchsia::test]
2021 fn test_full_filtering() {
2022 let test_selectors = vec!["*:non-existent-root"];
2024 assert_eq!(
2025 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2026 None,
2027 );
2028
2029 let test_selectors = vec!["*:root/i-dont-exist:foo"];
2031 assert_eq!(
2032 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2033 None,
2034 );
2035
2036 let test_selectors = vec!["*:root:i-dont-exist"];
2039 assert_eq!(
2040 parse_selectors_and_filter_hierarchy(get_test_hierarchy(), test_selectors),
2041 None,
2042 );
2043 }
2044
2045 #[fuchsia::test]
2046 fn test_subtree_selection_includes_empty_nodes() {
2047 let test_selectors = vec!["*:root"];
2048 let mut empty_hierarchy = DiagnosticsHierarchy::new(
2049 "root",
2050 vec![],
2051 vec![
2052 DiagnosticsHierarchy::new(
2053 "foo",
2054 vec![],
2055 vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2056 ),
2057 DiagnosticsHierarchy::new(
2058 "bar",
2059 vec![],
2060 vec![DiagnosticsHierarchy::new("zed", vec![], vec![])],
2061 ),
2062 ],
2063 );
2064
2065 empty_hierarchy.sort();
2066
2067 assert_eq!(
2068 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), test_selectors),
2069 Some(empty_hierarchy)
2070 );
2071 }
2072
2073 #[fuchsia::test]
2074 fn test_empty_tree_filtering() {
2075 let mut empty_hierarchy = DiagnosticsHierarchy::new("root", vec![], vec![]);
2077 empty_hierarchy.sort();
2078
2079 let subtree_selector = vec!["*:root"];
2080 assert_eq!(
2081 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), subtree_selector),
2082 Some(empty_hierarchy.clone())
2083 );
2084
2085 let fake_property_selector = vec!["*:root:blorp"];
2087 assert_eq!(
2088 parse_selectors_and_filter_hierarchy(empty_hierarchy.clone(), fake_property_selector),
2089 None,
2090 );
2091 }
2092
2093 #[test_case(vec![Property::Int("11".to_string(), -4)], "root/foo:11" ; "specific_property")]
2094 #[test_case(make_all_foo_props(), "root/foo:*" ; "many_properties")]
2095 #[test_case(vec![], "root/foo:none" ; "property_not_there")]
2096 #[fuchsia::test]
2097 fn test_select_from_hierarchy_property_selectors(expected: Vec<Property>, tree_selector: &str) {
2098 let hierarchy = get_test_hierarchy();
2099 let parsed_selector =
2100 selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2101 .expect("All test selectors are valid and parsable.");
2102 let Ok(SelectResult::Properties(mut property_entry_vec)) =
2103 select_from_hierarchy(&hierarchy, &parsed_selector)
2104 else {
2105 panic!("must be properties");
2106 };
2107
2108 property_entry_vec.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2109 let mut expected = expected.iter().map(Cow::Borrowed).collect::<Vec<_>>();
2110 expected.sort_by(|p1, p2| p1.name().cmp(p2.name()));
2111
2112 assert_eq!(property_entry_vec, expected);
2113 }
2114
2115 #[test_case(vec![], "root/none" ; "node_not_there")]
2116 #[test_case(make_zed(), "root/foo/zed" ; "properties_only")]
2117 #[test_case(vec![make_foo()], "root/foo" ; "nodes_and_properties")]
2118 #[test_case(vec![get_test_hierarchy()], "root" ; "select_root")]
2119 #[fuchsia::test]
2120 fn test_select_from_hierarchy_tree_selectors(
2121 expected: Vec<DiagnosticsHierarchy>,
2122 tree_selector: &str,
2123 ) {
2124 let hierarchy = get_test_hierarchy();
2125 let parsed_selector =
2126 selectors::parse_selector::<VerboseError>(&format!("*:{tree_selector}"))
2127 .expect("All test selectors are valid and parsable.");
2128 let Ok(SelectResult::Nodes(node_vec)) = select_from_hierarchy(&hierarchy, &parsed_selector)
2129 else {
2130 panic!("must be nodes");
2131 };
2132
2133 let expected = expected.iter().map(Cow::Borrowed).collect::<Vec<_>>();
2134
2135 assert_eq!(node_vec, expected);
2136 }
2137
2138 #[fuchsia::test]
2139 fn sort_numerical_value() {
2140 let mut diagnostics_hierarchy = DiagnosticsHierarchy::new(
2141 "root",
2142 vec![
2143 Property::Double("2".to_string(), 2.3),
2144 Property::Int("0".to_string(), -4),
2145 Property::Uint("10".to_string(), 3),
2146 Property::String("1".to_string(), "test".to_string()),
2147 ],
2148 vec![
2149 DiagnosticsHierarchy::new("123", vec![], vec![]),
2150 DiagnosticsHierarchy::new("34", vec![], vec![]),
2151 DiagnosticsHierarchy::new("4", vec![], vec![]),
2152 DiagnosticsHierarchy::new("023", vec![], vec![]),
2153 DiagnosticsHierarchy::new("12", vec![], vec![]),
2154 DiagnosticsHierarchy::new("1", vec![], vec![]),
2155 ],
2156 );
2157 diagnostics_hierarchy.sort();
2158 assert_eq!(
2159 diagnostics_hierarchy,
2160 DiagnosticsHierarchy::new(
2161 "root",
2162 vec![
2163 Property::Int("0".to_string(), -4),
2164 Property::String("1".to_string(), "test".to_string()),
2165 Property::Double("2".to_string(), 2.3),
2166 Property::Uint("10".to_string(), 3),
2167 ],
2168 vec![
2169 DiagnosticsHierarchy::new("1", vec![], vec![]),
2170 DiagnosticsHierarchy::new("4", vec![], vec![]),
2171 DiagnosticsHierarchy::new("12", vec![], vec![]),
2172 DiagnosticsHierarchy::new("023", vec![], vec![]),
2173 DiagnosticsHierarchy::new("34", vec![], vec![]),
2174 DiagnosticsHierarchy::new("123", vec![], vec![]),
2175 ]
2176 )
2177 );
2178 }
2179
2180 #[fuchsia::test]
2181 fn filter_hierarchy_doesnt_return_partial_matches() {
2182 let hierarchy = DiagnosticsHierarchy::new(
2183 "root",
2184 vec![],
2185 vec![DiagnosticsHierarchy::new("session_started_at", vec![], vec![])],
2186 );
2187 let test_selectors = vec!["*:root/session_started_at/0"];
2188 assert_eq!(parse_selectors_and_filter_hierarchy(hierarchy, test_selectors), None);
2189 }
2190
2191 #[fuchsia::test]
2192 fn test_filter_tree() {
2193 let test_selectors = vec!["root/foo:11", "root:z", r#"root/bar/zed:13\/\:"#];
2194 let parsed_test_selectors = test_selectors
2195 .into_iter()
2196 .map(|s| {
2197 selectors::parse_tree_selector::<VerboseError>(s)
2198 .expect("All test selectors are valid and parsable.")
2199 })
2200 .collect::<Vec<_>>();
2201
2202 let result =
2203 filter_tree(get_test_hierarchy(), &parsed_test_selectors).map(|mut hierarchy| {
2204 hierarchy.sort();
2205 hierarchy
2206 });
2207 assert_eq!(
2208 result,
2209 Some(DiagnosticsHierarchy::new(
2210 "root",
2211 vec![Property::Int("z".to_string(), -4),],
2212 vec![
2213 DiagnosticsHierarchy::new(
2214 "bar",
2215 vec![],
2216 vec![DiagnosticsHierarchy::new(
2217 "zed",
2218 vec![Property::Int("13/:".to_string(), -4)],
2219 vec![],
2220 )],
2221 ),
2222 DiagnosticsHierarchy::new(
2223 "foo",
2224 vec![Property::Int("11".to_string(), -4),],
2225 vec![],
2226 )
2227 ],
2228 ))
2229 );
2230 }
2231
2232 #[fuchsia::test]
2233 fn test_matcher_from_iterator() {
2234 let matcher = HierarchyMatcher::new(
2235 ["*:root/foo:11", "*:root:z", r#"*:root/bar/zed:13\/\:"#].into_iter().map(|s| {
2236 selectors::parse_selector::<VerboseError>(s)
2237 .expect("All test selectors are valid and parsable.")
2238 }),
2239 )
2240 .expect("create matcher from iterator of selectors");
2241 let result = filter_hierarchy(get_test_hierarchy(), &matcher).map(|mut hierarchy| {
2242 hierarchy.sort();
2243 hierarchy
2244 });
2245 assert_eq!(
2246 result,
2247 Some(DiagnosticsHierarchy::new(
2248 "root",
2249 vec![Property::Int("z".to_string(), -4),],
2250 vec![
2251 DiagnosticsHierarchy::new(
2252 "bar",
2253 vec![],
2254 vec![DiagnosticsHierarchy::new(
2255 "zed",
2256 vec![Property::Int("13/:".to_string(), -4)],
2257 vec![],
2258 )],
2259 ),
2260 DiagnosticsHierarchy::new(
2261 "foo",
2262 vec![Property::Int("11".to_string(), -4),],
2263 vec![],
2264 )
2265 ],
2266 ))
2267 );
2268 }
2269}