fuchsia_inspect/writer/types/
uint_linear_histogram.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::writer::{
6    ArithmeticArrayProperty, ArrayProperty, HistogramProperty, InspectType, Node, UintArrayProperty,
7};
8use diagnostics_hierarchy::{ArrayFormat, LinearHistogramParams};
9use log::error;
10use std::borrow::Cow;
11
12#[derive(Debug, Default)]
13/// A linear histogram property for unsigned integer values.
14pub struct UintLinearHistogramProperty {
15    array: UintArrayProperty,
16    floor: u64,
17    buckets: usize,
18    step_size: u64,
19}
20
21impl InspectType for UintLinearHistogramProperty {}
22
23impl UintLinearHistogramProperty {
24    pub(crate) fn new(
25        name: Cow<'_, str>,
26        params: LinearHistogramParams<u64>,
27        parent: &Node,
28    ) -> Self {
29        let slots = params.buckets + ArrayFormat::LinearHistogram.extra_slots();
30        let array = parent.create_uint_array_internal(name, slots, ArrayFormat::LinearHistogram);
31        array.set(0, params.floor);
32        array.set(1, params.step_size);
33        Self { floor: params.floor, step_size: params.step_size, buckets: params.buckets, array }
34    }
35
36    fn get_index(&self, value: u64) -> usize {
37        let mut bucket_end = self.floor; // The exclusive end of a bucket's range.
38        let mut index = ArrayFormat::LinearHistogram.underflow_bucket_index();
39        let overflow_index = ArrayFormat::LinearHistogram.overflow_bucket_index(self.buckets);
40        while value >= bucket_end && index < overflow_index {
41            bucket_end = bucket_end.saturating_add(self.step_size);
42            index += 1;
43        }
44        index
45    }
46}
47
48impl HistogramProperty for UintLinearHistogramProperty {
49    type Type = u64;
50
51    fn insert(&self, value: u64) {
52        self.insert_multiple(value, 1);
53    }
54
55    fn insert_multiple(&self, value: u64, count: usize) {
56        self.array.add(self.get_index(value), count as u64);
57    }
58
59    fn clear(&self) {
60        if let Some(ref inner_ref) = self.array.inner.inner_ref() {
61            // Ensure we don't delete the array slots that contain histogram metadata.
62            inner_ref
63                .state
64                .try_lock()
65                .and_then(|mut state| {
66                    // Clear histogram buckets starting at first bucket, which
67                    // is the underflow bucket.
68                    state.clear_array(
69                        inner_ref.block_index,
70                        ArrayFormat::LinearHistogram.underflow_bucket_index(),
71                    )
72                })
73                .unwrap_or_else(|err| {
74                    error!(err:?; "Failed to clear property");
75                });
76        }
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use crate::writer::Inspector;
84    use crate::writer::testing_utils::GetBlockExt;
85    use inspect_format::{Array, Uint};
86
87    #[fuchsia::test]
88    fn uint_linear_histogram() {
89        let inspector = Inspector::default();
90        let root = inspector.root();
91        let node = root.create_child("node");
92        {
93            let uint_histogram = node.create_uint_linear_histogram(
94                "uint-histogram",
95                LinearHistogramParams { floor: 10, step_size: 5, buckets: 5 },
96            );
97            uint_histogram.insert_multiple(0, 2); // underflow
98            uint_histogram.insert(25);
99            uint_histogram.insert(500); // overflow
100            uint_histogram.array.get_block::<_, Array<Uint>>(|block| {
101                for (i, value) in [10, 5, 2, 0, 0, 0, 1, 0, 1].iter().enumerate() {
102                    assert_eq!(block.get(i).unwrap(), *value);
103                }
104            });
105
106            uint_histogram.clear();
107            uint_histogram.array.get_block::<_, Array<Uint>>(|block| {
108                for (i, value) in [10, 5, 0, 0, 0, 0, 0, 0, 0].iter().enumerate() {
109                    assert_eq!(*value, block.get(i).unwrap());
110                }
111            });
112
113            node.get_block::<_, inspect_format::Node>(|node_block| {
114                assert_eq!(node_block.child_count(), 1);
115            });
116        }
117        node.get_block::<_, inspect_format::Node>(|node_block| {
118            assert_eq!(node_block.child_count(), 0);
119        });
120    }
121
122    #[fuchsia::test]
123    fn underflow() {
124        let inspector = Inspector::default();
125        let root = inspector.root();
126        let hist = root.create_uint_linear_histogram(
127            "test",
128            LinearHistogramParams { floor: 0, step_size: u64::MAX / 2, buckets: 4 },
129        );
130
131        // | Bucket    | Range                  |
132        // |-----------|------------------------|
133        // | Underflow | Empty                  |
134        // | 0         | [0, u64::MAX/2)        |
135        // | 1         | [u64::MAX/2, u64::MAX) |
136        // | 2..3      | Empty                  |
137        // | Overflow  | [u64::MAX, +inf)       |
138
139        hist.insert(0); // increment bucket 0
140        hist.insert(u64::MAX / 2); // increment bucket 1
141        hist.insert(u64::MAX); // increment overflow bucket
142
143        hist.array.get_block::<_, Array<Uint>>(|block| {
144            assert_eq!(block.get(0).unwrap(), 0);
145            assert_eq!(block.get(1).unwrap(), u64::MAX / 2);
146
147            assert_eq!(block.get(2).unwrap(), 0); // underflow
148            assert_eq!(block.get(3).unwrap(), 1); // bucket 0
149            assert_eq!(block.get(4).unwrap(), 1); // bucket 1
150            assert_eq!(block.get(5).unwrap(), 0); // bucket 2
151            assert_eq!(block.get(6).unwrap(), 0); // bucket 3
152            assert_eq!(block.get(7).unwrap(), 1); // overflow
153        });
154    }
155}