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        if self.allocation_count == 0 {
64            0
65        } else {
66            self.total_bytes / self.allocation_count
67        }
68    }
69
70    /// Get memory efficiency (peak vs current).
71    pub fn memory_efficiency(&self) -> f64 {
72        if self.peak_bytes == 0 {
73            1.0
74        } else {
75            self.total_bytes as f64 / self.peak_bytes as f64
76        }
77    }
78}
79
80impl std::fmt::Display for ViewStats {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(
83            f,
84            "ViewStats {{ allocations: {}, events: {}, bytes: {}, peak: {}, threads: {}, types: {} }}",
85            self.allocation_count,
86            self.event_count,
87            self.total_bytes,
88            self.peak_bytes,
89            self.thread_count,
90            self.type_count
91        )
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use crate::event_store::MemoryEvent;
99
100    #[test]
101    fn test_empty_stats() {
102        let view = super::super::MemoryView::from_events(vec![]);
103        let stats = ViewStats::from_view(&view);
104        assert!(stats.is_empty());
105        assert_eq!(stats.allocation_count, 0);
106    }
107
108    #[test]
109    fn test_stats_with_allocations() {
110        let events = vec![
111            MemoryEvent::allocate(0x1000, 64, 1),
112            MemoryEvent::allocate(0x2000, 128, 2),
113        ];
114        let view = super::super::MemoryView::from_events(events);
115        let stats = ViewStats::from_view(&view);
116        assert_eq!(stats.allocation_count, 2);
117        assert_eq!(stats.total_bytes, 192);
118        assert_eq!(stats.thread_count, 2);
119    }
120
121    #[test]
122    fn test_view_stats_default() {
123        let stats = ViewStats::default();
124        assert!(stats.is_empty());
125        assert_eq!(stats.allocation_count, 0);
126        assert_eq!(stats.event_count, 0);
127        assert_eq!(stats.total_bytes, 0);
128        assert_eq!(stats.peak_bytes, 0);
129        assert_eq!(stats.thread_count, 0);
130        assert_eq!(stats.type_count, 0);
131    }
132
133    #[test]
134    fn test_view_stats_clone() {
135        let stats = ViewStats {
136            allocation_count: 10,
137            event_count: 20,
138            total_bytes: 1024,
139            peak_bytes: 2048,
140            thread_count: 4,
141            type_count: 5,
142            deallocation_count: 2,
143            reallocation_count: 1,
144        };
145
146        let cloned = stats.clone();
147        assert_eq!(cloned.allocation_count, 10);
148        assert_eq!(cloned.event_count, 20);
149        assert_eq!(cloned.total_bytes, 1024);
150    }
151
152    #[test]
153    fn test_view_stats_debug() {
154        let stats = ViewStats {
155            allocation_count: 5,
156            event_count: 10,
157            total_bytes: 512,
158            peak_bytes: 1024,
159            thread_count: 2,
160            type_count: 3,
161            deallocation_count: 1,
162            reallocation_count: 0,
163        };
164
165        let debug_str = format!("{:?}", stats);
166        assert!(debug_str.contains("ViewStats"));
167        assert!(debug_str.contains("allocation_count"));
168    }
169
170    #[test]
171    fn test_view_stats_display() {
172        let stats = ViewStats {
173            allocation_count: 5,
174            event_count: 10,
175            total_bytes: 512,
176            peak_bytes: 1024,
177            thread_count: 2,
178            type_count: 3,
179            deallocation_count: 1,
180            reallocation_count: 0,
181        };
182
183        let display_str = format!("{}", stats);
184        assert!(display_str.contains("allocations: 5"));
185        assert!(display_str.contains("events: 10"));
186        assert!(display_str.contains("bytes: 512"));
187    }
188
189    #[test]
190    fn test_avg_allocation_size_zero() {
191        let stats = ViewStats::default();
192        assert_eq!(stats.avg_allocation_size(), 0);
193    }
194
195    #[test]
196    fn test_avg_allocation_size_nonzero() {
197        let stats = ViewStats {
198            allocation_count: 4,
199            total_bytes: 100,
200            ..Default::default()
201        };
202        assert_eq!(stats.avg_allocation_size(), 25);
203    }
204
205    #[test]
206    fn test_memory_efficiency_zero_peak() {
207        let stats = ViewStats {
208            peak_bytes: 0,
209            total_bytes: 100,
210            ..Default::default()
211        };
212        assert_eq!(stats.memory_efficiency(), 1.0);
213    }
214
215    #[test]
216    fn test_memory_efficiency_half() {
217        let stats = ViewStats {
218            peak_bytes: 100,
219            total_bytes: 50,
220            ..Default::default()
221        };
222        assert!((stats.memory_efficiency() - 0.5).abs() < 0.001);
223    }
224
225    #[test]
226    fn test_memory_efficiency_full() {
227        let stats = ViewStats {
228            peak_bytes: 100,
229            total_bytes: 100,
230            ..Default::default()
231        };
232        assert!((stats.memory_efficiency() - 1.0).abs() < 0.001);
233    }
234
235    #[test]
236    fn test_view_stats_serialization() {
237        let stats = ViewStats {
238            allocation_count: 10,
239            event_count: 20,
240            total_bytes: 1024,
241            peak_bytes: 2048,
242            thread_count: 4,
243            type_count: 5,
244            deallocation_count: 2,
245            reallocation_count: 1,
246        };
247
248        let json = serde_json::to_string(&stats);
249        assert!(json.is_ok());
250
251        let deserialized: Result<ViewStats, _> = serde_json::from_str(&json.unwrap());
252        assert!(deserialized.is_ok());
253    }
254
255    #[test]
256    fn test_from_snapshot() {
257        let events = vec![
258            MemoryEvent::allocate(0x1000, 64, 1),
259            MemoryEvent::allocate(0x2000, 128, 2),
260        ];
261        let view = super::super::MemoryView::from_events(events);
262        let stats = ViewStats::from_snapshot(view.snapshot());
263
264        assert_eq!(stats.allocation_count, 2);
265        assert_eq!(stats.total_bytes, 192);
266        assert_eq!(stats.event_count, 0); // from_snapshot doesn't set event_count
267    }
268
269    #[test]
270    fn test_from_view_event_count() {
271        let events = vec![
272            MemoryEvent::allocate(0x1000, 64, 1),
273            MemoryEvent::allocate(0x2000, 128, 2),
274            MemoryEvent::deallocate(0x1000, 64, 1),
275        ];
276        let view = super::super::MemoryView::from_events(events);
277        let stats = ViewStats::from_view(&view);
278
279        assert_eq!(stats.event_count, 3);
280    }
281}