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