Skip to main content

memscope_rs/capture/types/
fragmentation.rs

1//! Fragmentation analysis types.
2//!
3//! This module contains types for analyzing memory fragmentation,
4//! including block distribution, metrics, and cause analysis.
5
6use serde::{Deserialize, Serialize};
7
8use super::allocation::ImpactLevel;
9
10/// Enhanced memory fragmentation analysis.
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct EnhancedFragmentationAnalysis {
13    /// Total heap size.
14    pub total_heap_size: usize,
15    /// Used heap size.
16    pub used_heap_size: usize,
17    /// Free heap size.
18    pub free_heap_size: usize,
19    /// Number of free blocks.
20    pub free_block_count: usize,
21    /// Free block size distribution.
22    pub free_block_distribution: Vec<BlockSizeRange>,
23    /// Fragmentation metrics.
24    pub fragmentation_metrics: FragmentationMetrics,
25    /// Allocation patterns causing fragmentation.
26    pub fragmentation_causes: Vec<FragmentationCause>,
27}
28
29/// Block size range for distribution analysis.
30#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub struct BlockSizeRange {
32    /// Minimum size in range.
33    pub min_size: usize,
34    /// Maximum size in range.
35    pub max_size: usize,
36    /// Number of blocks in this range.
37    pub block_count: usize,
38    /// Total size of blocks in this range.
39    pub total_size: usize,
40}
41
42/// Fragmentation metrics.
43#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
44pub struct FragmentationMetrics {
45    /// External fragmentation ratio.
46    pub external_fragmentation: f64,
47    /// Internal fragmentation ratio.
48    pub internal_fragmentation: f64,
49    /// Largest free block size.
50    pub largest_free_block: usize,
51    /// Average free block size.
52    pub average_free_block_size: f64,
53    /// Fragmentation severity level.
54    pub severity_level: FragmentationSeverity,
55}
56
57/// Fragmentation severity levels.
58#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59pub enum FragmentationSeverity {
60    /// Low fragmentation severity.
61    Low,
62    /// Moderate fragmentation severity.
63    Moderate,
64    /// High fragmentation severity.
65    High,
66    /// Critical fragmentation severity.
67    Critical,
68}
69
70/// Fragmentation cause analysis.
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub struct FragmentationCause {
73    /// Cause type.
74    pub cause_type: FragmentationCauseType,
75    /// Description of the cause.
76    pub description: String,
77    /// Impact on fragmentation.
78    pub impact_level: ImpactLevel,
79    /// Suggested mitigation.
80    pub mitigation_suggestion: String,
81}
82
83/// Types of fragmentation causes.
84#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
85pub enum FragmentationCauseType {
86    /// Mixed allocation sizes.
87    MixedAllocationSizes,
88    /// Frequent allocation/deallocation.
89    FrequentAllocDealloc,
90    /// Long-lived allocations blocking coalescing.
91    LongLivedAllocations,
92    /// Poor allocation strategy.
93    PoorAllocationStrategy,
94    /// Memory leaks.
95    MemoryLeaks,
96}
97
98// Implement From trait for converting from core::types to capture::types
99impl From<crate::core::types::EnhancedFragmentationAnalysis> for EnhancedFragmentationAnalysis {
100    fn from(old: crate::core::types::EnhancedFragmentationAnalysis) -> Self {
101        Self {
102            total_heap_size: old.total_heap_size,
103            used_heap_size: old.used_heap_size,
104            free_heap_size: old.free_heap_size,
105            free_block_count: old.free_block_count,
106            free_block_distribution: old
107                .free_block_distribution
108                .into_iter()
109                .map(|b| BlockSizeRange {
110                    min_size: b.min_size,
111                    max_size: b.max_size,
112                    block_count: b.block_count,
113                    total_size: b.total_size,
114                })
115                .collect(),
116            fragmentation_metrics: FragmentationMetrics {
117                external_fragmentation: old.fragmentation_metrics.external_fragmentation,
118                internal_fragmentation: old.fragmentation_metrics.internal_fragmentation,
119                largest_free_block: old.fragmentation_metrics.largest_free_block,
120                average_free_block_size: old.fragmentation_metrics.average_free_block_size,
121                severity_level: match old.fragmentation_metrics.severity_level {
122                    crate::core::types::FragmentationSeverity::Low => FragmentationSeverity::Low,
123                    crate::core::types::FragmentationSeverity::Moderate => {
124                        FragmentationSeverity::Moderate
125                    }
126                    crate::core::types::FragmentationSeverity::High => FragmentationSeverity::High,
127                    crate::core::types::FragmentationSeverity::Critical => {
128                        FragmentationSeverity::Critical
129                    }
130                },
131            },
132            fragmentation_causes: old
133                .fragmentation_causes
134                .into_iter()
135                .map(|c| FragmentationCause {
136                    cause_type: match c.cause_type {
137                        crate::core::types::FragmentationCauseType::MixedAllocationSizes => {
138                            FragmentationCauseType::MixedAllocationSizes
139                        }
140                        crate::core::types::FragmentationCauseType::FrequentAllocDealloc => {
141                            FragmentationCauseType::FrequentAllocDealloc
142                        }
143                        crate::core::types::FragmentationCauseType::LongLivedAllocations => {
144                            FragmentationCauseType::LongLivedAllocations
145                        }
146                        crate::core::types::FragmentationCauseType::PoorAllocationStrategy => {
147                            FragmentationCauseType::PoorAllocationStrategy
148                        }
149                        crate::core::types::FragmentationCauseType::MemoryLeaks => {
150                            FragmentationCauseType::MemoryLeaks
151                        }
152                    },
153                    description: c.description,
154                    impact_level: match c.impact_level {
155                        crate::core::types::ImpactLevel::Low => ImpactLevel::Low,
156                        crate::core::types::ImpactLevel::Medium => ImpactLevel::Medium,
157                        crate::core::types::ImpactLevel::High => ImpactLevel::High,
158                        crate::core::types::ImpactLevel::Critical => ImpactLevel::Critical,
159                    },
160                    mitigation_suggestion: c.mitigation_suggestion,
161                })
162                .collect(),
163        }
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn test_enhanced_fragmentation_analysis() {
173        let analysis = EnhancedFragmentationAnalysis {
174            total_heap_size: 1024 * 1024,
175            used_heap_size: 512 * 1024,
176            free_heap_size: 512 * 1024,
177            free_block_count: 10,
178            free_block_distribution: vec![],
179            fragmentation_metrics: FragmentationMetrics {
180                external_fragmentation: 0.3,
181                internal_fragmentation: 0.1,
182                largest_free_block: 256 * 1024,
183                average_free_block_size: 51200.0,
184                severity_level: FragmentationSeverity::Low,
185            },
186            fragmentation_causes: vec![],
187        };
188
189        assert_eq!(analysis.total_heap_size, 1024 * 1024);
190        assert_eq!(analysis.free_block_count, 10);
191    }
192
193    #[test]
194    fn test_fragmentation_severity() {
195        let severity = FragmentationSeverity::High;
196        assert!(matches!(severity, FragmentationSeverity::High));
197    }
198
199    #[test]
200    fn test_fragmentation_cause_type() {
201        let cause = FragmentationCauseType::MixedAllocationSizes;
202        assert!(matches!(
203            cause,
204            FragmentationCauseType::MixedAllocationSizes
205        ));
206    }
207
208    #[test]
209    fn test_block_size_range_creation() {
210        let range = BlockSizeRange {
211            min_size: 0,
212            max_size: 1024,
213            block_count: 50,
214            total_size: 25600,
215        };
216
217        assert_eq!(range.min_size, 0);
218        assert_eq!(range.max_size, 1024);
219        assert_eq!(range.block_count, 50);
220        assert_eq!(range.total_size, 25600);
221    }
222
223    #[test]
224    fn test_block_size_range_serialization() {
225        let range = BlockSizeRange {
226            min_size: 1024,
227            max_size: 4096,
228            block_count: 25,
229            total_size: 64000,
230        };
231
232        let json = serde_json::to_string(&range).unwrap();
233        let deserialized: BlockSizeRange = serde_json::from_str(&json).unwrap();
234        assert_eq!(deserialized, range);
235    }
236
237    #[test]
238    fn test_fragmentation_metrics_all_severities() {
239        let severities = [
240            FragmentationSeverity::Low,
241            FragmentationSeverity::Moderate,
242            FragmentationSeverity::High,
243            FragmentationSeverity::Critical,
244        ];
245
246        for severity in severities {
247            let metrics = FragmentationMetrics {
248                external_fragmentation: 0.5,
249                internal_fragmentation: 0.2,
250                largest_free_block: 1024,
251                average_free_block_size: 512.0,
252                severity_level: severity.clone(),
253            };
254            assert_eq!(metrics.severity_level, severity);
255        }
256    }
257
258    #[test]
259    fn test_fragmentation_metrics_boundary_values() {
260        let metrics = FragmentationMetrics {
261            external_fragmentation: 0.0,
262            internal_fragmentation: 1.0,
263            largest_free_block: 0,
264            average_free_block_size: 0.0,
265            severity_level: FragmentationSeverity::Critical,
266        };
267
268        assert!((metrics.external_fragmentation - 0.0).abs() < f64::EPSILON);
269        assert!((metrics.internal_fragmentation - 1.0).abs() < f64::EPSILON);
270        assert_eq!(metrics.largest_free_block, 0);
271    }
272
273    #[test]
274    fn test_fragmentation_metrics_serialization() {
275        let metrics = FragmentationMetrics {
276            external_fragmentation: 0.75,
277            internal_fragmentation: 0.25,
278            largest_free_block: 2048,
279            average_free_block_size: 1024.0,
280            severity_level: FragmentationSeverity::High,
281        };
282
283        let json = serde_json::to_string(&metrics).unwrap();
284        let deserialized: FragmentationMetrics = serde_json::from_str(&json).unwrap();
285        assert_eq!(deserialized, metrics);
286    }
287
288    #[test]
289    fn test_fragmentation_cause_creation() {
290        let cause = FragmentationCause {
291            cause_type: FragmentationCauseType::FrequentAllocDealloc,
292            description: "Frequent allocation and deallocation pattern".to_string(),
293            impact_level: ImpactLevel::High,
294            mitigation_suggestion: "Use object pooling".to_string(),
295        };
296
297        assert!(matches!(
298            cause.cause_type,
299            FragmentationCauseType::FrequentAllocDealloc
300        ));
301        assert_eq!(
302            cause.description,
303            "Frequent allocation and deallocation pattern"
304        );
305        assert!(matches!(cause.impact_level, ImpactLevel::High));
306    }
307
308    #[test]
309    fn test_all_fragmentation_cause_types() {
310        let cause_types = [
311            FragmentationCauseType::MixedAllocationSizes,
312            FragmentationCauseType::FrequentAllocDealloc,
313            FragmentationCauseType::LongLivedAllocations,
314            FragmentationCauseType::PoorAllocationStrategy,
315            FragmentationCauseType::MemoryLeaks,
316        ];
317
318        for cause_type in cause_types {
319            let cause = FragmentationCause {
320                cause_type: cause_type.clone(),
321                description: "Test description".to_string(),
322                impact_level: ImpactLevel::Low,
323                mitigation_suggestion: "Test mitigation".to_string(),
324            };
325            assert_eq!(cause.cause_type, cause_type);
326        }
327    }
328
329    #[test]
330    fn test_fragmentation_cause_serialization() {
331        let cause = FragmentationCause {
332            cause_type: FragmentationCauseType::MemoryLeaks,
333            description: "Memory leak detected".to_string(),
334            impact_level: ImpactLevel::Critical,
335            mitigation_suggestion: "Fix memory leaks".to_string(),
336        };
337
338        let json = serde_json::to_string(&cause).unwrap();
339        let deserialized: FragmentationCause = serde_json::from_str(&json).unwrap();
340        assert_eq!(deserialized, cause);
341    }
342
343    #[test]
344    fn test_all_fragmentation_severities_serialization() {
345        let severities = vec![
346            FragmentationSeverity::Low,
347            FragmentationSeverity::Moderate,
348            FragmentationSeverity::High,
349            FragmentationSeverity::Critical,
350        ];
351
352        for severity in severities {
353            let json = serde_json::to_string(&severity).unwrap();
354            let deserialized: FragmentationSeverity = serde_json::from_str(&json).unwrap();
355            assert_eq!(deserialized, severity);
356        }
357    }
358
359    #[test]
360    fn test_all_cause_types_serialization() {
361        let cause_types = vec![
362            FragmentationCauseType::MixedAllocationSizes,
363            FragmentationCauseType::FrequentAllocDealloc,
364            FragmentationCauseType::LongLivedAllocations,
365            FragmentationCauseType::PoorAllocationStrategy,
366            FragmentationCauseType::MemoryLeaks,
367        ];
368
369        for cause_type in cause_types {
370            let json = serde_json::to_string(&cause_type).unwrap();
371            let deserialized: FragmentationCauseType = serde_json::from_str(&json).unwrap();
372            assert_eq!(deserialized, cause_type);
373        }
374    }
375
376    #[test]
377    fn test_enhanced_fragmentation_analysis_with_distribution() {
378        let distribution = vec![
379            BlockSizeRange {
380                min_size: 0,
381                max_size: 256,
382                block_count: 100,
383                total_size: 12800,
384            },
385            BlockSizeRange {
386                min_size: 256,
387                max_size: 1024,
388                block_count: 50,
389                total_size: 32000,
390            },
391        ];
392
393        let causes = vec![FragmentationCause {
394            cause_type: FragmentationCauseType::MixedAllocationSizes,
395            description: "Mixed allocation sizes".to_string(),
396            impact_level: ImpactLevel::Medium,
397            mitigation_suggestion: "Use size classes".to_string(),
398        }];
399
400        let analysis = EnhancedFragmentationAnalysis {
401            total_heap_size: 1024 * 1024,
402            used_heap_size: 768 * 1024,
403            free_heap_size: 256 * 1024,
404            free_block_count: 150,
405            free_block_distribution: distribution.clone(),
406            fragmentation_metrics: FragmentationMetrics {
407                external_fragmentation: 0.6,
408                internal_fragmentation: 0.15,
409                largest_free_block: 65536,
410                average_free_block_size: 1706.0,
411                severity_level: FragmentationSeverity::Moderate,
412            },
413            fragmentation_causes: causes.clone(),
414        };
415
416        assert_eq!(analysis.free_block_distribution.len(), 2);
417        assert_eq!(analysis.free_block_distribution[0].block_count, 100);
418        assert_eq!(analysis.fragmentation_causes.len(), 1);
419    }
420
421    #[test]
422    fn test_enhanced_fragmentation_analysis_serialization() {
423        let analysis = EnhancedFragmentationAnalysis {
424            total_heap_size: 2048 * 1024,
425            used_heap_size: 1024 * 1024,
426            free_heap_size: 1024 * 1024,
427            free_block_count: 25,
428            free_block_distribution: vec![BlockSizeRange {
429                min_size: 0,
430                max_size: 4096,
431                block_count: 25,
432                total_size: 51200,
433            }],
434            fragmentation_metrics: FragmentationMetrics {
435                external_fragmentation: 0.4,
436                internal_fragmentation: 0.1,
437                largest_free_block: 32768,
438                average_free_block_size: 40960.0,
439                severity_level: FragmentationSeverity::Low,
440            },
441            fragmentation_causes: vec![],
442        };
443
444        let json = serde_json::to_string(&analysis).unwrap();
445        let deserialized: EnhancedFragmentationAnalysis = serde_json::from_str(&json).unwrap();
446        assert_eq!(deserialized, analysis);
447    }
448
449    #[test]
450    fn test_enhanced_fragmentation_analysis_clone() {
451        let analysis = EnhancedFragmentationAnalysis {
452            total_heap_size: 1024,
453            used_heap_size: 512,
454            free_heap_size: 512,
455            free_block_count: 5,
456            free_block_distribution: vec![],
457            fragmentation_metrics: FragmentationMetrics {
458                external_fragmentation: 0.5,
459                internal_fragmentation: 0.2,
460                largest_free_block: 256,
461                average_free_block_size: 102.4,
462                severity_level: FragmentationSeverity::Moderate,
463            },
464            fragmentation_causes: vec![],
465        };
466
467        let cloned = analysis.clone();
468        assert_eq!(cloned.total_heap_size, analysis.total_heap_size);
469        assert_eq!(cloned.free_block_count, analysis.free_block_count);
470    }
471
472    #[test]
473    fn test_enhanced_fragmentation_analysis_debug() {
474        let analysis = EnhancedFragmentationAnalysis {
475            total_heap_size: 1024,
476            used_heap_size: 512,
477            free_heap_size: 512,
478            free_block_count: 5,
479            free_block_distribution: vec![],
480            fragmentation_metrics: FragmentationMetrics {
481                external_fragmentation: 0.5,
482                internal_fragmentation: 0.2,
483                largest_free_block: 256,
484                average_free_block_size: 102.4,
485                severity_level: FragmentationSeverity::Low,
486            },
487            fragmentation_causes: vec![],
488        };
489
490        let debug_str = format!("{:?}", analysis);
491        assert!(debug_str.contains("EnhancedFragmentationAnalysis"));
492        assert!(debug_str.contains("total_heap_size"));
493    }
494
495    #[test]
496    fn test_block_size_range_equality() {
497        let range1 = BlockSizeRange {
498            min_size: 0,
499            max_size: 1024,
500            block_count: 10,
501            total_size: 5120,
502        };
503        let range2 = BlockSizeRange {
504            min_size: 0,
505            max_size: 1024,
506            block_count: 10,
507            total_size: 5120,
508        };
509        let range3 = BlockSizeRange {
510            min_size: 0,
511            max_size: 2048,
512            block_count: 10,
513            total_size: 5120,
514        };
515
516        assert_eq!(range1, range2);
517        assert_ne!(range1, range3);
518    }
519
520    #[test]
521    fn test_fragmentation_severity_equality() {
522        assert_eq!(FragmentationSeverity::Low, FragmentationSeverity::Low);
523        assert_ne!(FragmentationSeverity::Low, FragmentationSeverity::High);
524        assert_ne!(
525            FragmentationSeverity::Moderate,
526            FragmentationSeverity::Critical
527        );
528    }
529
530    #[test]
531    fn test_fragmentation_cause_type_equality() {
532        assert_eq!(
533            FragmentationCauseType::MixedAllocationSizes,
534            FragmentationCauseType::MixedAllocationSizes
535        );
536        assert_ne!(
537            FragmentationCauseType::MixedAllocationSizes,
538            FragmentationCauseType::MemoryLeaks
539        );
540    }
541
542    #[test]
543    fn test_fragmentation_cause_with_all_impact_levels() {
544        let impact_levels = [
545            ImpactLevel::Low,
546            ImpactLevel::Medium,
547            ImpactLevel::High,
548            ImpactLevel::Critical,
549        ];
550
551        for impact in impact_levels {
552            let cause = FragmentationCause {
553                cause_type: FragmentationCauseType::PoorAllocationStrategy,
554                description: "Test".to_string(),
555                impact_level: impact.clone(),
556                mitigation_suggestion: "Fix it".to_string(),
557            };
558            assert_eq!(cause.impact_level, impact);
559        }
560    }
561
562    #[test]
563    fn test_empty_free_block_distribution() {
564        let analysis = EnhancedFragmentationAnalysis {
565            total_heap_size: 1024,
566            used_heap_size: 0,
567            free_heap_size: 1024,
568            free_block_count: 0,
569            free_block_distribution: vec![],
570            fragmentation_metrics: FragmentationMetrics {
571                external_fragmentation: 0.0,
572                internal_fragmentation: 0.0,
573                largest_free_block: 1024,
574                average_free_block_size: 0.0,
575                severity_level: FragmentationSeverity::Low,
576            },
577            fragmentation_causes: vec![],
578        };
579
580        assert!(analysis.free_block_distribution.is_empty());
581        assert_eq!(analysis.free_block_count, 0);
582    }
583
584    #[test]
585    fn test_large_heap_analysis() {
586        let analysis = EnhancedFragmentationAnalysis {
587            total_heap_size: usize::MAX / 2,
588            used_heap_size: usize::MAX / 4,
589            free_heap_size: usize::MAX / 4,
590            free_block_count: 1000000,
591            free_block_distribution: vec![],
592            fragmentation_metrics: FragmentationMetrics {
593                external_fragmentation: f64::MAX,
594                internal_fragmentation: f64::MIN,
595                largest_free_block: usize::MAX,
596                average_free_block_size: f64::INFINITY,
597                severity_level: FragmentationSeverity::Critical,
598            },
599            fragmentation_causes: vec![],
600        };
601
602        assert_eq!(analysis.total_heap_size, usize::MAX / 2);
603        assert!(
604            analysis
605                .fragmentation_metrics
606                .external_fragmentation
607                .is_finite()
608                || analysis
609                    .fragmentation_metrics
610                    .external_fragmentation
611                    .is_infinite()
612        );
613    }
614}