fuchsia_inspect/writer/types/
int_exponential_histogram.rs1use crate::writer::{
6 ArithmeticArrayProperty, ArrayProperty, HistogramProperty, InspectType, IntArrayProperty, Node,
7};
8use diagnostics_hierarchy::{ArrayFormat, ExponentialHistogramParams};
9use log::error;
10use std::borrow::Cow;
11
12#[derive(Debug, Default)]
13pub struct IntExponentialHistogramProperty {
15 array: IntArrayProperty,
16 floor: i64,
17 initial_step: i64,
18 step_multiplier: i64,
19 buckets: usize,
20}
21
22impl InspectType for IntExponentialHistogramProperty {
23 fn into_recorded(self) -> crate::writer::types::RecordedInspectType {
24 crate::writer::types::RecordedInspectType::IntArray(self.array)
25 }
26}
27
28impl IntExponentialHistogramProperty {
29 pub(crate) fn new(
30 name: Cow<'_, str>,
31 params: ExponentialHistogramParams<i64>,
32 parent: &Node,
33 ) -> Self {
34 let slots = params.buckets + ArrayFormat::ExponentialHistogram.extra_slots();
35 let array =
36 parent.create_int_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: i64) -> usize {
50 let value = value as i128;
54 let floor = self.floor as i128;
55 let mut bucket_end = floor; let mut step = self.initial_step as i128;
57
58 let mut index = ArrayFormat::ExponentialHistogram.underflow_bucket_index();
59 let overflow_index = ArrayFormat::ExponentialHistogram.overflow_bucket_index(self.buckets);
60
61 while value >= bucket_end && index < overflow_index {
62 if let Some(c) = floor.checked_add(step) {
63 bucket_end = c;
64 } else {
65 return index + 1;
68 }
69
70 if bucket_end > i64::MAX as i128 {
71 return index + 1;
74 }
75
76 step = step.saturating_mul(self.step_multiplier as i128);
77 index += 1;
78 }
79 index
80 }
81}
82
83impl HistogramProperty for IntExponentialHistogramProperty {
84 type Type = i64;
85
86 fn insert(&self, value: i64) {
87 self.insert_multiple(value, 1);
88 }
89
90 fn insert_multiple(&self, value: i64, count: usize) {
91 self.array.add(self.get_index(value), count as i64);
92 }
93
94 fn clear(&self) {
95 if let Some(ref inner_ref) = self.array.inner.inner_ref() {
96 inner_ref
98 .state
99 .try_lock()
100 .and_then(|mut state| {
101 state.clear_array(
104 inner_ref.block_index,
105 ArrayFormat::ExponentialHistogram.underflow_bucket_index(),
106 )
107 })
108 .unwrap_or_else(|err| {
109 error!(err:?; "Failed to clear property");
110 });
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::writer::Inspector;
119 use crate::writer::testing_utils::GetBlockExt;
120 use inspect_format::{Array, Int};
121
122 #[fuchsia::test]
123 fn test_int_exp_histogram() {
124 let inspector = Inspector::default();
125 let root = inspector.root();
126 let node = root.create_child("node");
127 {
128 let int_histogram = node.create_int_exponential_histogram(
129 "int-histogram",
130 ExponentialHistogramParams {
131 floor: 1,
132 initial_step: 1,
133 step_multiplier: 2,
134 buckets: 4,
135 },
136 );
137 int_histogram.insert_multiple(-1, 2); int_histogram.insert(8);
139 int_histogram.insert(500); int_histogram.array.get_block::<_, Array<Int>>(|block| {
141 for (i, value) in [1, 1, 2, 2, 0, 0, 0, 1, 1].iter().enumerate() {
142 assert_eq!(block.get(i).unwrap(), *value);
143 }
144 });
145
146 node.get_block::<_, inspect_format::Node>(|node_block| {
147 assert_eq!(node_block.child_count(), 1);
148 });
149 }
150 node.get_block::<_, inspect_format::Node>(|node_block| {
151 assert_eq!(node_block.child_count(), 0);
152 });
153 }
154
155 #[fuchsia::test]
156 fn exp_histogram_insert() {
157 let inspector = Inspector::default();
158 let root = inspector.root();
159 let hist = root.create_int_exponential_histogram(
160 "test",
161 ExponentialHistogramParams {
162 floor: 0,
163 initial_step: 2,
164 step_multiplier: 4,
165 buckets: 4,
166 },
167 );
168 for i in -200..200 {
169 hist.insert(i);
170 }
171 hist.array.get_block::<_, Array<Int>>(|block| {
172 assert_eq!(block.get(0).unwrap(), 0);
173 assert_eq!(block.get(1).unwrap(), 2);
174 assert_eq!(block.get(2).unwrap(), 4);
175
176 let i = 3;
178 assert_eq!(block.get(i).unwrap(), 200);
179 assert_eq!(block.get(i + 1).unwrap(), 2);
180 assert_eq!(block.get(i + 2).unwrap(), 6);
181 assert_eq!(block.get(i + 3).unwrap(), 24);
182 assert_eq!(block.get(i + 4).unwrap(), 96);
183 assert_eq!(block.get(i + 5).unwrap(), 72);
184 });
185 }
186
187 #[fuchsia::test]
188 fn overflow_underflow() {
189 let inspector = Inspector::default();
190 let root = inspector.root();
191 let hist = root.create_int_exponential_histogram(
192 "test",
193 ExponentialHistogramParams {
194 floor: 0,
195 initial_step: i64::MAX / 2,
196 step_multiplier: 2,
197 buckets: 4,
198 },
199 );
200
201 hist.insert((i64::MAX / 2) + 1);
203
204 hist.insert(-5);
205
206 hist.array.get_block::<_, Array<Int>>(|block| {
207 assert_eq!(block.get(0).unwrap(), 0);
208 assert_eq!(block.get(1).unwrap(), i64::MAX / 2);
209 assert_eq!(block.get(2).unwrap(), 2);
210
211 assert_eq!(block.get(3).unwrap(), 1);
212 assert_eq!(block.get(4).unwrap(), 0);
213 assert_eq!(block.get(5).unwrap(), 1);
214 assert_eq!(block.get(6).unwrap(), 0);
215 assert_eq!(block.get(7).unwrap(), 0);
216 assert_eq!(block.get(8).unwrap(), 0);
217 });
218 }
219}