1use super::types::*;
6use super::MetadataValue;
7use crate::nodes::BoundedListNode;
8use fuchsia_inspect::{self as inspect, InspectTypeReparentable};
9use fuchsia_sync::Mutex;
10use std::collections::VecDeque;
11use std::marker::PhantomData;
12use std::ops::Deref;
13use std::sync::{Arc, Weak};
14
15pub struct MetaEventNode(inspect::Node);
16
17impl MetaEventNode {
18 pub fn new(parent: &inspect::Node) -> Self {
19 Self(parent.create_child("meta"))
20 }
21
22 fn take_node(self) -> inspect::Node {
23 self.0
24 }
25}
26
27impl Deref for MetaEventNode {
28 type Target = inspect::Node;
29 fn deref(&self) -> &Self::Target {
30 &self.0
31 }
32}
33
34#[derive(Debug)]
36pub struct ShadowEvent {
37 time: zx::BootInstant,
38}
39
40#[derive(Debug)]
42pub struct ShadowBuffer {
43 buffer: VecDeque<ShadowEvent>,
44 capacity: usize,
45}
46
47impl ShadowBuffer {
48 pub fn new(capacity: usize) -> Self {
49 Self { buffer: VecDeque::with_capacity(capacity), capacity }
50 }
51 pub fn add_entry(&mut self, shadow_event: ShadowEvent) {
53 if self.buffer.len() >= self.capacity {
54 self.buffer.pop_front();
55 }
56 self.buffer.push_back(shadow_event);
57 }
58 pub fn history_duration(&self) -> zx::BootDuration {
60 if self.buffer.len() < 2 {
61 return zx::BootDuration::ZERO;
62 }
63 self.buffer.back().unwrap().time - self.buffer.front().unwrap().time
64 }
65 pub fn at_capacity(&self) -> bool {
67 self.buffer.len() == self.capacity
68 }
69}
70
71#[derive(Debug)]
73pub struct Inner {
74 buffer: BoundedListNode,
76 shadow: ShadowBuffer,
79}
80
81#[derive(Debug)]
82pub struct GraphEventsTracker {
83 inner: Arc<Mutex<Inner>>,
85}
86
87impl GraphEventsTracker {
88 pub fn new(list_node: inspect::Node, max_events: usize) -> Self {
89 Self {
90 inner: Arc::new(Mutex::new(Inner {
91 buffer: BoundedListNode::new(list_node, max_events),
92 shadow: ShadowBuffer::new(max_events),
93 })),
94 }
95 }
96
97 pub fn for_vertex<I>(&self) -> GraphObjectEventTracker<VertexMarker<I>> {
98 GraphObjectEventTracker { inner: self.inner.clone(), _phantom: PhantomData }
99 }
100
101 pub fn history_stats_accessor(&self) -> HistoryStatsAccessor {
103 HistoryStatsAccessor(Arc::downgrade(&self.inner))
104 }
105}
106
107#[derive(Clone)]
108pub struct HistoryStatsAccessor(Weak<Mutex<Inner>>);
109
110impl HistoryStatsAccessor {
111 pub fn history_duration(&self) -> zx::BootDuration {
112 self.0
113 .upgrade()
114 .map_or(zx::BootDuration::ZERO, |inner| inner.lock().shadow.history_duration())
115 }
116 pub fn at_capacity(&self) -> bool {
117 self.0.upgrade().is_some_and(|inner| inner.lock().shadow.at_capacity())
118 }
119}
120
121#[derive(Debug)]
122pub struct GraphObjectEventTracker<T> {
123 inner: Arc<Mutex<Inner>>,
124 _phantom: PhantomData<T>,
125}
126
127impl<I> GraphObjectEventTracker<VertexMarker<I>>
128where
129 I: VertexId,
130{
131 pub fn for_edge(&self) -> GraphObjectEventTracker<EdgeMarker> {
132 GraphObjectEventTracker { inner: self.inner.clone(), _phantom: PhantomData }
133 }
134
135 pub fn record_added(&self, id: &I, meta_event_node: MetaEventNode) {
136 let meta_event_node = meta_event_node.take_node();
137 let instant = zx::BootInstant::get();
138 let mut inner = self.inner.lock();
139 inner.buffer.add_entry(|node| {
140 node.record_int("@time", instant.into_nanos());
141 node.record_string("event", "add_vertex");
142 node.record_string("vertex_id", id.get_id().as_ref());
143 let _ = meta_event_node.reparent(node);
144 node.record(meta_event_node);
145 });
146 inner.shadow.add_entry(ShadowEvent { time: instant });
147 }
148
149 pub fn record_removed(&self, id: &str) {
150 let instant = zx::BootInstant::get();
151 let mut inner = self.inner.lock();
152 inner.buffer.add_entry(|node| {
153 node.record_int("@time", instant.into_nanos());
154 node.record_string("event", "remove_vertex");
155 node.record_string("vertex_id", id.get_id().as_ref());
156 });
157 inner.shadow.add_entry(ShadowEvent { time: instant });
158 }
159}
160
161impl GraphObjectEventTracker<EdgeMarker> {
162 pub fn record_added(&self, from: &str, to: &str, id: u64, meta_event_node: MetaEventNode) {
163 let meta_event_node = meta_event_node.take_node();
164 let instant = zx::BootInstant::get();
165 let mut inner = self.inner.lock();
166 inner.buffer.add_entry(|node| {
167 node.record_int("@time", instant.into_nanos());
168 node.record_string("event", "add_edge");
169 node.record_string("from", from);
170 node.record_string("to", to);
171 node.record_uint("edge_id", id);
172 let _ = meta_event_node.reparent(node);
173 node.record(meta_event_node);
174 });
175 inner.shadow.add_entry(ShadowEvent { time: instant });
176 }
177
178 pub fn record_removed(&self, id: u64) {
179 let instant = zx::BootInstant::get();
180 let mut inner = self.inner.lock();
181 inner.buffer.add_entry(|node| {
182 node.record_int("@time", instant.into_nanos());
183 node.record_string("event", "remove_edge");
184 node.record_uint("edge_id", id);
185 });
186 inner.shadow.add_entry(ShadowEvent { time: instant });
187 }
188}
189
190impl<T> GraphObjectEventTracker<T>
191where
192 T: GraphObject,
193{
194 pub fn metadata_updated(&self, id: &T::Id, key: &str, value: &MetadataValue<'_>) {
195 let instant = zx::BootInstant::get();
196 let mut inner = self.inner.lock();
197 inner.buffer.add_entry(|node| {
198 node.record_int("@time", instant.into_nanos());
199 node.record_string("event", "update_key");
200 node.record_string("key", key);
201 value.record_inspect(node, "update");
202 T::write_to_node(node, id);
203 });
204 inner.shadow.add_entry(ShadowEvent { time: instant });
205 }
206
207 pub fn metadata_dropped(&self, id: &T::Id, key: &str) {
208 let instant = zx::BootInstant::get();
209 let mut inner = self.inner.lock();
210 inner.buffer.add_entry(|node| {
211 node.record_int("@time", instant.into_nanos());
212 node.record_string("event", "drop_key");
213 node.record_string("key", key);
214 T::write_to_node(node, id);
215 });
216 inner.shadow.add_entry(ShadowEvent { time: instant });
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use diagnostics_assertions::{assert_data_tree, AnyProperty};
224 use fuchsia_inspect::DiagnosticsHierarchyGetter;
225
226 impl GraphEventsTracker {
227 fn shadow_buffer_len(&self) -> usize {
228 self.inner.lock().shadow.buffer.len()
229 }
230 fn shadow_time_of(&self, index: usize) -> zx::BootInstant {
231 self.inner.lock().shadow.buffer.get(index).unwrap().time
232 }
233 }
234
235 #[test]
236 fn tracker_starts_empty() {
237 let inspector = inspect::Inspector::default();
238 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
239 assert_data_tree!(inspector, root: {
240 events: {}
241 });
242 assert_eq!(tracker.shadow_buffer_len(), 0);
243 }
244
245 fn get_time(inspector: &inspect::Inspector, path: &[&str]) -> zx::BootInstant {
246 let instant = inspector
247 .get_diagnostics_hierarchy()
248 .get_property_by_path(path)
249 .and_then(|p| p.int())
250 .unwrap();
251 zx::BootInstant::from_nanos(instant)
252 }
253
254 #[fuchsia::test]
255 fn vertex_add() {
256 let inspector = inspect::Inspector::default();
257 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
258 let vertex_tracker = tracker.for_vertex::<u64>();
259 let meta_event_node = MetaEventNode::new(inspector.root());
260 meta_event_node.record_bool("placeholder", true);
261 vertex_tracker.record_added(&123, meta_event_node);
262 assert_data_tree!(inspector, root: {
263 events: {
264 "0": {
265 "@time": AnyProperty,
266 event: "add_vertex",
267 vertex_id: "123",
268 meta: {
269 placeholder: true,
270 }
271 }
272 }
273 });
274 assert_eq!(tracker.shadow_buffer_len(), 1);
275 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
276 }
277
278 #[fuchsia::test]
279 fn vertex_remove() {
280 let inspector = inspect::Inspector::default();
281 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
282 let vertex_tracker = tracker.for_vertex::<u64>();
283 vertex_tracker.record_removed("20");
284 assert_data_tree!(inspector, root: {
285 events: {
286 "0": {
287 "@time": AnyProperty,
288 event: "remove_vertex",
289 vertex_id: "20",
290 }
291 }
292 });
293 assert_eq!(tracker.shadow_buffer_len(), 1);
294 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
295 }
296
297 #[fuchsia::test]
298 fn vertex_metadata_update() {
299 let inspector = inspect::Inspector::default();
300 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
301 let vertex_tracker = tracker.for_vertex::<u64>();
302 vertex_tracker.metadata_updated(&10, "foo", &MetadataValue::Uint(3));
303 assert_data_tree!(inspector, root: {
304 events: {
305 "0": {
306 "@time": AnyProperty,
307 event: "update_key",
308 vertex_id: "10",
309 key: "foo",
310 update: 3u64,
311 }
312 }
313 });
314 assert_eq!(tracker.shadow_buffer_len(), 1);
315 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
316 }
317
318 #[fuchsia::test]
319 fn vertex_metadata_drop() {
320 let inspector = inspect::Inspector::default();
321 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 2);
322 let vertex_tracker = tracker.for_vertex::<u64>();
323 vertex_tracker.metadata_updated(&10, "foo", &MetadataValue::Uint(3));
324 vertex_tracker.metadata_dropped(&10, "foo");
325 assert_data_tree!(inspector, root: {
326 events: {
327 "0": {
328 "@time": AnyProperty,
329 event: "update_key",
330 vertex_id: "10",
331 key: "foo",
332 update: 3u64,
333 },
334 "1": {
335 "@time": AnyProperty,
336 event: "drop_key",
337 vertex_id: "10",
338 key: "foo",
339 }
340 }
341 });
342 assert_eq!(tracker.shadow_buffer_len(), 2);
343 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
344 assert_eq!(get_time(&inspector, &["events", "1", "@time"]), tracker.shadow_time_of(1));
345 }
346
347 #[fuchsia::test]
348 fn edge_add() {
349 let inspector = inspect::Inspector::default();
350 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
351 let vertex_tracker = tracker.for_vertex::<u64>();
352 let edge_tracker = vertex_tracker.for_edge();
353 let meta_event_node = MetaEventNode::new(inspector.root());
354 meta_event_node.record_bool("placeholder", true);
355 edge_tracker.record_added("src", "dst", 10, meta_event_node);
356 assert_data_tree!(inspector, root: {
357 events: {
358 "0": {
359 "@time": AnyProperty,
360 event: "add_edge",
361 from: "src",
362 to: "dst",
363 edge_id: 10u64,
364 meta: {
365 placeholder: true,
366 }
367 }
368 }
369 });
370 assert_eq!(tracker.shadow_buffer_len(), 1);
371 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
372 }
373
374 #[fuchsia::test]
375 fn edge_remove() {
376 let inspector = inspect::Inspector::default();
377 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
378 let vertex_tracker = tracker.for_vertex::<u64>();
379 let edge_tracker = vertex_tracker.for_edge();
380 edge_tracker.record_removed(20);
381 assert_data_tree!(inspector, root: {
382 events: {
383 "0": {
384 "@time": AnyProperty,
385 event: "remove_edge",
386 edge_id: 20u64,
387 }
388 }
389 });
390 assert_eq!(tracker.shadow_buffer_len(), 1);
391 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
392 }
393
394 #[fuchsia::test]
395 fn edge_metadata_update() {
396 let inspector = inspect::Inspector::default();
397 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 1);
398 let vertex_tracker = tracker.for_vertex::<u64>();
399 let edge_tracker = vertex_tracker.for_edge();
400 edge_tracker.metadata_updated(&10, "foo", &MetadataValue::Uint(3));
401 assert_data_tree!(inspector, root: {
402 events: {
403 "0": {
404 "@time": AnyProperty,
405 event: "update_key",
406 edge_id: 10u64,
407 key: "foo",
408 update: 3u64,
409 }
410 }
411 });
412 assert_eq!(tracker.shadow_buffer_len(), 1);
413 assert_eq!(get_time(&inspector, &["events", "0", "@time"]), tracker.shadow_time_of(0));
414 }
415
416 #[fuchsia::test]
417 fn circular_buffer_semantics() {
418 let inspector = inspect::Inspector::default();
419 let tracker = GraphEventsTracker::new(inspector.root().create_child("events"), 2);
420 let vertex_tracker = tracker.for_vertex::<u64>();
421 vertex_tracker.record_removed("20");
422 vertex_tracker.record_removed("30");
423 vertex_tracker.record_removed("40");
424 assert_data_tree!(inspector, root: {
425 events: {
426 "1": {
427 "@time": AnyProperty,
428 event: "remove_vertex",
429 vertex_id: "30",
430 },
431 "2": {
432 "@time": AnyProperty,
433 event: "remove_vertex",
434 vertex_id: "40",
435 }
436 }
437 });
438 assert_eq!(tracker.shadow_buffer_len(), 2);
439 assert_eq!(get_time(&inspector, &["events", "1", "@time"]), tracker.shadow_time_of(0));
440 assert_eq!(get_time(&inspector, &["events", "2", "@time"]), tracker.shadow_time_of(1));
441 }
442}