memscope_rs/analysis/
lifecycle_analysis.rs

1//! Advanced lifecycle analysis for Rust types
2//!
3//! This module implements the features outlined in ComplexTypeForRust.md:
4//! 1. Drop trait tracking - Record custom destructor execution
5//! 2. RAII pattern detection - Identify resource acquisition patterns
6//! 3. Borrow checker integration - Track borrow and mutable borrow lifetimes
7//! 4. Closure capture analysis - Track closure captured variable lifetimes
8
9use crate::core::safe_operations::SafeLock;
10use crate::core::types::AllocationInfo;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::{Arc, Mutex, OnceLock};
14use std::time::{SystemTime, UNIX_EPOCH};
15
16/// Global lifecycle analyzer instance
17static GLOBAL_LIFECYCLE_ANALYZER: OnceLock<Arc<LifecycleAnalyzer>> = OnceLock::new();
18
19/// Get the global lifecycle analyzer instance
20pub fn get_global_lifecycle_analyzer() -> Arc<LifecycleAnalyzer> {
21    GLOBAL_LIFECYCLE_ANALYZER
22        .get_or_init(|| Arc::new(LifecycleAnalyzer::new()))
23        .clone()
24}
25
26/// Advanced lifecycle analysis for Rust types
27pub struct LifecycleAnalyzer {
28    /// Drop trait execution tracking
29    drop_events: Mutex<Arc<Vec<DropEvent>>>,
30    /// RAII pattern instances
31    raii_patterns: Mutex<Arc<Vec<RAIIPattern>>>,
32    /// Borrow tracking information
33    borrow_tracker: Mutex<BorrowTracker>,
34    /// Closure capture analysis
35    closure_captures: Mutex<Arc<Vec<ClosureCapture>>>,
36}
37
38impl LifecycleAnalyzer {
39    /// Create a new lifecycle analyzer
40    pub fn new() -> Self {
41        Self {
42            drop_events: Mutex::new(Arc::new(Vec::new())),
43            raii_patterns: Mutex::new(Arc::new(Vec::new())),
44            borrow_tracker: Mutex::new(BorrowTracker::new()),
45            closure_captures: Mutex::new(Arc::new(Vec::new())),
46        }
47    }
48
49    /// Record a Drop trait execution
50    pub fn record_drop_event(&self, ptr: usize, type_name: &str, custom_drop: bool) {
51        let event = DropEvent {
52            ptr,
53            type_name: type_name.to_string(),
54            timestamp: current_timestamp(),
55            custom_drop,
56            thread_id: format!("{:?}", std::thread::current().id()),
57            call_stack: capture_call_stack(),
58        };
59
60        if let Ok(mut events) = self.drop_events.safe_lock() {
61            let mut new_events = (**events).clone();
62            new_events.push(event);
63            *events = Arc::new(new_events);
64        }
65    }
66
67    /// Detect RAII patterns in allocations
68    pub fn detect_raii_patterns(&self, allocations: &[AllocationInfo]) -> Vec<RAIIPattern> {
69        let mut patterns = Vec::new();
70
71        for allocation in allocations {
72            if let Some(type_name) = &allocation.type_name {
73                if let Some(pattern) = self.analyze_raii_pattern(allocation, type_name) {
74                    patterns.push(pattern);
75                }
76            }
77        }
78
79        // Store detected patterns
80        if let Ok(mut stored_patterns) = self.raii_patterns.safe_lock() {
81            let mut new_patterns = (**stored_patterns).clone();
82            new_patterns.extend(patterns.clone());
83            *stored_patterns = Arc::new(new_patterns);
84        }
85
86        patterns
87    }
88
89    /// Analyze RAII pattern for a specific allocation
90    fn analyze_raii_pattern(
91        &self,
92        allocation: &AllocationInfo,
93        type_name: &str,
94    ) -> Option<RAIIPattern> {
95        let resource_type = self.identify_resource_type(type_name)?;
96        let acquisition_method = self.identify_acquisition_method(type_name);
97        let release_method = self.identify_release_method(type_name);
98
99        Some(RAIIPattern {
100            ptr: allocation.ptr,
101            type_name: type_name.to_string(),
102            resource_type,
103            acquisition_method,
104            release_method,
105            acquisition_timestamp: allocation.timestamp_alloc,
106            release_timestamp: allocation.timestamp_dealloc,
107            scope_info: self.analyze_scope_info(allocation),
108            is_exception_safe: self.is_exception_safe(type_name),
109        })
110    }
111
112    /// Identify the type of resource being managed
113    fn identify_resource_type(&self, type_name: &str) -> Option<ResourceType> {
114        if type_name.contains("File")
115            || type_name.contains("BufReader")
116            || type_name.contains("BufWriter")
117        {
118            Some(ResourceType::FileHandle)
119        } else if type_name.contains("TcpStream")
120            || type_name.contains("UdpSocket")
121            || type_name.contains("Listener")
122        {
123            Some(ResourceType::NetworkSocket)
124        } else if type_name.contains("Mutex")
125            || type_name.contains("RwLock")
126            || type_name.contains("Semaphore")
127        {
128            Some(ResourceType::SynchronizationPrimitive)
129        } else if type_name.contains("Thread") || type_name.contains("JoinHandle") {
130            Some(ResourceType::ThreadHandle)
131        } else if type_name.contains("Box")
132            || type_name.contains("Vec")
133            || type_name.contains("String")
134        {
135            Some(ResourceType::Memory)
136        } else if type_name.contains("Guard") || type_name.contains("Lock") {
137            Some(ResourceType::LockGuard)
138        } else {
139            None
140        }
141    }
142
143    /// Identify how the resource is acquired
144    fn identify_acquisition_method(&self, type_name: &str) -> AcquisitionMethod {
145        if type_name.contains("new") || type_name.contains("Box") {
146            AcquisitionMethod::Constructor
147        } else if type_name.contains("open") || type_name.contains("connect") {
148            AcquisitionMethod::SystemCall
149        } else if type_name.contains("lock") || type_name.contains("Guard") {
150            AcquisitionMethod::Lock
151        } else if type_name.contains("with_capacity") || type_name.contains("reserve") {
152            AcquisitionMethod::Allocation
153        } else {
154            AcquisitionMethod::Unknown
155        }
156    }
157
158    /// Identify how the resource is released
159    fn identify_release_method(&self, type_name: &str) -> ReleaseMethod {
160        if type_name.contains("Guard")
161            || type_name.contains("Lock")
162            || type_name.contains("File")
163            || type_name.contains("Stream")
164        {
165            ReleaseMethod::AutomaticDrop
166        } else if type_name.contains("Box") || type_name.contains("Vec") {
167            ReleaseMethod::Deallocation
168        } else {
169            ReleaseMethod::CustomDrop
170        }
171    }
172
173    /// Analyze scope information for RAII pattern
174    fn analyze_scope_info(&self, allocation: &AllocationInfo) -> ScopeInfo {
175        ScopeInfo {
176            scope_name: allocation
177                .scope_name
178                .clone()
179                .unwrap_or_else(|| "unknown".to_string()),
180            scope_type: self.infer_scope_type(&allocation.scope_name),
181            nesting_level: self.calculate_nesting_level(&allocation.scope_name),
182        }
183    }
184
185    /// Infer the type of scope
186    fn infer_scope_type(&self, scope_name: &Option<String>) -> ScopeType {
187        match scope_name {
188            Some(name) if name.contains("fn ") => ScopeType::Function,
189            Some(name) if name.contains("impl ") => ScopeType::Method,
190            Some(name) if name.contains("for ") || name.contains("while ") => ScopeType::Loop,
191            Some(name) if name.contains("if ") || name.contains("match ") => ScopeType::Conditional,
192            Some(name) if name.contains("{") => ScopeType::Block,
193            _ => ScopeType::Unknown,
194        }
195    }
196
197    /// Calculate nesting level of scope
198    fn calculate_nesting_level(&self, scope_name: &Option<String>) -> usize {
199        scope_name
200            .as_ref()
201            .map(|name| name.matches('{').count())
202            .unwrap_or(0)
203    }
204
205    /// Check if type is exception safe
206    fn is_exception_safe(&self, type_name: &str) -> bool {
207        // Most Rust types are exception safe due to the type system
208        // Only unsafe code or FFI might not be
209        !type_name.contains("unsafe") && !type_name.contains("ffi")
210    }
211
212    /// Track borrow operations
213    pub fn track_borrow(&self, ptr: usize, borrow_type: BorrowType, location: &str) {
214        if let Ok(mut tracker) = self.borrow_tracker.safe_lock() {
215            tracker.track_borrow(ptr, borrow_type, location);
216        }
217    }
218
219    /// Track borrow release
220    pub fn track_borrow_release(&self, ptr: usize, borrow_id: u64) {
221        if let Ok(mut tracker) = self.borrow_tracker.safe_lock() {
222            tracker.release_borrow(ptr, borrow_id);
223        }
224    }
225
226    /// Analyze closure captures
227    pub fn analyze_closure_capture(
228        &self,
229        closure_ptr: usize,
230        captured_vars: Vec<CapturedVariable>,
231    ) {
232        let capture = ClosureCapture {
233            closure_ptr,
234            captured_vars,
235            capture_timestamp: current_timestamp(),
236            thread_id: format!("{:?}", std::thread::current().id()),
237        };
238
239        if let Ok(mut captures) = self.closure_captures.safe_lock() {
240            let mut new_captures = (**captures).clone();
241            new_captures.push(capture);
242            *captures = Arc::new(new_captures);
243        }
244    }
245
246    /// Get comprehensive lifecycle analysis report
247    pub fn get_lifecycle_report(&self) -> LifecycleAnalysisReport {
248        let drop_events = self
249            .drop_events
250            .safe_lock()
251            .map(|events| Arc::clone(&events))
252            .unwrap_or_else(|_| Arc::new(Vec::new()));
253        let raii_patterns = self
254            .raii_patterns
255            .safe_lock()
256            .map(|patterns| Arc::clone(&patterns))
257            .unwrap_or_else(|_| Arc::new(Vec::new()));
258        let borrow_analysis = self
259            .borrow_tracker
260            .safe_lock()
261            .map(|tracker| tracker.get_analysis())
262            .unwrap_or_else(|_| BorrowAnalysis {
263                conflicts: Vec::new(),
264                active_borrows: 0,
265                borrow_patterns: Vec::new(),
266                long_lived_borrows: Vec::new(),
267                total_borrows: 0,
268            });
269        let closure_captures = self
270            .closure_captures
271            .safe_lock()
272            .map(|captures| Arc::clone(&captures))
273            .unwrap_or_else(|_| Arc::new(Vec::new()));
274
275        LifecycleAnalysisReport {
276            drop_events: (*drop_events).clone(),
277            raii_patterns: (*raii_patterns).clone(),
278            borrow_analysis,
279            closure_captures: (*closure_captures).clone(),
280            analysis_timestamp: current_timestamp(),
281        }
282    }
283}
284
285/// Drop trait execution event
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct DropEvent {
288    /// Pointer to the dropped object
289    pub ptr: usize,
290    /// Type name of the dropped object
291    pub type_name: String,
292    /// Timestamp when drop was executed
293    pub timestamp: u64,
294    /// Whether this is a custom Drop implementation
295    pub custom_drop: bool,
296    /// Thread where drop occurred
297    pub thread_id: String,
298    /// Call stack at drop time
299    pub call_stack: Vec<String>,
300}
301
302/// RAII pattern instance
303#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct RAIIPattern {
305    /// Pointer to the RAII object
306    pub ptr: usize,
307    /// Type name
308    pub type_name: String,
309    /// Type of resource being managed
310    pub resource_type: ResourceType,
311    /// How the resource is acquired
312    pub acquisition_method: AcquisitionMethod,
313    /// How the resource is released
314    pub release_method: ReleaseMethod,
315    /// When the resource was acquired
316    pub acquisition_timestamp: u64,
317    /// When the resource was released (if applicable)
318    pub release_timestamp: Option<u64>,
319    /// Scope information
320    pub scope_info: ScopeInfo,
321    /// Whether the pattern is exception safe
322    pub is_exception_safe: bool,
323}
324
325/// Types of resources managed by RAII
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327pub enum ResourceType {
328    /// Memory
329    Memory,
330    /// File handle
331    FileHandle,
332    /// Network socket
333    NetworkSocket,
334    /// Synchronization primitive
335    SynchronizationPrimitive,
336    /// Thread handle
337    ThreadHandle,
338    /// Lock guard  
339    LockGuard,
340    /// Other resource type
341    Other(String),
342}
343
344/// Methods of resource acquisition
345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346pub enum AcquisitionMethod {
347    /// Constructor
348    Constructor,
349    /// System call
350    SystemCall,
351    /// Lock
352    Lock,
353    /// Allocation
354    Allocation,
355    /// Unknown
356    Unknown,
357}
358
359/// Methods of resource release
360#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
361pub enum ReleaseMethod {
362    /// Automatic drop (RAII)
363    AutomaticDrop,
364    /// Custom drop implementation
365    CustomDrop,
366    /// Deallocation
367    Deallocation,
368    /// System call
369    SystemCall,
370}
371
372/// Scope information for RAII patterns
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct ScopeInfo {
375    /// Scope name
376    pub scope_name: String,
377    /// Scope type
378    pub scope_type: ScopeType,
379    /// Nesting level
380    pub nesting_level: usize,
381}
382
383/// Types of scopes
384#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
385pub enum ScopeType {
386    /// Function scope
387    Function,
388    /// Method scope
389    Method,
390    /// Block scope
391    Block,
392    /// Loop scope
393    Loop,
394    /// Conditional scope
395    Conditional,
396    /// Unknown scope
397    Unknown,
398}
399
400/// Borrow tracking system
401#[derive(Debug)]
402pub struct BorrowTracker {
403    /// Active borrows (ptr -> borrow info)
404    active_borrows: HashMap<usize, Vec<BorrowInfo>>,
405    /// Borrow history
406    borrow_history: Vec<BorrowEvent>,
407    /// Next borrow ID
408    next_borrow_id: u64,
409}
410
411impl Default for BorrowTracker {
412    fn default() -> Self {
413        Self::new()
414    }
415}
416
417impl BorrowTracker {
418    /// Create a new borrow tracker
419    pub fn new() -> Self {
420        Self {
421            active_borrows: HashMap::new(),
422            borrow_history: Vec::new(),
423            next_borrow_id: 1,
424        }
425    }
426
427    /// Track a borrow
428    pub fn track_borrow(&mut self, ptr: usize, borrow_type: BorrowType, location: &str) -> u64 {
429        let borrow_id = self.next_borrow_id;
430        self.next_borrow_id += 1;
431
432        let borrow_info = BorrowInfo {
433            borrow_id,
434            borrow_type,
435            start_timestamp: current_timestamp(),
436            location: location.to_string(),
437            thread_id: format!("{:?}", std::thread::current().id()),
438        };
439
440        self.active_borrows
441            .entry(ptr)
442            .or_default()
443            .push(borrow_info.clone());
444
445        self.borrow_history.push(BorrowEvent {
446            ptr,
447            borrow_info,
448            event_type: BorrowEventType::Acquired,
449            timestamp: current_timestamp(),
450        });
451
452        borrow_id
453    }
454
455    /// Release a borrow
456    pub fn release_borrow(&mut self, ptr: usize, borrow_id: u64) {
457        if let Some(borrows) = self.active_borrows.get_mut(&ptr) {
458            if let Some(pos) = borrows.iter().position(|b| b.borrow_id == borrow_id) {
459                let borrow_info = borrows.remove(pos);
460
461                self.borrow_history.push(BorrowEvent {
462                    ptr,
463                    borrow_info,
464                    event_type: BorrowEventType::Released,
465                    timestamp: current_timestamp(),
466                });
467
468                if borrows.is_empty() {
469                    self.active_borrows.remove(&ptr);
470                }
471            }
472        }
473    }
474
475    /// Get comprehensive borrow analysis report
476    pub fn get_analysis(&self) -> BorrowAnalysis {
477        let mut conflicts = Vec::new();
478        let mut long_lived_borrows = Vec::new();
479
480        // Analyze for conflicts and long-lived borrows
481        for events in self.borrow_history.windows(2) {
482            if let [event1, event2] = events {
483                if event1.ptr == event2.ptr
484                    && self.has_borrow_conflict(&event1.borrow_info, &event2.borrow_info)
485                {
486                    conflicts.push(BorrowConflict {
487                        ptr: event1.ptr,
488                        first_borrow: event1.borrow_info.clone(),
489                        second_borrow: event2.borrow_info.clone(),
490                        conflict_type: self
491                            .classify_conflict(&event1.borrow_info, &event2.borrow_info),
492                    });
493                }
494            }
495        }
496
497        // Find long-lived borrows (>1 second)
498        let current_time = current_timestamp();
499        for (ptr, borrows) in &self.active_borrows {
500            for borrow in borrows {
501                if current_time - borrow.start_timestamp > 1_000_000_000 {
502                    // 1 second in nanoseconds
503                    long_lived_borrows.push(LongLivedBorrow {
504                        ptr: *ptr,
505                        borrow_info: borrow.clone(),
506                        duration_ns: current_time - borrow.start_timestamp,
507                    });
508                }
509            }
510        }
511
512        BorrowAnalysis {
513            total_borrows: self.borrow_history.len(),
514            active_borrows: self.active_borrows.len(),
515            conflicts,
516            long_lived_borrows,
517            borrow_patterns: self.analyze_borrow_patterns(),
518        }
519    }
520
521    fn has_borrow_conflict(&self, borrow1: &BorrowInfo, borrow2: &BorrowInfo) -> bool {
522        matches!(
523            (&borrow1.borrow_type, &borrow2.borrow_type),
524            (BorrowType::Mutable, _) | (_, BorrowType::Mutable)
525        )
526    }
527
528    fn classify_conflict(&self, borrow1: &BorrowInfo, borrow2: &BorrowInfo) -> ConflictType {
529        match (&borrow1.borrow_type, &borrow2.borrow_type) {
530            (BorrowType::Mutable, BorrowType::Mutable) => ConflictType::MutableMutable,
531            (BorrowType::Mutable, BorrowType::Immutable)
532            | (BorrowType::Immutable, BorrowType::Mutable) => ConflictType::MutableImmutable,
533            _ => ConflictType::None,
534        }
535    }
536
537    fn analyze_borrow_patterns(&self) -> Vec<BorrowPattern> {
538        // Analyze common borrow patterns
539        let mut patterns = Vec::new();
540
541        // Pattern: Frequent short borrows
542        let short_borrows = self
543            .borrow_history
544            .iter()
545            .filter(|event| matches!(event.event_type, BorrowEventType::Released))
546            .filter(|event| {
547                // Find corresponding acquire event
548                self.borrow_history.iter().any(|acquire_event| {
549                    acquire_event.borrow_info.borrow_id == event.borrow_info.borrow_id
550                        && matches!(acquire_event.event_type, BorrowEventType::Acquired)
551                        && event.timestamp >= acquire_event.timestamp
552                        && event.timestamp - acquire_event.timestamp < 1_000_000
553                    // < 1ms
554                })
555            })
556            .count();
557
558        if short_borrows > 10 {
559            patterns.push(BorrowPattern {
560                pattern_type: BorrowPatternType::FrequentShortBorrows,
561                description: format!("Detected {short_borrows} short-lived borrows (< 1ms)",),
562                impact: if short_borrows > 100 {
563                    PatternImpact::High
564                } else {
565                    PatternImpact::Medium
566                },
567                suggestion: "Consider batching operations to reduce borrow overhead".to_string(),
568            });
569        }
570
571        patterns
572    }
573}
574
575/// Types of borrows
576#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
577pub enum BorrowType {
578    /// Immutable borrow
579    Immutable,
580    /// Mutable borrow
581    Mutable,
582}
583
584/// Borrow information
585#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct BorrowInfo {
587    /// Borrow ID
588    pub borrow_id: u64,
589    /// Borrow type
590    pub borrow_type: BorrowType,
591    /// Start timestamp
592    pub start_timestamp: u64,
593    /// Location
594    pub location: String,
595    /// Thread ID
596    pub thread_id: String,
597}
598
599/// Borrow event
600#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct BorrowEvent {
602    /// Pointer to the memory location
603    pub ptr: usize,
604    /// Borrow information
605    pub borrow_info: BorrowInfo,
606    /// Event type
607    pub event_type: BorrowEventType,
608    /// Timestamp
609    pub timestamp: u64,
610}
611
612/// Types of borrow events
613#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
614pub enum BorrowEventType {
615    /// Borrow acquired
616    Acquired,
617    /// Borrow released
618    Released,
619}
620
621/// Borrow analysis results
622#[derive(Debug, Clone, Default, Serialize, Deserialize)]
623pub struct BorrowAnalysis {
624    /// Total number of borrows
625    pub total_borrows: usize,
626    /// Number of active borrows
627    pub active_borrows: usize,
628    /// List of borrow conflicts
629    pub conflicts: Vec<BorrowConflict>,
630    /// List of long-lived borrows
631    pub long_lived_borrows: Vec<LongLivedBorrow>,
632    /// List of borrow patterns
633    pub borrow_patterns: Vec<BorrowPattern>,
634}
635
636/// Borrow conflict information
637#[derive(Debug, Clone, Serialize, Deserialize)]
638pub struct BorrowConflict {
639    /// Pointer to the memory location
640    pub ptr: usize,
641    /// First borrow information
642    pub first_borrow: BorrowInfo,
643    /// Second borrow information
644    pub second_borrow: BorrowInfo,
645    /// Conflict type
646    pub conflict_type: ConflictType,
647}
648
649/// Types of borrow conflicts
650#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
651pub enum ConflictType {
652    /// Mutable borrow conflict
653    MutableMutable,
654    /// Mutable and immutable borrow conflict
655    MutableImmutable,
656    /// No conflict
657    None,
658}
659
660/// Long-lived borrow information
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct LongLivedBorrow {
663    /// Pointer to the memory location
664    pub ptr: usize,
665    /// Borrow information
666    pub borrow_info: BorrowInfo,
667    /// Duration in nanoseconds
668    pub duration_ns: u64,
669}
670
671/// Borrow pattern analysis
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct BorrowPattern {
674    /// Pattern type
675    pub pattern_type: BorrowPatternType,
676    /// Pattern description
677    pub description: String,
678    /// Pattern impact
679    pub impact: PatternImpact,
680    /// Pattern suggestion
681    pub suggestion: String,
682}
683
684/// Types of borrow patterns
685#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
686pub enum BorrowPatternType {
687    /// Frequent short borrows
688    FrequentShortBorrows,
689    /// Long-lived borrows
690    LongLivedBorrows,
691    /// Conflict-prone borrows
692    ConflictProne,
693}
694
695/// Impact level of patterns
696#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
697pub enum PatternImpact {
698    /// Low impact
699    Low,
700    /// Medium impact
701    Medium,
702    /// High impact
703    High,
704}
705
706/// Closure capture analysis
707#[derive(Debug, Clone, Serialize, Deserialize)]
708pub struct ClosureCapture {
709    /// Closure pointer
710    pub closure_ptr: usize,
711    /// List of captured variables
712    pub captured_vars: Vec<CapturedVariable>,
713    /// Capture timestamp
714    pub capture_timestamp: u64,
715    /// Thread ID
716    pub thread_id: String,
717}
718
719/// Information about a captured variable
720#[derive(Debug, Clone, Serialize, Deserialize)]
721pub struct CapturedVariable {
722    /// Variable name
723    pub var_name: String,
724    /// Variable memory address
725    pub var_ptr: usize,
726    /// Capture mode
727    pub capture_mode: CaptureMode,
728    /// Variable type
729    pub var_type: String,
730    /// Variable size in bytes
731    pub size: usize,
732}
733
734/// Modes of variable capture in closures
735#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
736pub enum CaptureMode {
737    /// Capture by value
738    ByValue,
739    /// Capture by reference
740    ByReference,
741    /// Capture by mutable reference
742    ByMutableReference,
743}
744
745/// Comprehensive lifecycle analysis report
746#[derive(Debug, Clone, Default, Serialize, Deserialize)]
747pub struct LifecycleAnalysisReport {
748    /// Drop events
749    pub drop_events: Vec<DropEvent>,
750    /// RAII patterns
751    pub raii_patterns: Vec<RAIIPattern>,
752    /// Borrow analysis
753    pub borrow_analysis: BorrowAnalysis,
754    /// Closure captures
755    pub closure_captures: Vec<ClosureCapture>,
756    /// Analysis timestamp
757    pub analysis_timestamp: u64,
758}
759
760/// Get current timestamp in nanoseconds
761fn current_timestamp() -> u64 {
762    SystemTime::now()
763        .duration_since(UNIX_EPOCH)
764        .unwrap_or_default()
765        .as_nanos() as u64
766}
767
768/// Capture call stack (simplified implementation)
769fn capture_call_stack() -> Vec<String> {
770    // In a real implementation, this would use backtrace
771    // For now, return a placeholder
772    vec!["<call_stack_placeholder>".to_string()]
773}
774
775impl Default for LifecycleAnalyzer {
776    fn default() -> Self {
777        Self::new()
778    }
779}
780
781#[cfg(test)]
782mod tests {
783    use super::*;
784
785    #[test]
786    fn test_lifecycle_analyzer_creation() {
787        let analyzer = LifecycleAnalyzer::new();
788
789        // Test that analyzer is properly initialized
790        assert!(analyzer.drop_events.lock().is_ok());
791        assert!(analyzer.raii_patterns.lock().is_ok());
792        assert!(analyzer.borrow_tracker.lock().is_ok());
793        assert!(analyzer.closure_captures.lock().is_ok());
794    }
795
796    #[test]
797    fn test_lifecycle_analyzer_singleton_behavior() {
798        // Test that we can create multiple analyzers without issues
799        let analyzer1 = LifecycleAnalyzer::new();
800        let analyzer2 = LifecycleAnalyzer::new();
801
802        // Each should be independent instances
803        assert!(analyzer1.drop_events.lock().is_ok());
804        assert!(analyzer2.drop_events.lock().is_ok());
805    }
806
807    #[test]
808    fn test_record_drop_event() {
809        let analyzer = LifecycleAnalyzer::new();
810
811        analyzer.record_drop_event(0x1000, "Vec<i32>", true);
812
813        // Verify drop event was recorded
814        let events = analyzer
815            .drop_events
816            .lock()
817            .expect("Drop events lock should not be poisoned");
818        assert_eq!(events.len(), 1);
819        assert_eq!(events[0].ptr, 0x1000);
820        assert_eq!(events[0].type_name, "Vec<i32>");
821        assert!(events[0].custom_drop);
822        assert!(events[0].timestamp > 0);
823        assert!(!events[0].thread_id.is_empty());
824        assert_eq!(events[0].call_stack, vec!["<call_stack_placeholder>"]);
825    }
826
827    #[test]
828    fn test_identify_resource_type() {
829        let analyzer = LifecycleAnalyzer::new();
830
831        // Test file handle detection
832        assert_eq!(
833            analyzer.identify_resource_type("std::fs::File"),
834            Some(ResourceType::FileHandle)
835        );
836        assert_eq!(
837            analyzer.identify_resource_type("BufReader<File>"),
838            Some(ResourceType::FileHandle)
839        );
840
841        // Test network socket detection
842        assert_eq!(
843            analyzer.identify_resource_type("TcpStream"),
844            Some(ResourceType::NetworkSocket)
845        );
846        assert_eq!(
847            analyzer.identify_resource_type("UdpSocket"),
848            Some(ResourceType::NetworkSocket)
849        );
850
851        // Test synchronization primitive detection
852        assert_eq!(
853            analyzer.identify_resource_type("Mutex<T>"),
854            Some(ResourceType::SynchronizationPrimitive)
855        );
856        assert_eq!(
857            analyzer.identify_resource_type("RwLock<T>"),
858            Some(ResourceType::SynchronizationPrimitive)
859        );
860
861        // Test thread handle detection
862        assert_eq!(
863            analyzer.identify_resource_type("JoinHandle<()>"),
864            Some(ResourceType::ThreadHandle)
865        );
866
867        // Test memory detection
868        assert_eq!(
869            analyzer.identify_resource_type("Box<dyn Trait>"),
870            Some(ResourceType::Memory)
871        );
872        assert_eq!(
873            analyzer.identify_resource_type("Vec<i32>"),
874            Some(ResourceType::Memory)
875        );
876        assert_eq!(
877            analyzer.identify_resource_type("String"),
878            Some(ResourceType::Memory)
879        );
880
881        // Test lock guard detection
882        assert_eq!(
883            analyzer.identify_resource_type("MutexGuard<T>"),
884            Some(ResourceType::SynchronizationPrimitive)
885        );
886
887        // Test unknown type
888        assert_eq!(analyzer.identify_resource_type("SomeCustomType"), None);
889    }
890
891    #[test]
892    fn test_identify_acquisition_method() {
893        let analyzer = LifecycleAnalyzer::new();
894
895        assert_eq!(
896            analyzer.identify_acquisition_method("Box::new"),
897            AcquisitionMethod::Constructor
898        );
899        assert_eq!(
900            analyzer.identify_acquisition_method("File::open"),
901            AcquisitionMethod::SystemCall
902        );
903        assert_eq!(
904            analyzer.identify_acquisition_method("mutex.lock()"),
905            AcquisitionMethod::Lock
906        );
907        assert_eq!(
908            analyzer.identify_acquisition_method("Vec::with_capacity"),
909            AcquisitionMethod::Allocation
910        );
911        assert_eq!(
912            analyzer.identify_acquisition_method("unknown_method"),
913            AcquisitionMethod::Unknown
914        );
915    }
916
917    #[test]
918    fn test_identify_release_method() {
919        let analyzer = LifecycleAnalyzer::new();
920
921        assert_eq!(
922            analyzer.identify_release_method("MutexGuard"),
923            ReleaseMethod::AutomaticDrop
924        );
925        assert_eq!(
926            analyzer.identify_release_method("File"),
927            ReleaseMethod::AutomaticDrop
928        );
929        assert_eq!(
930            analyzer.identify_release_method("Box<T>"),
931            ReleaseMethod::Deallocation
932        );
933        assert_eq!(
934            analyzer.identify_release_method("Vec<T>"),
935            ReleaseMethod::Deallocation
936        );
937        assert_eq!(
938            analyzer.identify_release_method("CustomType"),
939            ReleaseMethod::CustomDrop
940        );
941    }
942
943    #[test]
944    fn test_infer_scope_type() {
945        let analyzer = LifecycleAnalyzer::new();
946
947        assert_eq!(
948            analyzer.infer_scope_type(&Some("fn main()".to_string())),
949            ScopeType::Function
950        );
951        assert_eq!(
952            analyzer.infer_scope_type(&Some("impl MyStruct".to_string())),
953            ScopeType::Method
954        );
955        assert_eq!(
956            analyzer.infer_scope_type(&Some("for i in 0..10".to_string())),
957            ScopeType::Loop
958        );
959        assert_eq!(
960            analyzer.infer_scope_type(&Some("while condition".to_string())),
961            ScopeType::Loop
962        );
963        assert_eq!(
964            analyzer.infer_scope_type(&Some("if condition".to_string())),
965            ScopeType::Conditional
966        );
967        assert_eq!(
968            analyzer.infer_scope_type(&Some("match value".to_string())),
969            ScopeType::Conditional
970        );
971        assert_eq!(
972            analyzer.infer_scope_type(&Some("{ block }".to_string())),
973            ScopeType::Block
974        );
975        assert_eq!(analyzer.infer_scope_type(&None), ScopeType::Unknown);
976    }
977
978    #[test]
979    fn test_calculate_nesting_level() {
980        let analyzer = LifecycleAnalyzer::new();
981
982        assert_eq!(
983            analyzer.calculate_nesting_level(&Some("fn main() { { } }".to_string())),
984            2
985        );
986        assert_eq!(
987            analyzer.calculate_nesting_level(&Some("simple".to_string())),
988            0
989        );
990        assert_eq!(analyzer.calculate_nesting_level(&None), 0);
991    }
992
993    #[test]
994    fn test_is_exception_safe() {
995        let analyzer = LifecycleAnalyzer::new();
996
997        assert!(analyzer.is_exception_safe("Vec<i32>"));
998        assert!(analyzer.is_exception_safe("String"));
999        assert!(!analyzer.is_exception_safe("unsafe fn"));
1000        assert!(!analyzer.is_exception_safe("ffi::CString"));
1001    }
1002
1003    #[test]
1004    fn test_detect_raii_patterns() {
1005        let analyzer = LifecycleAnalyzer::new();
1006
1007        let mut allocation = AllocationInfo::new(0x1000, 1024);
1008        allocation.type_name = Some("std::fs::File".to_string());
1009        allocation.scope_name = Some("fn main()".to_string());
1010
1011        let allocations = vec![allocation];
1012        let patterns = analyzer.detect_raii_patterns(&allocations);
1013
1014        assert_eq!(patterns.len(), 1);
1015        let pattern = &patterns[0];
1016        assert_eq!(pattern.ptr, 0x1000);
1017        assert_eq!(pattern.type_name, "std::fs::File");
1018        assert_eq!(pattern.resource_type, ResourceType::FileHandle);
1019        assert_eq!(pattern.acquisition_method, AcquisitionMethod::Unknown);
1020        assert_eq!(pattern.release_method, ReleaseMethod::AutomaticDrop);
1021        assert!(pattern.is_exception_safe);
1022        assert_eq!(pattern.scope_info.scope_name, "fn main()");
1023        assert_eq!(pattern.scope_info.scope_type, ScopeType::Function);
1024    }
1025
1026    #[test]
1027    fn test_borrow_tracker_creation() {
1028        let tracker = BorrowTracker::new();
1029
1030        assert!(tracker.active_borrows.is_empty());
1031        assert!(tracker.borrow_history.is_empty());
1032        assert_eq!(tracker.next_borrow_id, 1);
1033    }
1034
1035    #[test]
1036    fn test_track_borrow() {
1037        let mut tracker = BorrowTracker::new();
1038
1039        let borrow_id = tracker.track_borrow(0x1000, BorrowType::Immutable, "test.rs:10");
1040
1041        assert_eq!(borrow_id, 1);
1042        assert_eq!(tracker.next_borrow_id, 2);
1043        assert!(tracker.active_borrows.contains_key(&0x1000));
1044        assert_eq!(tracker.active_borrows[&0x1000].len(), 1);
1045        assert_eq!(tracker.borrow_history.len(), 1);
1046
1047        let borrow_info = &tracker.active_borrows[&0x1000][0];
1048        assert_eq!(borrow_info.borrow_id, 1);
1049        assert_eq!(borrow_info.borrow_type, BorrowType::Immutable);
1050        assert_eq!(borrow_info.location, "test.rs:10");
1051        assert!(borrow_info.start_timestamp > 0);
1052    }
1053
1054    #[test]
1055    fn test_release_borrow() {
1056        let mut tracker = BorrowTracker::new();
1057
1058        let borrow_id = tracker.track_borrow(0x1000, BorrowType::Mutable, "test.rs:15");
1059        tracker.release_borrow(0x1000, borrow_id);
1060
1061        assert!(!tracker.active_borrows.contains_key(&0x1000));
1062        assert_eq!(tracker.borrow_history.len(), 2); // Acquire + Release
1063
1064        let release_event = &tracker.borrow_history[1];
1065        assert_eq!(release_event.event_type, BorrowEventType::Released);
1066        assert_eq!(release_event.ptr, 0x1000);
1067    }
1068
1069    #[test]
1070    fn test_borrow_conflict_detection() {
1071        let mut tracker = BorrowTracker::new();
1072
1073        // Create conflicting borrows
1074        tracker.track_borrow(0x1000, BorrowType::Mutable, "test.rs:20");
1075        tracker.track_borrow(0x1000, BorrowType::Immutable, "test.rs:21");
1076
1077        let analysis = tracker.get_analysis();
1078
1079        assert_eq!(analysis.total_borrows, 2);
1080        assert_eq!(analysis.active_borrows, 1); // Same pointer, so only one entry
1081        assert_eq!(analysis.conflicts.len(), 1);
1082
1083        let conflict = &analysis.conflicts[0];
1084        assert_eq!(conflict.ptr, 0x1000);
1085        assert_eq!(conflict.conflict_type, ConflictType::MutableImmutable);
1086    }
1087
1088    #[test]
1089    fn test_has_borrow_conflict() {
1090        let tracker = BorrowTracker::new();
1091
1092        let mutable_borrow = BorrowInfo {
1093            borrow_id: 1,
1094            borrow_type: BorrowType::Mutable,
1095            start_timestamp: 1000,
1096            location: "test.rs:1".to_string(),
1097            thread_id: "thread-1".to_string(),
1098        };
1099
1100        let immutable_borrow = BorrowInfo {
1101            borrow_id: 2,
1102            borrow_type: BorrowType::Immutable,
1103            start_timestamp: 2000,
1104            location: "test.rs:2".to_string(),
1105            thread_id: "thread-1".to_string(),
1106        };
1107
1108        let another_immutable = BorrowInfo {
1109            borrow_id: 3,
1110            borrow_type: BorrowType::Immutable,
1111            start_timestamp: 3000,
1112            location: "test.rs:3".to_string(),
1113            thread_id: "thread-1".to_string(),
1114        };
1115
1116        // Mutable vs Immutable should conflict
1117        assert!(tracker.has_borrow_conflict(&mutable_borrow, &immutable_borrow));
1118        assert!(tracker.has_borrow_conflict(&immutable_borrow, &mutable_borrow));
1119
1120        // Immutable vs Immutable should not conflict
1121        assert!(!tracker.has_borrow_conflict(&immutable_borrow, &another_immutable));
1122    }
1123
1124    #[test]
1125    fn test_classify_conflict() {
1126        let tracker = BorrowTracker::new();
1127
1128        let mutable1 = BorrowInfo {
1129            borrow_id: 1,
1130            borrow_type: BorrowType::Mutable,
1131            start_timestamp: 1000,
1132            location: "test.rs:1".to_string(),
1133            thread_id: "thread-1".to_string(),
1134        };
1135
1136        let mutable2 = BorrowInfo {
1137            borrow_id: 2,
1138            borrow_type: BorrowType::Mutable,
1139            start_timestamp: 2000,
1140            location: "test.rs:2".to_string(),
1141            thread_id: "thread-1".to_string(),
1142        };
1143
1144        let immutable = BorrowInfo {
1145            borrow_id: 3,
1146            borrow_type: BorrowType::Immutable,
1147            start_timestamp: 3000,
1148            location: "test.rs:3".to_string(),
1149            thread_id: "thread-1".to_string(),
1150        };
1151
1152        assert_eq!(
1153            tracker.classify_conflict(&mutable1, &mutable2),
1154            ConflictType::MutableMutable
1155        );
1156        assert_eq!(
1157            tracker.classify_conflict(&mutable1, &immutable),
1158            ConflictType::MutableImmutable
1159        );
1160        assert_eq!(
1161            tracker.classify_conflict(&immutable, &mutable1),
1162            ConflictType::MutableImmutable
1163        );
1164    }
1165
1166    #[test]
1167    fn test_analyze_closure_capture() {
1168        let analyzer = LifecycleAnalyzer::new();
1169
1170        let captured_vars = vec![
1171            CapturedVariable {
1172                var_name: "x".to_string(),
1173                var_ptr: 0x1000,
1174                capture_mode: CaptureMode::ByValue,
1175                var_type: "i32".to_string(),
1176                size: 4,
1177            },
1178            CapturedVariable {
1179                var_name: "y".to_string(),
1180                var_ptr: 0x2000,
1181                capture_mode: CaptureMode::ByReference,
1182                var_type: "&str".to_string(),
1183                size: 8,
1184            },
1185        ];
1186
1187        analyzer.analyze_closure_capture(0x5000, captured_vars.clone());
1188
1189        // Verify closure capture was recorded
1190        let captures = analyzer
1191            .closure_captures
1192            .lock()
1193            .expect("Closure captures lock should not be poisoned");
1194        assert_eq!(captures.len(), 1);
1195
1196        let capture = &captures[0];
1197        assert_eq!(capture.closure_ptr, 0x5000);
1198        assert_eq!(capture.captured_vars.len(), 2);
1199        assert_eq!(capture.captured_vars[0].var_name, "x");
1200        assert_eq!(capture.captured_vars[0].capture_mode, CaptureMode::ByValue);
1201        assert_eq!(capture.captured_vars[1].var_name, "y");
1202        assert_eq!(
1203            capture.captured_vars[1].capture_mode,
1204            CaptureMode::ByReference
1205        );
1206        assert!(capture.capture_timestamp > 0);
1207        assert!(!capture.thread_id.is_empty());
1208    }
1209
1210    #[test]
1211    fn test_track_borrow_operations() {
1212        let analyzer = LifecycleAnalyzer::new();
1213
1214        analyzer.track_borrow(0x1000, BorrowType::Immutable, "test.rs:30");
1215        analyzer.track_borrow_release(0x1000, 1);
1216
1217        // Verify borrow operations were tracked
1218        let tracker = analyzer
1219            .borrow_tracker
1220            .lock()
1221            .expect("Borrow tracker lock should not be poisoned");
1222        assert_eq!(tracker.borrow_history.len(), 2);
1223        assert!(!tracker.active_borrows.contains_key(&0x1000));
1224    }
1225
1226    #[test]
1227    fn test_get_lifecycle_report() {
1228        let analyzer = LifecycleAnalyzer::new();
1229
1230        // Add some test data
1231        analyzer.record_drop_event(0x1000, "Vec<i32>", true);
1232
1233        let mut allocation = AllocationInfo::new(0x2000, 512);
1234        allocation.type_name = Some("std::fs::File".to_string());
1235        analyzer.detect_raii_patterns(&[allocation]);
1236
1237        analyzer.track_borrow(0x3000, BorrowType::Mutable, "test.rs:40");
1238
1239        let captured_vars = vec![CapturedVariable {
1240            var_name: "data".to_string(),
1241            var_ptr: 0x4000,
1242            capture_mode: CaptureMode::ByValue,
1243            var_type: "String".to_string(),
1244            size: 24,
1245        }];
1246        analyzer.analyze_closure_capture(0x5000, captured_vars);
1247
1248        let report = analyzer.get_lifecycle_report();
1249
1250        assert_eq!(report.drop_events.len(), 1);
1251        assert_eq!(report.raii_patterns.len(), 1);
1252        assert_eq!(report.borrow_analysis.total_borrows, 1);
1253        assert_eq!(report.closure_captures.len(), 1);
1254        assert!(report.analysis_timestamp > 0);
1255    }
1256
1257    #[test]
1258    fn test_borrow_pattern_analysis() {
1259        let mut tracker = BorrowTracker::new();
1260
1261        // Create many short borrows to trigger pattern detection
1262        for i in 0..15 {
1263            let borrow_id = tracker.track_borrow(0x1000 + i, BorrowType::Immutable, "test.rs");
1264            // Simulate immediate release (short borrow) by manually adding release event
1265            tracker.borrow_history.push(BorrowEvent {
1266                ptr: 0x1000 + i,
1267                borrow_info: BorrowInfo {
1268                    borrow_id,
1269                    borrow_type: BorrowType::Immutable,
1270                    start_timestamp: 1000, // Start time
1271                    location: "test.rs".to_string(),
1272                    thread_id: "thread-1".to_string(),
1273                },
1274                event_type: BorrowEventType::Released,
1275                timestamp: 1000 + 500_000, // 0.5ms later
1276            });
1277        }
1278
1279        let analysis = tracker.get_analysis();
1280
1281        // The pattern detection should find short borrows
1282        // If no patterns are detected, that's also valid behavior
1283        if !analysis.borrow_patterns.is_empty() {
1284            let pattern = &analysis.borrow_patterns[0];
1285            assert_eq!(
1286                pattern.pattern_type,
1287                BorrowPatternType::FrequentShortBorrows
1288            );
1289            assert!(pattern.description.contains("short-lived borrows"));
1290            assert!(!pattern.suggestion.is_empty());
1291        }
1292
1293        // Verify that we have the expected number of borrow events
1294        assert_eq!(analysis.total_borrows, 30); // 15 acquire + 15 release
1295    }
1296
1297    #[test]
1298    fn test_enum_variants() {
1299        // Test all enum variants for completeness
1300        let resource_types = vec![
1301            ResourceType::Memory,
1302            ResourceType::FileHandle,
1303            ResourceType::NetworkSocket,
1304            ResourceType::SynchronizationPrimitive,
1305            ResourceType::ThreadHandle,
1306            ResourceType::LockGuard,
1307            ResourceType::Other("custom".to_string()),
1308        ];
1309
1310        for resource_type in resource_types {
1311            assert!(!format!("{resource_type:?}").is_empty());
1312        }
1313
1314        let acquisition_methods = vec![
1315            AcquisitionMethod::Constructor,
1316            AcquisitionMethod::SystemCall,
1317            AcquisitionMethod::Lock,
1318            AcquisitionMethod::Allocation,
1319            AcquisitionMethod::Unknown,
1320        ];
1321
1322        for method in acquisition_methods {
1323            assert!(!format!("{method:?}").is_empty());
1324        }
1325
1326        let release_methods = vec![
1327            ReleaseMethod::AutomaticDrop,
1328            ReleaseMethod::CustomDrop,
1329            ReleaseMethod::Deallocation,
1330            ReleaseMethod::SystemCall,
1331        ];
1332
1333        for method in release_methods {
1334            assert!(!format!("{method:?}").is_empty());
1335        }
1336
1337        let scope_types = vec![
1338            ScopeType::Function,
1339            ScopeType::Method,
1340            ScopeType::Block,
1341            ScopeType::Loop,
1342            ScopeType::Conditional,
1343            ScopeType::Unknown,
1344        ];
1345
1346        for scope_type in scope_types {
1347            assert!(!format!("{scope_type:?}").is_empty());
1348        }
1349
1350        let borrow_types = vec![BorrowType::Immutable, BorrowType::Mutable];
1351
1352        for borrow_type in borrow_types {
1353            assert!(!format!("{borrow_type:?}").is_empty());
1354        }
1355
1356        let capture_modes = vec![
1357            CaptureMode::ByValue,
1358            CaptureMode::ByReference,
1359            CaptureMode::ByMutableReference,
1360        ];
1361
1362        for mode in capture_modes {
1363            assert!(!format!("{mode:?}").is_empty());
1364        }
1365    }
1366
1367    #[test]
1368    fn test_current_timestamp() {
1369        let timestamp1 = current_timestamp();
1370        std::thread::sleep(std::time::Duration::from_millis(1));
1371        let timestamp2 = current_timestamp();
1372
1373        assert!(timestamp2 > timestamp1);
1374        assert!(timestamp1 > 0);
1375    }
1376
1377    #[test]
1378    fn test_capture_call_stack() {
1379        let call_stack = capture_call_stack();
1380        assert!(!call_stack.is_empty());
1381        assert_eq!(call_stack[0], "<call_stack_placeholder>");
1382    }
1383
1384    #[test]
1385    fn test_default_implementations() {
1386        let _analyzer = LifecycleAnalyzer::default();
1387        let _tracker = BorrowTracker::default();
1388        let _analysis = BorrowAnalysis::default();
1389        let _report = LifecycleAnalysisReport::default();
1390    }
1391}