Skip to main content

fuchsia_inspect/writer/types/
uint_exponential_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, ExponentialHistogramParams};
9use log::error;
10use std::borrow::Cow;
11
12#[derive(Debug, Default)]
13/// An exponential histogram property for uint values.
14pub struct UintExponentialHistogramProperty {
15    array: UintArrayProperty,
16    floor: u64,
17    initial_step: u64,
18    step_multiplier: u64,
19    buckets: usize,
20}
21
22impl InspectType for UintExponentialHistogramProperty {
23    fn into_recorded(self) -> crate::writer::types::RecordedInspectType {
24        crate::writer::types::RecordedInspectType::UintArray(self.array)
25    }
26}
27
28impl UintExponentialHistogramProperty {
29    pub(crate) fn new(
30        name: Cow<'_, str>,
31        params: ExponentialHistogramParams<u64>,
32        parent: &Node,
33    ) -> Self {
34        let slots = params.buckets + ArrayFormat::ExponentialHistogram.extra_slots();
35        let array =
36            parent.create_uint_array_internal(name, slots, ArrayFormat::ExponentialHistogram);
37        array.set(0, params.floor);
38        array.set(1, params.initial_step);
39        array.set(2, params.step_multiplier);
40        Self {
41            floor: params.floor,
42            initial_step: params.initial_step,
43            step_multiplier: params.step_multiplier,
44            buckets: params.buckets,
45            array,
46        }
47    }
48
49    fn get_index(&self, value: u64) -> usize {
50        let floor = self.floor;
51        let mut bucket_end = floor; // The exclusive end of a bucket's range.
52        let mut step = self.initial_step;
53
54        let mut index = ArrayFormat::ExponentialHistogram.underflow_bucket_index();
55        let overflow_index = ArrayFormat::ExponentialHistogram.overflow_bucket_index(self.buckets);
56        while value >= bucket_end && index < overflow_index {
57            if let Some(c) = floor.checked_add(step) {
58                bucket_end = c;
59            } else {
60                // Overflow. The next bucket is guaranteed to be choosen, as it
61                // contains all possible remaining values for a u64.
62                return index + 1;
63            }
64
65            step = step.saturating_mul(self.step_multiplier);
66            index += 1;
67        }
68        index
69    }
70}
71
72impl HistogramProperty for UintExponentialHistogramProperty {
73    type Type = u64;
74
75    fn insert(&self, value: u64) {
76        self.insert_multiple(value, 1);
77    }
78
79    fn insert_multiple(&self, value: u64, count: usize) {
80        self.array.add(self.get_index(value), count as u64);
81    }
82
83    fn clear(&self) {
84        if let Some(ref inner_ref) = self.array.inner.inner_ref() {
85            // Ensure we don't delete the array slots that contain histogram metadata.
86            inner_ref
87                .state
88                .try_lock()
89                .and_then(|mut state| {
90                    // Clear histogram buckets starting at first bucket, which
91                    // is the underflow bucket.
92                    state.clear_array(
93                        inner_ref.block_index,
94                        ArrayFormat::ExponentialHistogram.underflow_bucket_index(),
95                    )
96                })
97                .unwrap_or_else(|err| {
98                    error!(err:?; "Failed to clear property");
99                });
100        }
101    }
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use crate::writer::Inspector;
108    use crate::writer::testing_utils::GetBlockExt;
109    use inspect_format::{Array, Uint};
110
111    #[fuchsia::test]
112    fn test_uint_exp_histogram() {
113        let inspector = Inspector::default();
114        let root = inspector.root();
115        let node = root.create_child("node");
116        {
117            let uint_histogram = node.create_uint_exponential_histogram(
118                "uint-histogram",
119                ExponentialHistogramParams {
120                    floor: 1,
121                    initial_step: 1,
122                    step_multiplier: 2,
123                    buckets: 4,
124                },
125            );
126            uint_histogram.insert_multiple(0, 2); // underflow
127            uint_histogram.insert(8);
128            uint_histogram.insert(500); // overflow
129            uint_histogram.array.get_block::<_, Array<Uint>>(|block| {
130                for (i, value) in [1, 1, 2, 2, 0, 0, 0, 1, 1].iter().enumerate() {
131                    assert_eq!(block.get(i).unwrap(), *value);
132                }
133            });
134
135            uint_histogram.clear();
136            uint_histogram.array.get_block::<_, Array<Uint>>(|block| {
137                for (i, value) in [1, 1, 2, 0, 0, 0, 0, 0, 0].iter().enumerate() {
138                    assert_eq!(*value, block.get(i).unwrap());
139                }
140            });
141
142            node.get_block::<_, inspect_format::Node>(|node_block| {
143                assert_eq!(node_block.child_count(), 1);
144            });
145        }
146        node.get_block::<_, inspect_format::Node>(|node_block| {
147            assert_eq!(node_block.child_count(), 0);
148        });
149    }
150
151    #[fuchsia::test]
152    fn overflow_underflow() {
153        let inspector = Inspector::default();
154        let root = inspector.root();
155        let hist = root.create_uint_exponential_histogram(
156            "test",
157            ExponentialHistogramParams {
158                floor: 1,
159                initial_step: u64::MAX / 2,
160                step_multiplier: 2,
161                buckets: 4,
162            },
163        );
164
165        // | Bucket    | Range                      |
166        // |-----------|----------------------------|
167        // | Underflow | [0, 1)                     |
168        // | 0         | [1, u64::MAX/2 + 1)        |
169        // | 1         | [u64::MAX/2 + 1, u64::MAX] |
170        // | 2..3      | Empty                      |
171        // | Overflow  | Empty                      |
172
173        hist.insert((u64::MAX / 2) + 1);
174        hist.insert(0);
175
176        hist.array.get_block::<_, Array<Uint>>(|block| {
177            assert_eq!(block.get(0).unwrap(), 1);
178            assert_eq!(block.get(1).unwrap(), u64::MAX / 2);
179            assert_eq!(block.get(2).unwrap(), 2);
180
181            assert_eq!(block.get(3).unwrap(), 1); // underflow
182            assert_eq!(block.get(4).unwrap(), 0); // bucket 0
183            assert_eq!(block.get(5).unwrap(), 1); // bucket 1
184            assert_eq!(block.get(6).unwrap(), 0); // bucket 2
185            assert_eq!(block.get(7).unwrap(), 0); // bucket 3
186            assert_eq!(block.get(8).unwrap(), 0); // overflow
187        });
188    }
189}