Skip to main content

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