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