Skip to main content

memscope_rs/analysis/
mod.rs

1//! Memory analysis functionality
2//!
3//! This module provides advanced analysis capabilities:
4//! - Enhanced memory analysis
5//! - Circular reference detection
6//! - Unsafe FFI tracking
7//! - Unknown memory region analysis
8//! - Type classification
9//! - Performance metrics
10//! - Quality assurance
11//! - Size estimation
12//! - Dedicated detectors (LeakDetector, UafDetector, etc.)
13
14// Detector modules
15pub mod detectors;
16
17pub mod circular_reference;
18pub mod heap_scanner;
19pub mod relation_inference;
20pub mod unsafe_ffi_tracker;
21pub mod unsafe_inference;
22pub mod variable_relationships;
23
24// Submodules from refactoring
25pub mod closure;
26pub mod generic;
27pub mod safety;
28pub mod security;
29pub mod unknown;
30
31// New analysis modules for ComplexTypeForRust.md features
32pub mod async_analysis;
33pub mod borrow_analysis;
34
35pub mod ffi_function_resolver;
36pub mod lifecycle;
37pub mod lifecycle_analysis;
38pub mod memory_passport_tracker;
39
40// Integrated analysis submodules
41pub mod classification;
42pub mod estimation;
43pub mod metrics;
44pub mod quality;
45
46// Re-export key analysis functions
47pub use circular_reference::{CircularReference, CircularReferenceAnalysis, CircularReferenceNode};
48pub use unsafe_ffi_tracker::UnsafeFFITracker;
49pub use variable_relationships::{
50    build_variable_relationship_graph, GraphStatistics, RelationshipType as VarRelationshipType,
51    SmartPointerInfo as VarSmartPointerInfo, VariableCategory, VariableCluster, VariableNode,
52    VariableRelationship, VariableRelationshipGraph,
53};
54
55// Relationship cycle detection
56pub mod relationship_cycle_detector;
57pub use relationship_cycle_detector::{detect_cycles_in_relationships, CycleDetectionResult};
58
59// Ownership graph analysis
60pub mod ownership_graph;
61pub use ownership_graph::{Edge, EdgeKind, Node, ObjectId, OwnershipGraph, OwnershipOp};
62
63// Re-export new analysis modules
64pub use detectors::{
65    Detector, LeakDetector, LeakDetectorConfig, LifecycleDetector, LifecycleDetectorConfig,
66    OverflowDetector, OverflowDetectorConfig, SafetyDetector, SafetyDetectorConfig, UafDetector,
67    UafDetectorConfig,
68};
69
70pub use async_analysis::{
71    get_global_async_analyzer, AsyncAnalyzer, AsyncPatternAnalysis, AsyncStatistics,
72};
73pub use borrow_analysis::{get_global_borrow_analyzer, BorrowAnalyzer, BorrowPatternAnalysis};
74pub use closure::{get_global_closure_analyzer, ClosureAnalysisReport, ClosureAnalyzer};
75pub use ffi_function_resolver::{
76    get_global_ffi_resolver, initialize_global_ffi_resolver, FfiFunctionCategory,
77    FfiFunctionResolver, FfiRiskLevel, ResolutionStats, ResolvedFfiFunction, ResolverConfig,
78};
79pub use generic::{get_global_generic_analyzer, GenericAnalyzer, GenericStatistics};
80pub use lifecycle::{
81    lifecycle_summary::{
82        AllocationLifecycleSummary, ExportMetadata, LifecycleEvent, LifecycleEventSummary,
83        LifecycleExportData, LifecyclePattern, LifecycleSummaryGenerator, SummaryConfig,
84        VariableGroup,
85    },
86    ownership_history::{
87        BorrowInfo, CloneInfo, OwnershipEvent, OwnershipEventType, OwnershipHistoryRecorder,
88        OwnershipStatistics, OwnershipSummary, RefCountInfo,
89    },
90};
91pub use lifecycle_analysis::{
92    get_global_lifecycle_analyzer, LifecycleAnalysisReport, LifecycleAnalyzer,
93};
94pub use memory_passport_tracker::{
95    get_global_passport_tracker, initialize_global_passport_tracker, LeakDetail,
96    LeakDetectionResult, MemoryPassport, MemoryPassportTracker, PassportEvent, PassportEventType,
97    PassportStatus, PassportTrackerConfig, PassportTrackerStats,
98};
99pub use safety::{
100    DynamicViolation, RiskAssessment, RiskFactor, RiskFactorType, SafetyAnalysisConfig,
101    SafetyAnalysisStats, SafetyAnalyzer, UnsafeReport, UnsafeSource,
102};
103pub use unsafe_ffi_tracker::ComprehensiveSafetyReport;
104pub use unsafe_inference::{
105    count_valid_pointers, get_valid_regions, is_valid_ptr, is_valid_ptr_static, InferenceMethod,
106    InferenceRecord, MemoryRegion, MemoryView, TypeGuess, TypeKind, UnsafeInferenceEngine,
107    ValidRegions,
108};
109
110// Heap scanner exports
111pub use heap_scanner::{HeapScanner, ScanResult};
112
113// Relation inference exports
114pub use relation_inference::{
115    detect_clones, detect_owner, detect_slice, CloneConfig, GraphBuilderConfig, RangeMap, Relation,
116    RelationEdge, RelationGraph, RelationGraphBuilder,
117};
118
119// Re-export integrated submodules
120pub use classification::{
121    pattern_matcher::PatternMatcher,
122    rule_engine::{Rule as ClassificationRule, RuleEngine},
123    type_classifier::{TypeCategory, TypeClassifier},
124};
125pub use estimation::{
126    size_estimator::SizeEstimator, type_classifier::TypeClassifier as EstimationTypeClassifier,
127};
128pub use metrics::{
129    analyzer::{Benchmark, PerformanceAnalyzer, PerformanceReport},
130    collector::{Metric, MetricType, MetricValue, MetricsCollector},
131    reporter::{AlertThreshold, MetricsReporter, ReportFormat},
132};
133pub use quality::{
134    analyzer::{AnalysisReport, CodeAnalyzer, QualityMetric},
135    checker::{MemoryLeakChecker, PerformanceChecker, SafetyChecker},
136    validator::{QualityValidator, ValidationResult, ValidationRule},
137};
138
139use crate::capture::types::stats::FragmentationAnalysis;
140use crate::capture::types::*;
141use std::sync::Arc;
142
143/// Main analysis interface - consolidates all analysis functionality
144pub struct AnalysisManager {
145    // This will contain the consolidated analysis functionality
146}
147
148impl AnalysisManager {
149    /// Create a new analysis manager instance
150    pub fn new() -> Self {
151        Self {}
152    }
153
154    /// Analyze memory fragmentation
155    pub fn analyze_fragmentation(&self, allocations: &[AllocationInfo]) -> FragmentationAnalysis {
156        if allocations.is_empty() {
157            return FragmentationAnalysis::default();
158        }
159
160        let active_allocations: Vec<_> = allocations
161            .iter()
162            .filter(|a| a.timestamp_dealloc.is_none())
163            .collect();
164
165        if active_allocations.is_empty() {
166            return FragmentationAnalysis::default();
167        }
168
169        let mut sorted_ptrs: Vec<usize> = active_allocations.iter().map(|a| a.ptr).collect();
170        sorted_ptrs.sort();
171
172        let mut gaps: Vec<usize> = Vec::new();
173        for i in 1..sorted_ptrs.len() {
174            let prev = sorted_ptrs[i - 1];
175            let curr = sorted_ptrs[i];
176            if curr > prev {
177                let prev_alloc = active_allocations
178                    .iter()
179                    .find(|a| a.ptr == prev)
180                    .map(|a| a.size)
181                    .unwrap_or(0);
182                let gap = curr.saturating_sub(prev + prev_alloc);
183                if gap > 0 {
184                    gaps.push(gap);
185                }
186            }
187        }
188
189        let total_memory: usize = active_allocations.iter().map(|a| a.size).sum();
190        let total_gap: usize = gaps.iter().sum();
191
192        let fragmentation_ratio = if total_memory > 0 {
193            total_gap as f64 / (total_memory + total_gap) as f64
194        } else {
195            0.0
196        };
197
198        let largest_free_block = gaps.iter().max().copied().unwrap_or(0);
199        let smallest_free_block = gaps.iter().min().copied().unwrap_or(0);
200        let free_block_count = gaps.len();
201        let total_free_memory = total_gap;
202
203        let external_fragmentation = if !gaps.is_empty() {
204            let avg_gap = total_gap as f64 / gaps.len() as f64;
205            let max_gap = largest_free_block as f64;
206            if max_gap > 0.0 {
207                1.0 - (avg_gap / max_gap)
208            } else {
209                0.0
210            }
211        } else {
212            0.0
213        };
214
215        let total_requested: usize = active_allocations.iter().map(|a| a.size).sum();
216        let total_allocated = total_requested + total_gap;
217        let internal_fragmentation = if total_allocated > 0 {
218            (total_gap as f64 / total_allocated as f64) * 100.0
219        } else {
220            0.0
221        };
222
223        FragmentationAnalysis {
224            fragmentation_ratio,
225            largest_free_block,
226            smallest_free_block,
227            free_block_count,
228            total_free_memory,
229            external_fragmentation,
230            internal_fragmentation,
231        }
232    }
233
234    /// Analyze system library usage
235    pub fn analyze_system_libraries(&self, allocations: &[AllocationInfo]) -> SystemLibraryStats {
236        let mut stats = SystemLibraryStats::default();
237
238        for alloc in allocations {
239            let type_name = alloc.type_name.as_deref().unwrap_or("");
240            let stack = alloc.stack_trace.as_deref().unwrap_or(&[]);
241
242            if type_name.contains("Vec<")
243                || type_name.contains("HashMap")
244                || type_name.contains("HashSet")
245                || type_name.contains("BTreeMap")
246                || type_name.contains("BTreeSet")
247            {
248                stats.std_collections.allocation_count += 1;
249                stats.std_collections.total_bytes += alloc.size;
250            }
251
252            if type_name.contains("Future")
253                || type_name.contains("async")
254                || type_name.contains("tokio")
255                || type_name.contains("async_std")
256            {
257                stats.async_runtime.allocation_count += 1;
258                stats.async_runtime.total_bytes += alloc.size;
259            }
260
261            if type_name.contains("TcpStream")
262                || type_name.contains("UdpSocket")
263                || type_name.contains("Http")
264                || stack.iter().any(|s| s.contains("net::"))
265            {
266                stats.network_io.allocation_count += 1;
267                stats.network_io.total_bytes += alloc.size;
268            }
269
270            if type_name.contains("File")
271                || type_name.contains("Path")
272                || stack.iter().any(|s| s.contains("fs::"))
273            {
274                stats.file_system.allocation_count += 1;
275                stats.file_system.total_bytes += alloc.size;
276            }
277
278            if type_name.contains("serde")
279                || type_name.contains("Json")
280                || type_name.contains("Deserialize")
281            {
282                stats.serialization.allocation_count += 1;
283                stats.serialization.total_bytes += alloc.size;
284            }
285
286            if type_name.contains("Regex") || type_name.contains("regex") {
287                stats.regex_engine.allocation_count += 1;
288                stats.regex_engine.total_bytes += alloc.size;
289            }
290
291            if type_name.contains("Crypto")
292                || type_name.contains("Hash")
293                || type_name.contains("Signature")
294            {
295                stats.crypto_security.allocation_count += 1;
296                stats.crypto_security.total_bytes += alloc.size;
297            }
298
299            if type_name.contains("Database")
300                || type_name.contains("Connection")
301                || type_name.contains("Query")
302            {
303                stats.database.allocation_count += 1;
304                stats.database.total_bytes += alloc.size;
305            }
306
307            if type_name.contains("Window")
308                || type_name.contains("Canvas")
309                || type_name.contains("Surface")
310                || type_name.contains("wgpu")
311            {
312                stats.graphics_ui.allocation_count += 1;
313                stats.graphics_ui.total_bytes += alloc.size;
314            }
315
316            if type_name.contains("Request")
317                || type_name.contains("Response")
318                || type_name.contains("hyper")
319                || type_name.contains("reqwest")
320            {
321                stats.http_stack.allocation_count += 1;
322                stats.http_stack.total_bytes += alloc.size;
323            }
324        }
325
326        stats
327    }
328
329    /// Analyze concurrency safety
330    pub fn analyze_concurrency_safety(
331        &self,
332        allocations: &[AllocationInfo],
333    ) -> ConcurrencyAnalysis {
334        let mut analysis = ConcurrencyAnalysis::default();
335
336        let mut thread_alloc_counts: std::collections::HashMap<std::thread::ThreadId, usize> =
337            std::collections::HashMap::new();
338
339        for alloc in allocations {
340            let type_name = alloc.type_name.as_deref().unwrap_or("");
341
342            if type_name.contains("Arc<")
343                || type_name.contains("Mutex<")
344                || type_name.contains("RwLock<")
345            {
346                analysis.thread_safety_allocations += 1;
347                analysis.shared_memory_bytes += alloc.size;
348            }
349
350            if type_name.contains("Arc<") {
351                analysis.arc_shared += 1;
352            }
353
354            if type_name.contains("mpsc")
355                || type_name.contains("channel")
356                || type_name.contains("Sender")
357                || type_name.contains("Receiver")
358            {
359                analysis.channel_buffers += 1;
360            }
361
362            if type_name.contains("thread_local") || type_name.contains("LocalKey") {
363                analysis.thread_local_storage += 1;
364            }
365
366            if type_name.contains("Atomic") {
367                analysis.atomic_operations += 1;
368            }
369
370            *thread_alloc_counts.entry(alloc.thread_id).or_insert(0) += 1;
371        }
372
373        let max_thread_allocs = thread_alloc_counts.values().max().copied().unwrap_or(0);
374        let min_thread_allocs = thread_alloc_counts.values().min().copied().unwrap_or(0);
375        let thread_count = thread_alloc_counts.len();
376
377        if thread_count <= 1 {
378            analysis.lock_contention_risk = "None".to_string();
379        } else {
380            let imbalance_ratio = if min_thread_allocs > 0 {
381                max_thread_allocs as f64 / min_thread_allocs as f64
382            } else {
383                f64::MAX
384            };
385
386            if imbalance_ratio > 10.0 {
387                analysis.lock_contention_risk = "High".to_string();
388            } else if imbalance_ratio > 3.0 {
389                analysis.lock_contention_risk = "Medium".to_string();
390            } else {
391                analysis.lock_contention_risk = "Low".to_string();
392            }
393        }
394
395        analysis
396    }
397
398    /// Get unsafe/FFI tracker instance
399    pub fn get_unsafe_ffi_tracker(&self) -> Arc<crate::unsafe_ffi_tracker::UnsafeFFITracker> {
400        // Delegate to existing global tracker
401        crate::unsafe_ffi_tracker::get_global_unsafe_ffi_tracker()
402    }
403
404    /// Get unsafe/FFI statistics
405    pub fn get_unsafe_ffi_stats(&self) -> crate::unsafe_ffi_tracker::UnsafeFFIStats {
406        // Get stats from the global tracker
407        self.get_unsafe_ffi_tracker().get_stats()
408    }
409
410    /// Analyze circular references in smart pointers
411    pub fn analyze_circular_references(
412        &self,
413        allocations: &[AllocationInfo],
414    ) -> crate::circular_reference::CircularReferenceAnalysis {
415        crate::circular_reference::detect_circular_references(allocations)
416    }
417
418    /// Analyze borrow checker integration and lifetime tracking
419    pub fn analyze_borrow_patterns(
420        &self,
421        _allocations: &[AllocationInfo],
422    ) -> BorrowPatternAnalysis {
423        let analyzer = get_global_borrow_analyzer();
424        analyzer.analyze_borrow_patterns()
425    }
426
427    /// Analyze generic type usage and constraints
428    pub fn analyze_generic_types(&self, _allocations: &[AllocationInfo]) -> GenericStatistics {
429        // Create a new analyzer instance to avoid global state pollution in tests
430        let analyzer = GenericAnalyzer::new();
431        analyzer.get_generic_statistics()
432    }
433
434    /// Analyze async types and Future state machines
435    pub fn analyze_async_patterns(&self, _allocations: &[AllocationInfo]) -> AsyncPatternAnalysis {
436        let analyzer = get_global_async_analyzer();
437        analyzer.analyze_async_patterns()
438    }
439
440    /// Analyze closure captures and lifetime relationships
441    pub fn analyze_closure_patterns(
442        &self,
443        allocations: &[AllocationInfo],
444    ) -> ClosureAnalysisReport {
445        let analyzer = get_global_closure_analyzer();
446        analyzer.analyze_closure_patterns(allocations)
447    }
448
449    /// Analyze lifecycle patterns including Drop trait and RAII
450    pub fn analyze_lifecycle_patterns(
451        &self,
452        _allocations: &[AllocationInfo],
453    ) -> LifecycleAnalysisReport {
454        let analyzer = get_global_lifecycle_analyzer();
455        analyzer.get_lifecycle_report()
456    }
457
458    /// Analyze memory leaks
459    pub fn analyze_memory_leaks(
460        &self,
461        allocations: &[AllocationInfo],
462    ) -> crate::analysis::detectors::DetectionResult {
463        let detector = crate::analysis::detectors::LeakDetector::new(
464            crate::analysis::detectors::LeakDetectorConfig::default(),
465        );
466        detector.detect(allocations)
467    }
468
469    /// Analyze use-after-free issues
470    pub fn analyze_use_after_free(
471        &self,
472        allocations: &[AllocationInfo],
473    ) -> crate::analysis::detectors::DetectionResult {
474        let detector = crate::analysis::detectors::UafDetector::new(
475            crate::analysis::detectors::UafDetectorConfig::default(),
476        );
477        detector.detect(allocations)
478    }
479
480    /// Analyze buffer overflow issues
481    pub fn analyze_buffer_overflow(
482        &self,
483        allocations: &[AllocationInfo],
484    ) -> crate::analysis::detectors::DetectionResult {
485        let detector = crate::analysis::detectors::OverflowDetector::new(
486            crate::analysis::detectors::OverflowDetectorConfig::default(),
487        );
488        detector.detect(allocations)
489    }
490
491    /// Analyze safety violations
492    pub fn analyze_safety_violations(
493        &self,
494        allocations: &[AllocationInfo],
495    ) -> crate::analysis::detectors::DetectionResult {
496        let detector = crate::analysis::detectors::SafetyDetector::new(
497            crate::analysis::detectors::SafetyDetectorConfig::default(),
498        );
499        detector.detect(allocations)
500    }
501
502    /// Analyze lifecycle issues
503    pub fn analyze_lifecycle_issues(
504        &self,
505        allocations: &[AllocationInfo],
506    ) -> crate::analysis::detectors::DetectionResult {
507        let detector = crate::analysis::detectors::LifecycleDetector::new(
508            crate::analysis::detectors::LifecycleDetectorConfig::default(),
509        );
510        detector.detect(allocations)
511    }
512
513    /// Perform comprehensive analysis
514    pub fn perform_comprehensive_analysis(
515        &self,
516        allocations: &[AllocationInfo],
517        stats: &MemoryStats,
518    ) -> ComprehensiveAnalysisReport {
519        let fragmentation = self.analyze_fragmentation(allocations);
520        let system_libs = self.analyze_system_libraries(allocations);
521        let concurrency = self.analyze_concurrency_safety(allocations);
522        let unsafe_stats = self.get_unsafe_ffi_stats();
523        let circular_refs = self.analyze_circular_references(allocations);
524
525        // New comprehensive analysis features
526        let borrow_analysis = self.analyze_borrow_patterns(allocations);
527        let generic_analysis = self.analyze_generic_types(allocations);
528        let async_analysis = self.analyze_async_patterns(allocations);
529        let closure_analysis = self.analyze_closure_patterns(allocations);
530        let lifecycle_analysis = self.analyze_lifecycle_patterns(allocations);
531
532        ComprehensiveAnalysisReport {
533            fragmentation_analysis: fragmentation,
534            system_library_stats: system_libs,
535            concurrency_analysis: concurrency,
536            unsafe_ffi_stats: unsafe_stats,
537            circular_reference_analysis: circular_refs,
538            borrow_analysis,
539            generic_analysis,
540            async_analysis,
541            closure_analysis,
542            lifecycle_analysis,
543            memory_stats: stats.clone(),
544            analysis_timestamp: std::time::SystemTime::now()
545                .duration_since(std::time::UNIX_EPOCH)
546                .unwrap_or_default()
547                .as_secs(),
548        }
549    }
550}
551
552impl Default for AnalysisManager {
553    fn default() -> Self {
554        Self::new()
555    }
556}
557
558/// Comprehensive analysis report
559#[derive(Debug, Clone)]
560pub struct ComprehensiveAnalysisReport {
561    /// Memory fragmentation analysis results
562    pub fragmentation_analysis: FragmentationAnalysis,
563    /// System library usage statistics
564    pub system_library_stats: SystemLibraryStats,
565    /// Concurrency safety analysis
566    pub concurrency_analysis: ConcurrencyAnalysis,
567    /// Unsafe and FFI operation statistics
568    pub unsafe_ffi_stats: crate::unsafe_ffi_tracker::UnsafeFFIStats,
569    /// Circular reference analysis for smart pointers
570    pub circular_reference_analysis: crate::circular_reference::CircularReferenceAnalysis,
571    /// Borrow checker integration and lifetime tracking
572    pub borrow_analysis: BorrowPatternAnalysis,
573    /// Generic type usage and constraint analysis
574    pub generic_analysis: GenericStatistics,
575    /// Async type and Future state machine analysis
576    pub async_analysis: AsyncPatternAnalysis,
577    /// Closure capture and lifetime analysis
578    pub closure_analysis: ClosureAnalysisReport,
579    /// Lifecycle patterns including Drop trait and RAII
580    pub lifecycle_analysis: LifecycleAnalysisReport,
581    /// Overall memory statistics
582    pub memory_stats: MemoryStats,
583    /// Timestamp when analysis was performed
584    pub analysis_timestamp: u64,
585}
586
587// Re-export all the existing analysis functions for backward compatibility
588// This ensures that existing code continues to work without changes
589
590/// Analyze memory fragmentation - backward compatibility function
591pub fn analyze_fragmentation(allocations: &[AllocationInfo]) -> FragmentationAnalysis {
592    let manager = AnalysisManager::new();
593    manager.analyze_fragmentation(allocations)
594}
595
596/// Analyze system library usage - backward compatibility function
597pub fn analyze_system_libraries(allocations: &[AllocationInfo]) -> SystemLibraryStats {
598    let manager = AnalysisManager::new();
599    manager.analyze_system_libraries(allocations)
600}
601
602/// Analyze concurrency safety - backward compatibility function
603pub fn analyze_concurrency_safety(allocations: &[AllocationInfo]) -> ConcurrencyAnalysis {
604    let manager = AnalysisManager::new();
605    manager.analyze_concurrency_safety(allocations)
606}
607
608/// Get global unsafe/FFI tracker - backward compatibility function
609pub fn get_global_unsafe_ffi_tracker() -> Arc<crate::unsafe_ffi_tracker::UnsafeFFITracker> {
610    crate::unsafe_ffi_tracker::get_global_unsafe_ffi_tracker()
611}
612
613/// Get unsafe/FFI statistics - convenience function
614pub fn get_unsafe_ffi_stats() -> crate::unsafe_ffi_tracker::UnsafeFFIStats {
615    let manager = AnalysisManager::new();
616    manager.get_unsafe_ffi_stats()
617}
618
619/// Perform comprehensive analysis - convenience function
620pub fn perform_comprehensive_analysis(
621    allocations: &[AllocationInfo],
622    stats: &MemoryStats,
623) -> ComprehensiveAnalysisReport {
624    let manager = AnalysisManager::new();
625    manager.perform_comprehensive_analysis(allocations, stats)
626}
627
628// Analysis module - consolidating implementations for better organization
629// For now, we're just creating the interface and delegating to the existing implementations
630
631#[cfg(test)]
632mod tests {
633    use super::*;
634    use crate::capture::types::AllocationInfo;
635
636    #[test]
637    fn test_analyze_fragmentation() {
638        let manager = AnalysisManager::new();
639        // Create allocations with known gaps to verify precise calculation.
640        // alloc1 at 0x1000 (size 1024) → ends at 0x1400
641        // alloc2 at 0x2000 (size 512) → ends at 0x2200
642        // Gap between them: 0x2000 - 0x1400 = 3072 bytes
643        let allocations = vec![
644            AllocationInfo::new(0x1000, 1024),
645            AllocationInfo::new(0x2000, 512),
646        ];
647
648        let result = manager.analyze_fragmentation(&allocations);
649
650        // With known layout: total_memory = 1536, total_gap = 3072
651        // fragmentation_ratio = 3072 / (1536 + 3072) = 3072/4608 ≈ 0.667
652        assert!(
653            result.fragmentation_ratio > 0.5,
654            "fragmentation_ratio should be > 0.5, got {}",
655            result.fragmentation_ratio
656        );
657        assert_eq!(result.free_block_count, 1, "should have exactly 1 gap");
658        assert_eq!(
659            result.largest_free_block, 3072,
660            "largest gap should be 3072"
661        );
662        assert_eq!(
663            result.smallest_free_block, 3072,
664            "smallest gap should be 3072"
665        );
666    }
667
668    #[test]
669    fn test_analyze_fragmentation_no_gaps() {
670        let manager = AnalysisManager::new();
671        // Contiguous allocations: no gaps
672        let allocations = vec![
673            AllocationInfo::new(0x1000, 256),
674            AllocationInfo::new(0x1100, 256),
675        ];
676
677        let result = manager.analyze_fragmentation(&allocations);
678        assert_eq!(
679            result.fragmentation_ratio, 0.0,
680            "no gaps means zero fragmentation"
681        );
682        assert_eq!(result.free_block_count, 0);
683    }
684
685    #[test]
686    fn test_analyze_system_libraries() {
687        let manager = AnalysisManager::new();
688
689        // Create allocations with specific type names to trigger detection
690        let mut alloc_vec = AllocationInfo::new(0x1000, 256);
691        alloc_vec.type_name = Some("Vec<String>".to_string());
692
693        let mut alloc_hashmap = AllocationInfo::new(0x2000, 512);
694        alloc_hashmap.type_name = Some("HashMap<K, V>".to_string());
695
696        let mut alloc_file = AllocationInfo::new(0x3000, 128);
697        alloc_file.type_name = Some("File".to_string());
698
699        let mut alloc_regex = AllocationInfo::new(0x4000, 64);
700        alloc_regex.type_name = Some("Regex".to_string());
701
702        let allocations = vec![alloc_vec, alloc_hashmap, alloc_file, alloc_regex];
703
704        let result = manager.analyze_system_libraries(&allocations);
705
706        assert_eq!(
707            result.std_collections.allocation_count, 2,
708            "Vec and HashMap"
709        );
710        assert_eq!(result.std_collections.total_bytes, 256 + 512);
711        assert_eq!(result.file_system.allocation_count, 1, "File");
712        assert_eq!(result.file_system.total_bytes, 128);
713        assert_eq!(result.regex_engine.allocation_count, 1, "Regex");
714        assert_eq!(result.regex_engine.total_bytes, 64);
715        assert_eq!(result.async_runtime.allocation_count, 0, "no async types");
716    }
717
718    #[test]
719    fn test_analyze_concurrency_safety() {
720        let manager = AnalysisManager::new();
721
722        let mut alloc_arc = AllocationInfo::new(0x1000, 64);
723        alloc_arc.type_name = Some("Arc<Mutex<i32>>".to_string());
724
725        let mut alloc_mutex = AllocationInfo::new(0x2000, 128);
726        alloc_mutex.type_name = Some("Mutex<String>".to_string());
727
728        let mut alloc_atomic = AllocationInfo::new(0x3000, 8);
729        alloc_atomic.type_name = Some("AtomicUsize".to_string());
730
731        let allocations = vec![alloc_arc, alloc_mutex, alloc_atomic];
732
733        let result = manager.analyze_concurrency_safety(&allocations);
734
735        assert_eq!(result.thread_safety_allocations, 2, "Arc and Mutex");
736        assert_eq!(result.arc_shared, 1, "Arc");
737        assert_eq!(result.atomic_operations, 1, "AtomicUsize");
738        assert_eq!(result.shared_memory_bytes, 64 + 128);
739        assert_eq!(result.lock_contention_risk, "None", "single thread");
740    }
741
742    #[test]
743    fn test_analyze_concurrency_safety_multi_thread() {
744        let manager = AnalysisManager::new();
745
746        // Note: We can't easily create different ThreadId values,
747        // so this tests the single-thread case which is "None" risk.
748        let mut alloc_arc = AllocationInfo::new(0x1000, 64);
749        alloc_arc.type_name = Some("Arc<i32>".to_string());
750
751        let allocations = vec![alloc_arc];
752
753        let result = manager.analyze_concurrency_safety(&allocations);
754        assert_eq!(result.arc_shared, 1);
755    }
756
757    #[test]
758    fn test_get_unsafe_ffi_tracker() {
759        let manager = AnalysisManager::new();
760
761        let _tracker = manager.get_unsafe_ffi_tracker();
762
763        // Test that tracker is returned successfully
764    }
765
766    #[test]
767    fn test_get_unsafe_ffi_stats() {
768        let manager = AnalysisManager::new();
769
770        let stats = manager.get_unsafe_ffi_stats();
771
772        // Test that stats are returned with valid default values
773        assert_eq!(stats.total_operations, 0);
774        assert_eq!(stats.ffi_calls, 0);
775        assert_eq!(stats.raw_pointer_operations, 0);
776        assert_eq!(stats.memory_violations, 0);
777        assert!(stats.operations.is_empty());
778    }
779
780    #[test]
781    fn test_analyze_circular_references() {
782        let manager = AnalysisManager::new();
783        // Without smart pointer info, no circular references can be detected.
784        // This tests the zero case.
785        let allocations = vec![
786            AllocationInfo::new(0x4000, 128),
787            AllocationInfo::new(0x5000, 256),
788        ];
789
790        let result = manager.analyze_circular_references(&allocations);
791        assert_eq!(result.total_smart_pointers, 0, "no smart pointer info");
792        assert_eq!(result.circular_references.len(), 0);
793        assert_eq!(result.pointers_in_cycles, 0);
794        assert_eq!(result.total_leaked_memory, 0);
795    }
796
797    #[test]
798    fn test_analyze_borrow_patterns() {
799        let manager = AnalysisManager::new();
800        let allocations = vec![AllocationInfo::new(0x7000, 1024)];
801
802        let result = manager.analyze_borrow_patterns(&allocations);
803
804        // Test that borrow pattern analysis returns valid results
805        assert!(result.patterns.is_empty());
806        assert_eq!(result.total_events, 0);
807        assert!(result.analysis_timestamp > 0);
808    }
809
810    #[test]
811    fn test_analyze_generic_types() {
812        let manager = AnalysisManager::new();
813        let allocations = vec![AllocationInfo::new(0x8000, 256)];
814
815        let result = manager.analyze_generic_types(&allocations);
816
817        // Test that generic type analysis returns valid results
818        assert_eq!(result.total_instances, 0);
819        assert_eq!(result.unique_base_types, 0);
820        assert_eq!(result.total_instantiations, 0);
821        assert_eq!(result.constraint_violations, 0);
822        assert!(result.most_used_types.is_empty());
823    }
824
825    #[test]
826    fn test_analyze_async_patterns() {
827        let manager = AnalysisManager::new();
828        let allocations = vec![AllocationInfo::new(0x9000, 2048)];
829
830        let result = manager.analyze_async_patterns(&allocations);
831
832        // Test that async pattern analysis returns valid results
833        assert!(result.patterns.is_empty());
834        assert_eq!(result.total_futures_analyzed, 0);
835        assert!(result.analysis_timestamp > 0);
836    }
837
838    #[test]
839    fn test_analyze_closure_patterns() {
840        let manager = AnalysisManager::new();
841        let allocations = vec![AllocationInfo::new(0xa000, 128)];
842
843        let result = manager.analyze_closure_patterns(&allocations);
844
845        // Test that closure pattern analysis returns valid results
846        assert!(result.detected_closures.is_empty());
847        assert_eq!(result.capture_statistics.total_closures, 0);
848        assert!(result.optimization_suggestions.is_empty());
849        assert!(result.lifetime_analysis.lifetime_patterns.is_empty());
850        assert!(result.analysis_timestamp > 0);
851    }
852
853    #[test]
854    fn test_analyze_lifecycle_patterns() {
855        let manager = AnalysisManager::new();
856        let allocations = vec![AllocationInfo::new(0xb000, 512)];
857
858        let result = manager.analyze_lifecycle_patterns(&allocations);
859
860        // Test that lifecycle pattern analysis returns valid results
861        assert!(result.drop_events.is_empty());
862        assert!(result.raii_patterns.is_empty());
863        assert!(result.borrow_analysis.borrow_patterns.is_empty());
864        assert!(result.closure_captures.is_empty());
865        assert!(result.analysis_timestamp > 0);
866    }
867
868    #[test]
869    fn test_perform_comprehensive_analysis() {
870        let manager = AnalysisManager::new();
871        let allocations = vec![
872            AllocationInfo::new(0x1000, 1024),
873            AllocationInfo::new(0x2000, 512),
874            AllocationInfo::new(0x3000, 256),
875        ];
876        let stats = MemoryStats::new();
877
878        let result = manager.perform_comprehensive_analysis(&allocations, &stats);
879
880        assert!(
881            result.fragmentation_analysis.fragmentation_ratio >= 0.0
882                && result.fragmentation_analysis.fragmentation_ratio <= 1.0
883        );
884        assert_eq!(
885            result.system_library_stats.std_collections.allocation_count,
886            0
887        );
888        assert_eq!(result.concurrency_analysis.thread_safety_allocations, 0);
889        assert_eq!(result.unsafe_ffi_stats.total_operations, 0);
890        assert_eq!(result.circular_reference_analysis.total_smart_pointers, 0);
891        assert!(result.borrow_analysis.patterns.is_empty());
892        assert_eq!(result.generic_analysis.total_instances, 0);
893        assert!(result.async_analysis.patterns.is_empty());
894        assert!(result.closure_analysis.detected_closures.is_empty());
895        assert!(result.lifecycle_analysis.drop_events.is_empty());
896        assert_eq!(result.memory_stats.total_allocations, 0);
897        assert!(result.analysis_timestamp > 0);
898    }
899
900    #[test]
901    fn test_backward_compatibility_functions() {
902        let allocations = vec![AllocationInfo::new(0x1000, 1024)];
903
904        // Test backward compatibility functions
905        let frag_result = analyze_fragmentation(&allocations);
906        assert_eq!(frag_result.fragmentation_ratio, 0.0);
907
908        let lib_result = analyze_system_libraries(&allocations);
909        assert_eq!(lib_result.std_collections.allocation_count, 0);
910
911        let conc_result = analyze_concurrency_safety(&allocations);
912        assert_eq!(conc_result.thread_safety_allocations, 0);
913
914        let _tracker = get_global_unsafe_ffi_tracker();
915
916        let stats = get_unsafe_ffi_stats();
917        assert_eq!(stats.total_operations, 0);
918
919        let memory_stats = MemoryStats::new();
920        let comp_result = perform_comprehensive_analysis(&allocations, &memory_stats);
921        assert!(comp_result.analysis_timestamp > 0);
922    }
923
924    #[test]
925    fn test_empty_allocations_analysis() {
926        let manager = AnalysisManager::new();
927        let empty_allocations: Vec<AllocationInfo> = vec![];
928
929        // Test that analysis works with empty allocation list
930        let frag_result = manager.analyze_fragmentation(&empty_allocations);
931        assert_eq!(frag_result.fragmentation_ratio, 0.0);
932
933        let lib_result = manager.analyze_system_libraries(&empty_allocations);
934        assert_eq!(lib_result.std_collections.allocation_count, 0);
935
936        let conc_result = manager.analyze_concurrency_safety(&empty_allocations);
937        assert_eq!(conc_result.thread_safety_allocations, 0);
938
939        let circ_result = manager.analyze_circular_references(&empty_allocations);
940        assert_eq!(circ_result.total_smart_pointers, 0);
941    }
942
943    #[test]
944    fn test_large_allocation_list_analysis() {
945        let manager = AnalysisManager::new();
946        let mut allocations = Vec::new();
947
948        // Create a larger list of allocations to test performance
949        for i in 0..100 {
950            allocations.push(AllocationInfo::new(0x1000 + i * 0x1000, 1024 + i));
951        }
952
953        let stats = MemoryStats::new();
954        let result = manager.perform_comprehensive_analysis(&allocations, &stats);
955
956        // Test that analysis completes successfully with larger datasets
957        assert!(result.analysis_timestamp > 0);
958        assert_eq!(result.memory_stats.total_allocations, 0); // Default stats
959    }
960}