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