Skip to main content

memscope_rs/capture/types/
stats.rs

1//! Memory statistics types.
2//!
3//! This module contains types for tracking memory usage statistics,
4//! including allocation counts, memory sizes, and fragmentation analysis.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use super::allocation::AllocationInfo;
10use super::scope::ScopeLifecycleMetrics;
11
12/// Memory statistics.
13///
14/// Comprehensive statistics about memory allocations, deallocations,
15/// leaks, and fragmentation.
16#[derive(Debug, Clone, Default, Serialize)]
17pub struct MemoryStats {
18    /// Total number of allocations made.
19    pub total_allocations: usize,
20    /// Total bytes allocated.
21    pub total_allocated: usize,
22    /// Number of currently active allocations.
23    pub active_allocations: usize,
24    /// Total bytes in active allocations.
25    pub active_memory: usize,
26    /// Peak number of concurrent allocations.
27    pub peak_allocations: usize,
28    /// Peak memory usage in bytes.
29    pub peak_memory: usize,
30    /// Total number of deallocations performed.
31    pub total_deallocations: usize,
32    /// Total bytes deallocated.
33    pub total_deallocated: usize,
34    /// Number of leaked allocations.
35    pub leaked_allocations: usize,
36    /// Total bytes in leaked allocations.
37    pub leaked_memory: usize,
38    /// Analysis of memory fragmentation.
39    pub fragmentation_analysis: FragmentationAnalysis,
40    /// Lifecycle statistics for scopes.
41    pub lifecycle_stats: ScopeLifecycleMetrics,
42    /// List of all allocation information.
43    pub allocations: Vec<AllocationInfo>,
44    /// Statistics for system library allocations.
45    pub system_library_stats: SystemLibraryStats,
46    /// Analysis of concurrent memory operations.
47    pub concurrency_analysis: ConcurrencyAnalysis,
48}
49
50impl MemoryStats {
51    /// Create a new empty MemoryStats.
52    pub fn new() -> Self {
53        Self {
54            total_allocations: 0,
55            total_allocated: 0,
56            active_allocations: 0,
57            active_memory: 0,
58            peak_allocations: 0,
59            peak_memory: 0,
60            total_deallocations: 0,
61            total_deallocated: 0,
62            leaked_allocations: 0,
63            leaked_memory: 0,
64            fragmentation_analysis: FragmentationAnalysis::default(),
65            lifecycle_stats: ScopeLifecycleMetrics::default(),
66            allocations: Vec::new(),
67            system_library_stats: SystemLibraryStats::default(),
68            concurrency_analysis: ConcurrencyAnalysis::default(),
69        }
70    }
71}
72
73impl From<crate::core::types::MemoryStats> for MemoryStats {
74    fn from(old: crate::core::types::MemoryStats) -> Self {
75        Self {
76            total_allocations: old.total_allocations,
77            total_allocated: old.total_allocated,
78            active_allocations: old.active_allocations,
79            active_memory: old.active_memory,
80            peak_allocations: old.peak_allocations,
81            peak_memory: old.peak_memory,
82            total_deallocations: old.total_deallocations,
83            total_deallocated: old.total_deallocated,
84            leaked_allocations: old.leaked_allocations,
85            leaked_memory: old.leaked_memory,
86            // Convert FragmentationAnalysis
87            fragmentation_analysis: FragmentationAnalysis {
88                fragmentation_ratio: old.fragmentation_analysis.fragmentation_ratio,
89                largest_free_block: old.fragmentation_analysis.largest_free_block,
90                smallest_free_block: old.fragmentation_analysis.smallest_free_block,
91                free_block_count: old.fragmentation_analysis.free_block_count,
92                total_free_memory: old.fragmentation_analysis.total_free_memory,
93                external_fragmentation: old.fragmentation_analysis.external_fragmentation,
94                internal_fragmentation: old.fragmentation_analysis.internal_fragmentation,
95            },
96            // Use default values for complex nested types to avoid unsafe conversions
97            lifecycle_stats: ScopeLifecycleMetrics::default(),
98            allocations: old.allocations.into_iter().map(|a| a.into()).collect(),
99            system_library_stats: SystemLibraryStats::default(),
100            concurrency_analysis: ConcurrencyAnalysis::default(),
101        }
102    }
103}
104
105/// Memory type analysis.
106#[derive(Debug, Clone, Serialize)]
107pub struct MemoryTypeInfo {
108    /// Name of the memory type.
109    pub type_name: String,
110    /// Total size in bytes for this type.
111    pub total_size: usize,
112    /// Number of allocations of this type.
113    pub allocation_count: usize,
114    /// Average size of allocations for this type.
115    pub average_size: usize,
116    /// Size of the largest allocation for this type.
117    pub largest_allocation: usize,
118    /// Size of the smallest allocation for this type.
119    pub smallest_allocation: usize,
120    /// Number of currently active instances.
121    pub active_instances: usize,
122    /// Number of leaked instances.
123    pub leaked_instances: usize,
124}
125
126/// Type memory usage information.
127#[derive(Debug, Clone, Serialize)]
128pub struct TypeMemoryUsage {
129    /// Name of the type.
130    pub type_name: String,
131    /// Total size allocated for this type.
132    pub total_size: usize,
133    /// Number of allocations for this type.
134    pub allocation_count: usize,
135    /// Average allocation size for this type.
136    pub average_size: f64,
137    /// Peak memory usage for this type.
138    pub peak_size: usize,
139    /// Current memory usage for this type.
140    pub current_size: usize,
141    /// Memory efficiency score for this type.
142    pub efficiency_score: f64,
143}
144
145/// Fragmentation analysis.
146#[derive(Debug, Clone, Default, Serialize, Deserialize)]
147pub struct FragmentationAnalysis {
148    /// Ratio of fragmented to total memory.
149    pub fragmentation_ratio: f64,
150    /// Size of the largest free memory block.
151    pub largest_free_block: usize,
152    /// Size of the smallest free memory block.
153    pub smallest_free_block: usize,
154    /// Total number of free memory blocks.
155    pub free_block_count: usize,
156    /// Total amount of free memory.
157    pub total_free_memory: usize,
158    /// External fragmentation percentage.
159    pub external_fragmentation: f64,
160    /// Internal fragmentation percentage.
161    pub internal_fragmentation: f64,
162}
163
164/// System library usage statistics.
165#[derive(Debug, Clone, Default, Serialize)]
166pub struct SystemLibraryStats {
167    /// Usage statistics for standard collections.
168    pub std_collections: LibraryUsage,
169    /// Usage statistics for async runtime.
170    pub async_runtime: LibraryUsage,
171    /// Usage statistics for network I/O.
172    pub network_io: LibraryUsage,
173    /// Usage statistics for file system operations.
174    pub file_system: LibraryUsage,
175    /// Usage statistics for serialization.
176    pub serialization: LibraryUsage,
177    /// Usage statistics for regex operations.
178    pub regex_engine: LibraryUsage,
179    /// Usage statistics for cryptographic operations.
180    pub crypto_security: LibraryUsage,
181    /// Usage statistics for database operations.
182    pub database: LibraryUsage,
183    /// Usage statistics for graphics and UI.
184    pub graphics_ui: LibraryUsage,
185    /// Usage statistics for HTTP operations.
186    pub http_stack: LibraryUsage,
187}
188
189/// Library usage information.
190#[derive(Debug, Clone, Default, Serialize)]
191pub struct LibraryUsage {
192    /// Number of allocations.
193    pub allocation_count: usize,
194    /// Total bytes allocated.
195    pub total_bytes: usize,
196    /// Peak memory usage in bytes.
197    pub peak_bytes: usize,
198    /// Average allocation size.
199    pub average_size: f64,
200    /// Categorized usage statistics.
201    pub categories: HashMap<String, usize>,
202    /// Functions with high allocation activity.
203    pub hotspot_functions: Vec<String>,
204}
205
206/// Concurrency safety analysis.
207#[derive(Debug, Clone, Default, Serialize)]
208pub struct ConcurrencyAnalysis {
209    /// Thread Safety Allocations.
210    pub thread_safety_allocations: usize,
211    /// Shared Memory Bytes.
212    pub shared_memory_bytes: usize,
213    /// Mutex Protected.
214    pub mutex_protected: usize,
215    /// Arc Shared.
216    pub arc_shared: usize,
217    /// Rc Shared.
218    pub rc_shared: usize,
219    /// Channel Buffers.
220    pub channel_buffers: usize,
221    /// Thread Local Storage.
222    pub thread_local_storage: usize,
223    /// Atomic Operations.
224    pub atomic_operations: usize,
225    /// Lock Contention Risk.
226    pub lock_contention_risk: String,
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn test_memory_stats_creation() {
235        let stats = MemoryStats::new();
236
237        assert_eq!(stats.total_allocations, 0);
238        assert_eq!(stats.total_allocated, 0);
239        assert_eq!(stats.active_allocations, 0);
240    }
241
242    #[test]
243    fn test_fragmentation_analysis_default() {
244        let frag = FragmentationAnalysis::default();
245
246        assert_eq!(frag.fragmentation_ratio, 0.0);
247        assert_eq!(frag.largest_free_block, 0);
248        assert_eq!(frag.free_block_count, 0);
249    }
250
251    #[test]
252    fn test_system_library_stats_default() {
253        let stats = SystemLibraryStats::default();
254
255        assert_eq!(stats.std_collections.allocation_count, 0);
256        assert_eq!(stats.async_runtime.total_bytes, 0);
257        assert_eq!(stats.network_io.peak_bytes, 0);
258    }
259
260    #[test]
261    fn test_library_usage_default() {
262        let usage = LibraryUsage::default();
263
264        assert_eq!(usage.allocation_count, 0);
265        assert_eq!(usage.total_bytes, 0);
266        assert_eq!(usage.average_size, 0.0);
267        assert!(usage.categories.is_empty());
268        assert!(usage.hotspot_functions.is_empty());
269    }
270
271    #[test]
272    fn test_concurrency_analysis_default() {
273        let analysis = ConcurrencyAnalysis::default();
274
275        assert_eq!(analysis.thread_safety_allocations, 0);
276        assert_eq!(analysis.shared_memory_bytes, 0);
277        assert_eq!(analysis.mutex_protected, 0);
278    }
279
280    #[test]
281    fn test_memory_stats_default() {
282        let stats = MemoryStats::default();
283
284        assert_eq!(stats.total_allocations, 0);
285        assert_eq!(stats.active_memory, 0);
286        assert_eq!(stats.peak_memory, 0);
287        assert_eq!(stats.leaked_allocations, 0);
288    }
289
290    #[test]
291    fn test_memory_stats_with_values() {
292        let mut stats = MemoryStats::new();
293        stats.total_allocations = 100;
294        stats.total_allocated = 1024 * 1024;
295        stats.active_allocations = 50;
296        stats.active_memory = 512 * 1024;
297        stats.peak_allocations = 75;
298        stats.peak_memory = 768 * 1024;
299        stats.total_deallocations = 50;
300        stats.total_deallocated = 512 * 1024;
301        stats.leaked_allocations = 5;
302        stats.leaked_memory = 10240;
303
304        assert_eq!(stats.total_allocations, 100);
305        assert_eq!(stats.active_allocations, 50);
306        assert_eq!(stats.leaked_allocations, 5);
307    }
308
309    #[test]
310    fn test_fragmentation_analysis_with_values() {
311        let frag = FragmentationAnalysis {
312            fragmentation_ratio: 0.35,
313            largest_free_block: 65536,
314            smallest_free_block: 16,
315            free_block_count: 128,
316            total_free_memory: 524288,
317            external_fragmentation: 0.25,
318            internal_fragmentation: 0.10,
319        };
320
321        assert!((frag.fragmentation_ratio - 0.35).abs() < f64::EPSILON);
322        assert_eq!(frag.free_block_count, 128);
323        assert!((frag.external_fragmentation - 0.25).abs() < f64::EPSILON);
324    }
325
326    #[test]
327    fn test_memory_type_info_creation() {
328        let info = MemoryTypeInfo {
329            type_name: "Vec<u8>".to_string(),
330            total_size: 1024,
331            allocation_count: 10,
332            average_size: 102,
333            largest_allocation: 512,
334            smallest_allocation: 16,
335            active_instances: 8,
336            leaked_instances: 1,
337        };
338
339        assert_eq!(info.type_name, "Vec<u8>");
340        assert_eq!(info.allocation_count, 10);
341        assert_eq!(info.leaked_instances, 1);
342    }
343
344    #[test]
345    fn test_type_memory_usage_creation() {
346        let usage = TypeMemoryUsage {
347            type_name: "String".to_string(),
348            total_size: 2048,
349            allocation_count: 20,
350            average_size: 102.4,
351            peak_size: 512,
352            current_size: 256,
353            efficiency_score: 0.85,
354        };
355
356        assert_eq!(usage.type_name, "String");
357        assert!((usage.average_size - 102.4).abs() < f64::EPSILON);
358        assert!((usage.efficiency_score - 0.85).abs() < f64::EPSILON);
359    }
360
361    #[test]
362    fn test_library_usage_with_values() {
363        let mut categories = HashMap::new();
364        categories.insert("HashMap".to_string(), 1000);
365        categories.insert("Vec".to_string(), 2000);
366
367        let usage = LibraryUsage {
368            allocation_count: 100,
369            total_bytes: 10240,
370            peak_bytes: 5120,
371            average_size: 102.4,
372            categories,
373            hotspot_functions: vec!["push".to_string(), "insert".to_string()],
374        };
375
376        assert_eq!(usage.allocation_count, 100);
377        assert_eq!(usage.categories.len(), 2);
378        assert_eq!(usage.hotspot_functions.len(), 2);
379    }
380
381    #[test]
382    fn test_system_library_stats_with_values() {
383        let mut stats = SystemLibraryStats::default();
384        stats.std_collections.allocation_count = 500;
385        stats.async_runtime.total_bytes = 10240;
386        stats.network_io.peak_bytes = 2048;
387        stats.file_system.allocation_count = 100;
388        stats.serialization.total_bytes = 4096;
389        stats.regex_engine.allocation_count = 50;
390        stats.crypto_security.total_bytes = 8192;
391        stats.database.allocation_count = 200;
392        stats.graphics_ui.total_bytes = 16384;
393        stats.http_stack.allocation_count = 75;
394
395        assert_eq!(stats.std_collections.allocation_count, 500);
396        assert_eq!(stats.async_runtime.total_bytes, 10240);
397    }
398
399    #[test]
400    fn test_concurrency_analysis_with_values() {
401        let analysis = ConcurrencyAnalysis {
402            thread_safety_allocations: 100,
403            shared_memory_bytes: 10240,
404            mutex_protected: 50,
405            arc_shared: 30,
406            rc_shared: 20,
407            channel_buffers: 15,
408            thread_local_storage: 10,
409            atomic_operations: 200,
410            lock_contention_risk: "Low".to_string(),
411        };
412
413        assert_eq!(analysis.thread_safety_allocations, 100);
414        assert_eq!(analysis.arc_shared, 30);
415        assert_eq!(analysis.lock_contention_risk, "Low");
416    }
417
418    #[test]
419    fn test_memory_stats_serialization() {
420        let stats = MemoryStats::new();
421
422        let json = serde_json::to_string(&stats).unwrap();
423        assert!(json.contains("total_allocations"));
424        assert!(json.contains("active_memory"));
425    }
426
427    #[test]
428    fn test_fragmentation_analysis_serialization() {
429        let frag = FragmentationAnalysis {
430            fragmentation_ratio: 0.5,
431            largest_free_block: 1024,
432            smallest_free_block: 16,
433            free_block_count: 50,
434            total_free_memory: 2048,
435            external_fragmentation: 0.3,
436            internal_fragmentation: 0.2,
437        };
438
439        let json = serde_json::to_string(&frag).unwrap();
440        let deserialized: FragmentationAnalysis = serde_json::from_str(&json).unwrap();
441        assert!((deserialized.fragmentation_ratio - frag.fragmentation_ratio).abs() < f64::EPSILON);
442    }
443
444    #[test]
445    fn test_memory_type_info_serialization() {
446        let info = MemoryTypeInfo {
447            type_name: "HashMap<String, i32>".to_string(),
448            total_size: 4096,
449            allocation_count: 25,
450            average_size: 163,
451            largest_allocation: 1024,
452            smallest_allocation: 64,
453            active_instances: 20,
454            leaked_instances: 2,
455        };
456
457        let json = serde_json::to_string(&info).unwrap();
458        assert!(json.contains("HashMap"));
459        assert!(json.contains("4096"));
460    }
461
462    #[test]
463    fn test_type_memory_usage_serialization() {
464        let usage = TypeMemoryUsage {
465            type_name: "Box<dyn Any>".to_string(),
466            total_size: 8192,
467            allocation_count: 100,
468            average_size: 81.92,
469            peak_size: 2048,
470            current_size: 1024,
471            efficiency_score: 0.75,
472        };
473
474        let json = serde_json::to_string(&usage).unwrap();
475        assert!(json.contains("Box"));
476        assert!(json.contains("8192"));
477    }
478
479    #[test]
480    fn test_library_usage_serialization() {
481        let usage = LibraryUsage {
482            allocation_count: 50,
483            total_bytes: 5120,
484            peak_bytes: 2560,
485            average_size: 102.4,
486            categories: HashMap::new(),
487            hotspot_functions: vec![],
488        };
489
490        let json = serde_json::to_string(&usage).unwrap();
491        assert!(json.contains("allocation_count"));
492    }
493
494    #[test]
495    fn test_concurrency_analysis_serialization() {
496        let analysis = ConcurrencyAnalysis {
497            thread_safety_allocations: 10,
498            shared_memory_bytes: 1024,
499            mutex_protected: 5,
500            arc_shared: 3,
501            rc_shared: 2,
502            channel_buffers: 1,
503            thread_local_storage: 0,
504            atomic_operations: 50,
505            lock_contention_risk: "Medium".to_string(),
506        };
507
508        let json = serde_json::to_string(&analysis).unwrap();
509        assert!(json.contains("thread_safety_allocations"));
510    }
511
512    #[test]
513    fn test_boundary_values_memory_stats() {
514        let mut stats = MemoryStats::new();
515        stats.total_allocations = usize::MAX;
516        stats.total_allocated = usize::MAX;
517        stats.active_allocations = usize::MAX;
518        stats.active_memory = usize::MAX;
519        stats.peak_allocations = usize::MAX;
520        stats.peak_memory = usize::MAX;
521
522        assert_eq!(stats.total_allocations, usize::MAX);
523        assert_eq!(stats.peak_memory, usize::MAX);
524    }
525
526    #[test]
527    fn test_boundary_values_fragmentation() {
528        let frag = FragmentationAnalysis {
529            fragmentation_ratio: f64::MAX,
530            largest_free_block: usize::MAX,
531            smallest_free_block: usize::MAX,
532            free_block_count: usize::MAX,
533            total_free_memory: usize::MAX,
534            external_fragmentation: f64::MAX,
535            internal_fragmentation: f64::MAX,
536        };
537
538        assert!(frag.fragmentation_ratio.is_finite());
539        assert_eq!(frag.largest_free_block, usize::MAX);
540    }
541
542    #[test]
543    fn test_boundary_values_concurrency() {
544        let analysis = ConcurrencyAnalysis {
545            thread_safety_allocations: usize::MAX,
546            shared_memory_bytes: usize::MAX,
547            mutex_protected: usize::MAX,
548            arc_shared: usize::MAX,
549            rc_shared: usize::MAX,
550            channel_buffers: usize::MAX,
551            thread_local_storage: usize::MAX,
552            atomic_operations: usize::MAX,
553            lock_contention_risk: String::new(),
554        };
555
556        assert_eq!(analysis.thread_safety_allocations, usize::MAX);
557        assert_eq!(analysis.atomic_operations, usize::MAX);
558    }
559
560    #[test]
561    fn test_memory_stats_clone() {
562        let mut stats = MemoryStats::new();
563        stats.total_allocations = 42;
564
565        let cloned = stats.clone();
566        assert_eq!(cloned.total_allocations, 42);
567    }
568
569    #[test]
570    fn test_memory_stats_debug() {
571        let stats = MemoryStats::new();
572        let debug_str = format!("{:?}", stats);
573
574        assert!(debug_str.contains("MemoryStats"));
575        assert!(debug_str.contains("total_allocations"));
576    }
577
578    #[test]
579    fn test_fragmentation_analysis_clone() {
580        let frag = FragmentationAnalysis {
581            fragmentation_ratio: 0.75,
582            largest_free_block: 2048,
583            smallest_free_block: 32,
584            free_block_count: 100,
585            total_free_memory: 4096,
586            external_fragmentation: 0.5,
587            internal_fragmentation: 0.25,
588        };
589
590        let cloned = frag.clone();
591        assert!((cloned.fragmentation_ratio - 0.75).abs() < f64::EPSILON);
592    }
593
594    #[test]
595    fn test_library_usage_clone() {
596        let usage = LibraryUsage {
597            allocation_count: 25,
598            total_bytes: 2560,
599            peak_bytes: 1280,
600            average_size: 102.4,
601            categories: HashMap::new(),
602            hotspot_functions: vec!["test".to_string()],
603        };
604
605        let cloned = usage.clone();
606        assert_eq!(cloned.allocation_count, 25);
607    }
608
609    #[test]
610    fn test_concurrency_analysis_clone() {
611        let analysis = ConcurrencyAnalysis {
612            thread_safety_allocations: 100,
613            shared_memory_bytes: 2048,
614            mutex_protected: 50,
615            arc_shared: 25,
616            rc_shared: 15,
617            channel_buffers: 10,
618            thread_local_storage: 5,
619            atomic_operations: 200,
620            lock_contention_risk: "High".to_string(),
621        };
622
623        let cloned = analysis.clone();
624        assert_eq!(cloned.thread_safety_allocations, 100);
625    }
626
627    #[test]
628    fn test_empty_library_usage_categories() {
629        let usage = LibraryUsage::default();
630        assert!(usage.categories.is_empty());
631        assert!(usage.hotspot_functions.is_empty());
632    }
633
634    #[test]
635    fn test_library_usage_with_categories() {
636        let mut categories = HashMap::new();
637        categories.insert("String".to_string(), 500);
638        categories.insert("Vec".to_string(), 1000);
639
640        let usage = LibraryUsage {
641            allocation_count: 10,
642            total_bytes: 1500,
643            peak_bytes: 750,
644            average_size: 150.0,
645            categories,
646            hotspot_functions: vec![],
647        };
648
649        assert_eq!(usage.categories.get("String"), Some(&500));
650        assert_eq!(usage.categories.get("Vec"), Some(&1000));
651        assert_eq!(usage.categories.get("HashMap"), None);
652    }
653}