Skip to main content

memscope_rs/view/
memory_view.rs

1//! MemoryView - Unified read-only access to memory data.
2
3use crate::event_store::MemoryEvent;
4use crate::snapshot::{build_snapshot_from_events, ActiveAllocation, MemorySnapshot};
5use std::sync::Arc;
6
7use super::{FilterBuilder, ViewStats};
8
9/// Memory view - unified read-only access to memory data.
10///
11/// This is the single source of truth for all analysis modules.
12/// Reuses MemorySnapshot to avoid duplicate allocation rebuilding.
13#[derive(Clone)]
14pub struct MemoryView {
15    snapshot: MemorySnapshot,
16    events: Arc<[MemoryEvent]>,
17}
18
19impl MemoryView {
20    /// Create view from snapshot and events.
21    pub fn new(snapshot: MemorySnapshot, events: Vec<MemoryEvent>) -> Self {
22        Self {
23            snapshot,
24            events: events.into(),
25        }
26    }
27
28    /// Create view from GlobalTracker.
29    pub fn from_tracker(
30        tracker: &crate::capture::backends::global_tracking::GlobalTracker,
31    ) -> Self {
32        let events = tracker.tracker().events();
33        let snapshot = build_snapshot_from_events(&events);
34        Self::new(snapshot, events)
35    }
36
37    /// Create view from events directly.
38    pub fn from_events(events: Vec<MemoryEvent>) -> Self {
39        let snapshot = build_snapshot_from_events(&events);
40        Self::new(snapshot, events)
41    }
42
43    /// Get all active allocations (from snapshot).
44    pub fn allocations(&self) -> Vec<&ActiveAllocation> {
45        self.snapshot.active_allocations.values().collect()
46    }
47
48    /// Get allocation by pointer.
49    pub fn get_allocation(&self, ptr: usize) -> Option<&ActiveAllocation> {
50        self.snapshot.active_allocations.get(&ptr)
51    }
52
53    /// Get all events.
54    pub fn events(&self) -> &[MemoryEvent] {
55        &self.events
56    }
57
58    /// Get underlying snapshot.
59    pub fn snapshot(&self) -> &MemorySnapshot {
60        &self.snapshot
61    }
62
63    /// Get memory stats.
64    pub fn stats(&self) -> ViewStats {
65        ViewStats::from_snapshot(&self.snapshot)
66    }
67
68    /// Create filter builder.
69    pub fn filter(&self) -> FilterBuilder<'_> {
70        FilterBuilder::new(self)
71    }
72
73    /// Get allocation count.
74    pub fn len(&self) -> usize {
75        self.snapshot.active_allocations.len()
76    }
77
78    /// Check if empty.
79    pub fn is_empty(&self) -> bool {
80        self.snapshot.active_allocations.is_empty()
81    }
82
83    /// Get total memory usage.
84    pub fn total_memory(&self) -> usize {
85        self.snapshot.stats.current_memory
86    }
87
88    /// Get thread IDs.
89    pub fn thread_ids(&self) -> Vec<u64> {
90        self.snapshot.thread_stats.keys().copied().collect()
91    }
92}
93
94impl Default for MemoryView {
95    fn default() -> Self {
96        Self::from_events(vec![])
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_empty_view() {
106        let view = MemoryView::from_events(vec![]);
107        assert!(view.is_empty());
108        assert_eq!(view.len(), 0);
109    }
110
111    #[test]
112    fn test_single_allocation() {
113        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
114        let view = MemoryView::from_events(events);
115        assert_eq!(view.len(), 1);
116        assert_eq!(view.total_memory(), 64);
117    }
118
119    #[test]
120    fn test_allocation_and_deallocation() {
121        let events = vec![
122            MemoryEvent::allocate(0x1000, 64, 1),
123            MemoryEvent::deallocate(0x1000, 64, 1),
124        ];
125        let view = MemoryView::from_events(events);
126        assert!(view.is_empty());
127        assert_eq!(view.total_memory(), 0);
128    }
129
130    #[test]
131    fn test_default_view() {
132        let view = MemoryView::default();
133        assert!(view.is_empty());
134        assert_eq!(view.len(), 0);
135        assert_eq!(view.total_memory(), 0);
136    }
137
138    #[test]
139    fn test_get_allocation_existing() {
140        let events = vec![MemoryEvent::allocate(0x1000, 128, 1)];
141        let view = MemoryView::from_events(events);
142
143        let alloc = view.get_allocation(0x1000);
144        assert!(alloc.is_some());
145        assert_eq!(alloc.unwrap().size, 128);
146    }
147
148    #[test]
149    fn test_get_allocation_not_found() {
150        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
151        let view = MemoryView::from_events(events);
152
153        let alloc = view.get_allocation(0x2000);
154        assert!(alloc.is_none());
155    }
156
157    #[test]
158    fn test_allocations_multiple() {
159        let events = vec![
160            MemoryEvent::allocate(0x1000, 64, 1),
161            MemoryEvent::allocate(0x2000, 128, 1),
162            MemoryEvent::allocate(0x3000, 256, 1),
163        ];
164        let view = MemoryView::from_events(events);
165
166        let allocs = view.allocations();
167        assert_eq!(allocs.len(), 3);
168    }
169
170    #[test]
171    fn test_events_access() {
172        let events = vec![
173            MemoryEvent::allocate(0x1000, 64, 1),
174            MemoryEvent::deallocate(0x1000, 64, 1),
175        ];
176        let view = MemoryView::from_events(events);
177
178        let view_events = view.events();
179        assert_eq!(view_events.len(), 2);
180    }
181
182    #[test]
183    fn test_snapshot_access() {
184        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
185        let view = MemoryView::from_events(events);
186
187        let snapshot = view.snapshot();
188        assert_eq!(snapshot.active_allocations.len(), 1);
189    }
190
191    #[test]
192    fn test_stats() {
193        let events = vec![
194            MemoryEvent::allocate(0x1000, 64, 1),
195            MemoryEvent::allocate(0x2000, 128, 1),
196        ];
197        let view = MemoryView::from_events(events);
198
199        let stats = view.stats();
200        assert_eq!(stats.allocation_count, 2);
201        assert_eq!(stats.total_bytes, 192);
202    }
203
204    #[test]
205    fn test_thread_ids() {
206        let events = vec![
207            MemoryEvent::allocate(0x1000, 64, 1),
208            MemoryEvent::allocate(0x2000, 128, 2),
209            MemoryEvent::allocate(0x3000, 256, 1),
210        ];
211        let view = MemoryView::from_events(events);
212
213        let threads = view.thread_ids();
214        assert_eq!(threads.len(), 2);
215        assert!(threads.contains(&1));
216        assert!(threads.contains(&2));
217    }
218
219    #[test]
220    fn test_filter_builder() {
221        let events = vec![
222            MemoryEvent::allocate(0x1000, 64, 1),
223            MemoryEvent::allocate(0x2000, 128, 2),
224        ];
225        let view = MemoryView::from_events(events);
226
227        let _filter = view.filter();
228    }
229
230    #[test]
231    fn test_clone() {
232        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
233        let view = MemoryView::from_events(events);
234
235        let cloned = view.clone();
236        assert_eq!(cloned.len(), 1);
237        assert_eq!(cloned.total_memory(), 64);
238    }
239
240    #[test]
241    fn test_new_constructor() {
242        let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
243        let snapshot = build_snapshot_from_events(&events);
244        let view = MemoryView::new(snapshot, events);
245
246        assert_eq!(view.len(), 1);
247    }
248
249    #[test]
250    fn test_multiple_allocations_same_thread() {
251        let events = vec![
252            MemoryEvent::allocate(0x1000, 64, 1),
253            MemoryEvent::allocate(0x2000, 128, 1),
254            MemoryEvent::allocate(0x3000, 256, 1),
255        ];
256        let view = MemoryView::from_events(events);
257
258        assert_eq!(view.len(), 3);
259        assert_eq!(view.total_memory(), 448);
260
261        let threads = view.thread_ids();
262        assert_eq!(threads.len(), 1);
263    }
264
265    #[test]
266    fn test_partial_deallocation() {
267        let events = vec![
268            MemoryEvent::allocate(0x1000, 64, 1),
269            MemoryEvent::allocate(0x2000, 128, 1),
270            MemoryEvent::deallocate(0x1000, 64, 1),
271        ];
272        let view = MemoryView::from_events(events);
273
274        assert_eq!(view.len(), 1);
275        assert_eq!(view.total_memory(), 128);
276    }
277}