Skip to main content

memscope_rs/view/
stats.rs

1//! View statistics.
2
3use crate::snapshot::MemorySnapshot;
4use serde::{Deserialize, Serialize};
5use std::collections::HashSet;
6
7/// View statistics.
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct ViewStats {
10    /// Total number of allocations
11    pub allocation_count: usize,
12    /// Total number of events
13    pub event_count: usize,
14    /// Total bytes currently allocated
15    pub total_bytes: usize,
16    /// Peak memory usage
17    pub peak_bytes: usize,
18    /// Number of threads
19    pub thread_count: usize,
20    /// Number of unique types
21    pub type_count: usize,
22    /// Number of deallocations
23    pub deallocation_count: usize,
24    /// Number of reallocations
25    pub reallocation_count: usize,
26}
27
28impl ViewStats {
29    /// Create stats from snapshot.
30    pub fn from_snapshot(snapshot: &MemorySnapshot) -> Self {
31        let types: HashSet<&str> = snapshot
32            .active_allocations
33            .values()
34            .filter_map(|a| a.type_name.as_deref())
35            .collect();
36
37        Self {
38            allocation_count: snapshot.stats.active_allocations,
39            event_count: 0, // Will be set separately if needed
40            total_bytes: snapshot.stats.current_memory,
41            peak_bytes: snapshot.stats.peak_memory,
42            thread_count: snapshot.thread_stats.len(),
43            type_count: types.len(),
44            deallocation_count: snapshot.stats.total_deallocations,
45            reallocation_count: snapshot.stats.total_reallocations,
46        }
47    }
48
49    /// Create stats from view.
50    pub fn from_view(view: &super::MemoryView) -> Self {
51        let mut stats = Self::from_snapshot(view.snapshot());
52        stats.event_count = view.events().len();
53        stats
54    }
55
56    /// Check if empty.
57    pub fn is_empty(&self) -> bool {
58        self.allocation_count == 0
59    }
60
61    /// Get average allocation size.
62    pub fn avg_allocation_size(&self) -> usize {
63        self.total_bytes
64            .checked_div(self.allocation_count)
65            .unwrap_or(0)
66    }
67
68    /// Get memory efficiency (peak vs current).
69    pub fn memory_efficiency(&self) -> f64 {
70        if self.peak_bytes == 0 {
71            1.0
72        } else {
73            self.total_bytes as f64 / self.peak_bytes as f64
74        }
75    }
76}
77
78impl std::fmt::Display for ViewStats {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        write!(
81            f,
82            "ViewStats {{ allocations: {}, events: {}, bytes: {}, peak: {}, threads: {}, types: {} }}",
83            self.allocation_count,
84            self.event_count,
85            self.total_bytes,
86            self.peak_bytes,
87            self.thread_count,
88            self.type_count
89        )
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::event_store::MemoryEvent;
97
98    #[test]
99    fn test_empty_stats() {
100        let view = super::super::MemoryView::from_events(vec![]);
101        let stats = ViewStats::from_view(&view);
102        assert!(stats.is_empty());
103        assert_eq!(stats.allocation_count, 0);
104    }
105
106    #[test]
107    fn test_stats_with_allocations() {
108        let events = vec![
109            MemoryEvent::allocate(0x1000, 64, 1),
110            MemoryEvent::allocate(0x2000, 128, 2),
111        ];
112        let view = super::super::MemoryView::from_events(events);
113        let stats = ViewStats::from_view(&view);
114        assert_eq!(stats.allocation_count, 2);
115        assert_eq!(stats.total_bytes, 192);
116        assert_eq!(stats.thread_count, 2);
117    }
118
119    #[test]
120    fn test_view_stats_default() {
121        let stats = ViewStats::default();
122        assert!(stats.is_empty());
123        assert_eq!(stats.allocation_count, 0);
124        assert_eq!(stats.event_count, 0);
125        assert_eq!(stats.total_bytes, 0);
126        assert_eq!(stats.peak_bytes, 0);
127        assert_eq!(stats.thread_count, 0);
128        assert_eq!(stats.type_count, 0);
129    }
130
131    #[test]
132    fn test_view_stats_clone() {
133        let stats = ViewStats {
134            allocation_count: 10,
135            event_count: 20,
136            total_bytes: 1024,
137            peak_bytes: 2048,
138            thread_count: 4,
139            type_count: 5,
140            deallocation_count: 2,
141            reallocation_count: 1,
142        };
143
144        let cloned = stats.clone();
145        assert_eq!(cloned.allocation_count, 10);
146        assert_eq!(cloned.event_count, 20);
147        assert_eq!(cloned.total_bytes, 1024);
148    }
149
150    #[test]
151    fn test_view_stats_debug() {
152        let stats = ViewStats {
153            allocation_count: 5,
154            event_count: 10,
155            total_bytes: 512,
156            peak_bytes: 1024,
157            thread_count: 2,
158            type_count: 3,
159            deallocation_count: 1,
160            reallocation_count: 0,
161        };
162
163        let debug_str = format!("{:?}", stats);
164        assert!(debug_str.contains("ViewStats"));
165        assert!(debug_str.contains("allocation_count"));
166    }
167
168    #[test]
169    fn test_view_stats_display() {
170        let stats = ViewStats {
171            allocation_count: 5,
172            event_count: 10,
173            total_bytes: 512,
174            peak_bytes: 1024,
175            thread_count: 2,
176            type_count: 3,
177            deallocation_count: 1,
178            reallocation_count: 0,
179        };
180
181        let display_str = format!("{}", stats);
182        assert!(display_str.contains("allocations: 5"));
183        assert!(display_str.contains("events: 10"));
184        assert!(display_str.contains("bytes: 512"));
185    }
186
187    #[test]
188    fn test_avg_allocation_size_zero() {
189        let stats = ViewStats::default();
190        assert_eq!(stats.avg_allocation_size(), 0);
191    }
192
193    #[test]
194    fn test_avg_allocation_size_nonzero() {
195        let stats = ViewStats {
196            allocation_count: 4,
197            total_bytes: 100,
198            ..Default::default()
199        };
200        assert_eq!(stats.avg_allocation_size(), 25);
201    }
202
203    #[test]
204    fn test_memory_efficiency_zero_peak() {
205        let stats = ViewStats {
206            peak_bytes: 0,
207            total_bytes: 100,
208            ..Default::default()
209        };
210        assert_eq!(stats.memory_efficiency(), 1.0);
211    }
212
213    #[test]
214    fn test_memory_efficiency_half() {
215        let stats = ViewStats {
216            peak_bytes: 100,
217            total_bytes: 50,
218            ..Default::default()
219        };
220        assert!((stats.memory_efficiency() - 0.5).abs() < 0.001);
221    }
222
223    #[test]
224    fn test_memory_efficiency_full() {
225        let stats = ViewStats {
226            peak_bytes: 100,
227            total_bytes: 100,
228            ..Default::default()
229        };
230        assert!((stats.memory_efficiency() - 1.0).abs() < 0.001);
231    }
232
233    #[test]
234    fn test_view_stats_serialization() {
235        let stats = ViewStats {
236            allocation_count: 10,
237            event_count: 20,
238            total_bytes: 1024,
239            peak_bytes: 2048,
240            thread_count: 4,
241            type_count: 5,
242            deallocation_count: 2,
243            reallocation_count: 1,
244        };
245
246        let json = serde_json::to_string(&stats);
247        assert!(json.is_ok());
248
249        let deserialized: Result<ViewStats, _> = serde_json::from_str(&json.unwrap());
250        assert!(deserialized.is_ok());
251    }
252
253    #[test]
254    fn test_from_snapshot() {
255        let events = vec![
256            MemoryEvent::allocate(0x1000, 64, 1),
257            MemoryEvent::allocate(0x2000, 128, 2),
258        ];
259        let view = super::super::MemoryView::from_events(events);
260        let stats = ViewStats::from_snapshot(view.snapshot());
261
262        assert_eq!(stats.allocation_count, 2);
263        assert_eq!(stats.total_bytes, 192);
264        assert_eq!(stats.event_count, 0); // from_snapshot doesn't set event_count
265    }
266
267    #[test]
268    fn test_from_view_event_count() {
269        let events = vec![
270            MemoryEvent::allocate(0x1000, 64, 1),
271            MemoryEvent::allocate(0x2000, 128, 2),
272            MemoryEvent::deallocate(0x1000, 64, 1),
273        ];
274        let view = super::super::MemoryView::from_events(events);
275        let stats = ViewStats::from_view(&view);
276
277        assert_eq!(stats.event_count, 3);
278    }
279}