fuchsia_inspect/writer/types/
double_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, DoubleArrayProperty, HistogramProperty, InspectType,
7    Node,
8};
9use diagnostics_hierarchy::{ArrayFormat, LinearHistogramParams};
10use log::error;
11use std::borrow::Cow;
12
13#[derive(Debug, Default)]
14/// A linear histogram property for double values.
15pub struct DoubleLinearHistogramProperty {
16    array: DoubleArrayProperty,
17    floor: f64,
18    buckets: usize,
19    step_size: f64,
20}
21
22impl InspectType for DoubleLinearHistogramProperty {}
23
24impl DoubleLinearHistogramProperty {
25    pub(crate) fn new(
26        name: Cow<'_, str>,
27        params: LinearHistogramParams<f64>,
28        parent: &Node,
29    ) -> Self {
30        let slots = params.buckets + ArrayFormat::LinearHistogram.extra_slots();
31        let array = parent.create_double_array_internal(name, slots, ArrayFormat::LinearHistogram);
32        array.set(0, params.floor);
33        array.set(1, params.step_size);
34        Self { floor: params.floor, step_size: params.step_size, buckets: params.buckets, array }
35    }
36
37    fn get_index(&self, value: f64) -> usize {
38        let mut bucket_end = self.floor; // The exclusive end of a bucket's range.
39        let mut index = ArrayFormat::LinearHistogram.underflow_bucket_index();
40        let overflow_index = ArrayFormat::LinearHistogram.overflow_bucket_index(self.buckets);
41        while value >= bucket_end && index < overflow_index {
42            bucket_end += self.step_size;
43            index += 1;
44        }
45        index
46    }
47}
48
49impl HistogramProperty for DoubleLinearHistogramProperty {
50    type Type = f64;
51
52    fn insert(&self, value: f64) {
53        self.insert_multiple(value, 1);
54    }
55
56    fn insert_multiple(&self, value: f64, count: usize) {
57        self.array.add(self.get_index(value), count as f64);
58    }
59
60    fn clear(&self) {
61        if let Some(ref inner_ref) = self.array.inner.inner_ref() {
62            // Ensure we don't delete the array slots that contain histogram metadata.
63            inner_ref
64                .state
65                .try_lock()
66                .and_then(|mut state| {
67                    // Clear histogram buckets starting at first bucket, which
68                    // is the underflow bucket.
69                    state.clear_array(
70                        inner_ref.block_index,
71                        ArrayFormat::LinearHistogram.underflow_bucket_index(),
72                    )
73                })
74                .unwrap_or_else(|err| {
75                    error!(err:?; "Failed to clear property");
76                });
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::writer::Inspector;
85    use crate::writer::testing_utils::GetBlockExt;
86    use inspect_format::{Array, Double};
87
88    #[fuchsia::test]
89    fn double_linear_histogram() {
90        let inspector = Inspector::default();
91        let root = inspector.root();
92        let node = root.create_child("node");
93        {
94            let double_histogram = node.create_double_linear_histogram(
95                "double-histogram",
96                LinearHistogramParams { floor: 10.0, step_size: 5.0, buckets: 5 },
97            );
98            double_histogram.insert_multiple(0.0, 2); // underflow
99            double_histogram.insert(25.3);
100            double_histogram.insert(500.0); // overflow
101            double_histogram.array.get_block::<_, Array<Double>>(|block| {
102                for (i, value) in [10.0, 5.0, 2.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0].iter().enumerate()
103                {
104                    assert_eq!(block.get(i).unwrap(), *value);
105                }
106            });
107
108            node.get_block::<_, inspect_format::Node>(|node_block| {
109                assert_eq!(node_block.child_count(), 1);
110            });
111        }
112        node.get_block::<_, inspect_format::Node>(|node_block| {
113            assert_eq!(node_block.child_count(), 0);
114        });
115    }
116}