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