fuchsia_inspect_contrib/nodes/
dedupe_log.rs1use std::collections::VecDeque;
6
7use fuchsia_inspect::{Node, NumericProperty, UintProperty};
8use fuchsia_inspect_derive::Unit;
9
10use crate::nodes::{BootTimeProperty, NodeTimeExt};
11
12const CREATED_AT_PROPERTY_NAME: &str = "Created@time";
16const LAST_SEEN_AT_PROPERTY_NAME: &str = "LastSeen@time";
20
21struct EntryData<T: Unit> {
22 _node: Node,
23 count: UintProperty,
24 last_seen: Option<BootTimeProperty>,
25 _time: BootTimeProperty,
26 _log: T::Data,
27}
28
29pub struct DedupeLogNode<T: Unit + PartialEq> {
32 node: Node,
33 last_log: Option<T>,
34 entries: VecDeque<EntryData<T>>,
35 next_index: u64,
36 capacity: usize,
37}
38
39impl<T: Unit + PartialEq> DedupeLogNode<T> {
40 pub fn new(node: Node, capacity: usize) -> Self {
41 let capacity = std::cmp::max(capacity, 1);
42 Self {
43 node,
44 last_log: None,
45 entries: VecDeque::with_capacity(capacity),
46 next_index: 0,
47 capacity,
48 }
49 }
50
51 pub fn insert(&mut self, log: T) {
58 if self.last_log.as_ref() == Some(&log) {
59 if let Some(entry) = self.entries.back_mut() {
60 entry.count.add(1);
61 if let Some(time_prop) = &entry.last_seen {
62 time_prop.update();
63 } else {
64 let now = zx::BootInstant::get();
65 entry.last_seen =
66 Some(entry._node.create_time_at(LAST_SEEN_AT_PROPERTY_NAME, now));
67 }
68 }
69 return;
70 }
71
72 if self.entries.len() >= self.capacity {
73 self.entries.pop_front();
74 }
75
76 let index_str = self.next_index.to_string();
77 self.next_index += 1;
78
79 let child_node = self.node.create_child(&index_str);
80
81 let now = zx::BootInstant::get();
82 let time_prop = child_node.create_time_at(CREATED_AT_PROPERTY_NAME, now);
83 let count_prop = child_node.create_uint("count", 1);
84 let log_data = log.inspect_create(&child_node, "log");
85
86 self.entries.push_back(EntryData {
87 _node: child_node,
88 count: count_prop,
89 last_seen: None,
90 _log: log_data,
91 _time: time_prop,
92 });
93
94 self.last_log = Some(log);
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use diagnostics_assertions::{AnyNumericProperty, assert_data_tree};
102 use fuchsia_inspect::Inspector;
103
104 #[fuchsia::test]
105 async fn test_insert_unique_items() {
106 let inspector = Inspector::default();
107 let log_node = inspector.root().create_child("log");
108 let mut dedupe_log = DedupeLogNode::new(log_node, 3);
109
110 dedupe_log.insert(111);
111 dedupe_log.insert(222);
112 dedupe_log.insert(333);
113
114 assert_data_tree!(inspector, root: {
115 log: {
116 "0": {
117 "Created@time": AnyNumericProperty,
118 count: 1u64,
119 log: 111i64,
120 },
121 "1": {
122 "Created@time": AnyNumericProperty,
123 count: 1u64,
124 log: 222i64,
125 },
126 "2": {
127 "Created@time": AnyNumericProperty,
128 count: 1u64,
129 log: 333i64,
130 },
131 }
132 });
133 }
134
135 #[fuchsia::test]
136 async fn test_deduplication() {
137 let inspector = Inspector::default();
138 let log_node = inspector.root().create_child("log");
139 let mut dedupe_log = DedupeLogNode::new(log_node, 3);
140
141 dedupe_log.insert(111);
142 dedupe_log.insert(111);
143 dedupe_log.insert(111);
144
145 assert_data_tree!(inspector, root: {
146 log: {
147 "0": {
148 "Created@time": AnyNumericProperty,
149 "LastSeen@time": AnyNumericProperty,
150 count: 3u64,
151 log: 111i64,
152 },
153 }
154 });
155
156 dedupe_log.insert(222);
158 dedupe_log.insert(222);
159
160 assert_data_tree!(inspector, root: {
161 log: {
162 "0": {
163 "Created@time": AnyNumericProperty,
164 "LastSeen@time": AnyNumericProperty,
165 count: 3u64,
166 log: 111i64,
167 },
168 "1": {
169 "Created@time": AnyNumericProperty,
170 "LastSeen@time": AnyNumericProperty,
171 count: 2u64,
172 log: 222i64,
173 },
174 }
175 });
176 }
177
178 #[fuchsia::test]
179 async fn test_eviction() {
180 let inspector = Inspector::default();
181 let log_node = inspector.root().create_child("log");
182 let mut dedupe_log = DedupeLogNode::new(log_node, 2);
183
184 dedupe_log.insert(111);
185 dedupe_log.insert(222);
186 dedupe_log.insert(333); assert_data_tree!(inspector, root: {
189 log: {
190 "1": {
191 "Created@time": AnyNumericProperty,
192 count: 1u64,
193 log: 222i64,
194 },
195 "2": {
196 "Created@time": AnyNumericProperty,
197 count: 1u64,
198 log: 333i64,
199 },
200 }
201 });
202 }
203
204 #[derive(PartialEq, Unit)]
205 struct Item {
206 num: u64,
207 string: String,
208 }
209
210 #[fuchsia::test]
211 async fn test_insert_custom_struct() {
212 let inspector = Inspector::default();
213 let log_node = inspector.root().create_child("log");
214 let mut dedupe_log = DedupeLogNode::new(log_node, 3);
215
216 dedupe_log.insert(Item { num: 1337u64, string: "42".to_string() });
217 dedupe_log.insert(Item { num: 1337u64, string: "42".to_string() }); assert_data_tree!(inspector, root: {
220 log: {
221 "0": {
222 "Created@time": AnyNumericProperty,
223 "LastSeen@time": AnyNumericProperty,
224 count: 2u64,
225 log: { num: 1337u64, string: "42".to_string() }
226 },
227 }
228 });
229 }
230}