Skip to main content

memscope_rs/snapshot/
engine.rs

1//! Snapshot Engine - Snapshot construction and aggregation
2//!
3//! This module provides the SnapshotEngine which is responsible for
4//! building memory snapshots from event data.
5
6use crate::event_store::{MemoryEvent, SharedEventStore};
7use crate::snapshot::build_snapshot_from_events;
8use crate::snapshot::types::MemorySnapshot;
9
10/// Snapshot Engine - Builds memory snapshots from event data
11///
12/// The SnapshotEngine is responsible for constructing point-in-time
13/// views of memory usage from the events stored in the EventStore.
14///
15/// Key properties:
16/// - Read-only: Does not consume events from EventStore
17/// - Efficient: Optimized for fast snapshot construction
18/// - Comprehensive: Captures all relevant memory state
19pub struct SnapshotEngine {
20    /// Reference to the event store
21    event_store: SharedEventStore,
22}
23
24impl SnapshotEngine {
25    /// Create a new SnapshotEngine
26    pub fn new(event_store: SharedEventStore) -> Self {
27        Self { event_store }
28    }
29
30    /// Build a snapshot from the current event store state
31    ///
32    /// This method reads all events from the event store and
33    /// constructs a snapshot representing the current memory state.
34    pub fn build_snapshot(&self) -> MemorySnapshot {
35        let events = self.event_store.snapshot();
36        build_snapshot_from_events(&events)
37    }
38
39    /// Build a snapshot from a specific set of events
40    ///
41    /// This method accepts a slice reference to avoid unnecessary cloning.
42    /// The caller retains ownership of the events vector.
43    ///
44    /// # Arguments
45    /// * `events` - A slice of events to build the snapshot from
46    ///
47    /// # Example
48    /// ```ignore
49    /// let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
50    /// let snapshot = engine.build_snapshot_from_events(&events);
51    /// // events is still available here
52    /// ```
53    pub fn build_snapshot_from_events(&self, events: &[MemoryEvent]) -> MemorySnapshot {
54        build_snapshot_from_events(events)
55    }
56
57    /// Get the event store reference
58    pub fn event_store(&self) -> &SharedEventStore {
59        &self.event_store
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::event_store::EventStore;
67    use std::sync::Arc;
68
69    #[test]
70    fn test_snapshot_engine_creation() {
71        let event_store = Arc::new(EventStore::new());
72        let engine = SnapshotEngine::new(event_store);
73        let snapshot = engine.build_snapshot();
74        assert_eq!(snapshot.active_count(), 0);
75    }
76
77    #[test]
78    fn test_snapshot_with_allocations() {
79        let event_store = Arc::new(EventStore::new());
80        event_store.record(MemoryEvent::allocate(0x1000, 1024, 1));
81        event_store.record(MemoryEvent::allocate(0x2000, 2048, 1));
82
83        let engine = SnapshotEngine::new(event_store);
84        let snapshot = engine.build_snapshot();
85
86        assert_eq!(snapshot.active_count(), 2);
87        assert_eq!(snapshot.current_memory(), 3072);
88    }
89
90    #[test]
91    fn test_snapshot_with_deallocations() {
92        let event_store = Arc::new(EventStore::new());
93        event_store.record(MemoryEvent::allocate(0x1000, 1024, 1));
94        event_store.record(MemoryEvent::deallocate(0x1000, 1024, 1));
95
96        let engine = SnapshotEngine::new(event_store);
97        let snapshot = engine.build_snapshot();
98
99        assert_eq!(snapshot.active_count(), 0);
100        assert_eq!(snapshot.current_memory(), 0);
101        assert_eq!(snapshot.stats.total_allocations, 1);
102        assert_eq!(snapshot.stats.total_deallocations, 1);
103    }
104
105    #[test]
106    fn test_snapshot_peak_memory() {
107        let event_store = Arc::new(EventStore::new());
108        event_store.record(MemoryEvent::allocate(0x1000, 1024, 1));
109        event_store.record(MemoryEvent::allocate(0x2000, 2048, 1));
110        event_store.record(MemoryEvent::deallocate(0x2000, 2048, 1));
111
112        let engine = SnapshotEngine::new(event_store);
113        let snapshot = engine.build_snapshot();
114
115        assert_eq!(snapshot.peak_memory(), 3072);
116        assert_eq!(snapshot.current_memory(), 1024);
117    }
118
119    #[test]
120    fn test_snapshot_thread_stats() {
121        let event_store = Arc::new(EventStore::new());
122        event_store.record(MemoryEvent::allocate(0x1000, 1024, 1));
123        event_store.record(MemoryEvent::allocate(0x2000, 2048, 2));
124
125        let engine = SnapshotEngine::new(event_store);
126        let snapshot = engine.build_snapshot();
127
128        assert_eq!(snapshot.thread_stats.len(), 2);
129
130        let thread1 = snapshot
131            .thread_stats
132            .get(&1)
133            .expect("Thread 1 stats should exist");
134        assert_eq!(thread1.allocation_count, 1);
135        assert_eq!(thread1.total_allocated, 1024);
136
137        let thread2 = snapshot
138            .thread_stats
139            .get(&2)
140            .expect("Thread 2 stats should exist");
141        assert_eq!(thread2.allocation_count, 1);
142        assert_eq!(thread2.total_allocated, 2048);
143    }
144
145    #[test]
146    fn test_deallocation_underflow_protection() {
147        let event_store = Arc::new(EventStore::new());
148        event_store.record(MemoryEvent::allocate(0x1000, 1024, 1));
149        event_store.record(MemoryEvent::deallocate(0x1000, 2048, 1));
150
151        let engine = SnapshotEngine::new(event_store);
152        let snapshot = engine.build_snapshot();
153
154        assert_eq!(snapshot.current_memory(), 0);
155        let thread1 = snapshot
156            .thread_stats
157            .get(&1)
158            .expect("Thread 1 stats should exist");
159        assert_eq!(thread1.current_memory, 0);
160    }
161
162    #[test]
163    fn test_build_snapshot_from_events_slice() {
164        let event_store = Arc::new(EventStore::new());
165        let engine = SnapshotEngine::new(event_store);
166
167        let events = vec![
168            MemoryEvent::allocate(0x1000, 1024, 1),
169            MemoryEvent::allocate(0x2000, 2048, 1),
170        ];
171
172        let snapshot = engine.build_snapshot_from_events(&events);
173
174        // events is still available after the call
175        assert_eq!(events.len(), 2);
176        assert_eq!(snapshot.active_count(), 2);
177    }
178}