1use 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
16static GLOBAL_LIFECYCLE_ANALYZER: OnceLock<Arc<LifecycleAnalyzer>> = OnceLock::new();
18
19pub fn get_global_lifecycle_analyzer() -> Arc<LifecycleAnalyzer> {
21 GLOBAL_LIFECYCLE_ANALYZER
22 .get_or_init(|| Arc::new(LifecycleAnalyzer::new()))
23 .clone()
24}
25
26pub struct LifecycleAnalyzer {
28 drop_events: Mutex<Arc<Vec<DropEvent>>>,
30 raii_patterns: Mutex<Arc<Vec<RAIIPattern>>>,
32 borrow_tracker: Mutex<BorrowTracker>,
34 closure_captures: Mutex<Arc<Vec<ClosureCapture>>>,
36}
37
38impl LifecycleAnalyzer {
39 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 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 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 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 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 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 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 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 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 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 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 fn is_exception_safe(&self, type_name: &str) -> bool {
207 !type_name.contains("unsafe") && !type_name.contains("ffi")
210 }
211
212 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 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 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct DropEvent {
288 pub ptr: usize,
290 pub type_name: String,
292 pub timestamp: u64,
294 pub custom_drop: bool,
296 pub thread_id: String,
298 pub call_stack: Vec<String>,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct RAIIPattern {
305 pub ptr: usize,
307 pub type_name: String,
309 pub resource_type: ResourceType,
311 pub acquisition_method: AcquisitionMethod,
313 pub release_method: ReleaseMethod,
315 pub acquisition_timestamp: u64,
317 pub release_timestamp: Option<u64>,
319 pub scope_info: ScopeInfo,
321 pub is_exception_safe: bool,
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
327pub enum ResourceType {
328 Memory,
330 FileHandle,
332 NetworkSocket,
334 SynchronizationPrimitive,
336 ThreadHandle,
338 LockGuard,
340 Other(String),
342}
343
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346pub enum AcquisitionMethod {
347 Constructor,
349 SystemCall,
351 Lock,
353 Allocation,
355 Unknown,
357}
358
359#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
361pub enum ReleaseMethod {
362 AutomaticDrop,
364 CustomDrop,
366 Deallocation,
368 SystemCall,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct ScopeInfo {
375 pub scope_name: String,
377 pub scope_type: ScopeType,
379 pub nesting_level: usize,
381}
382
383#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
385pub enum ScopeType {
386 Function,
388 Method,
390 Block,
392 Loop,
394 Conditional,
396 Unknown,
398}
399
400#[derive(Debug)]
402pub struct BorrowTracker {
403 active_borrows: HashMap<usize, Vec<BorrowInfo>>,
405 borrow_history: Vec<BorrowEvent>,
407 next_borrow_id: u64,
409}
410
411impl Default for BorrowTracker {
412 fn default() -> Self {
413 Self::new()
414 }
415}
416
417impl BorrowTracker {
418 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 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 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 pub fn get_analysis(&self) -> BorrowAnalysis {
477 let mut conflicts = Vec::new();
478 let mut long_lived_borrows = Vec::new();
479
480 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 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 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 let mut patterns = Vec::new();
540
541 let short_borrows = self
543 .borrow_history
544 .iter()
545 .filter(|event| matches!(event.event_type, BorrowEventType::Released))
546 .filter(|event| {
547 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 })
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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
577pub enum BorrowType {
578 Immutable,
580 Mutable,
582}
583
584#[derive(Debug, Clone, Serialize, Deserialize)]
586pub struct BorrowInfo {
587 pub borrow_id: u64,
589 pub borrow_type: BorrowType,
591 pub start_timestamp: u64,
593 pub location: String,
595 pub thread_id: String,
597}
598
599#[derive(Debug, Clone, Serialize, Deserialize)]
601pub struct BorrowEvent {
602 pub ptr: usize,
604 pub borrow_info: BorrowInfo,
606 pub event_type: BorrowEventType,
608 pub timestamp: u64,
610}
611
612#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
614pub enum BorrowEventType {
615 Acquired,
617 Released,
619}
620
621#[derive(Debug, Clone, Default, Serialize, Deserialize)]
623pub struct BorrowAnalysis {
624 pub total_borrows: usize,
626 pub active_borrows: usize,
628 pub conflicts: Vec<BorrowConflict>,
630 pub long_lived_borrows: Vec<LongLivedBorrow>,
632 pub borrow_patterns: Vec<BorrowPattern>,
634}
635
636#[derive(Debug, Clone, Serialize, Deserialize)]
638pub struct BorrowConflict {
639 pub ptr: usize,
641 pub first_borrow: BorrowInfo,
643 pub second_borrow: BorrowInfo,
645 pub conflict_type: ConflictType,
647}
648
649#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
651pub enum ConflictType {
652 MutableMutable,
654 MutableImmutable,
656 None,
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct LongLivedBorrow {
663 pub ptr: usize,
665 pub borrow_info: BorrowInfo,
667 pub duration_ns: u64,
669}
670
671#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct BorrowPattern {
674 pub pattern_type: BorrowPatternType,
676 pub description: String,
678 pub impact: PatternImpact,
680 pub suggestion: String,
682}
683
684#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
686pub enum BorrowPatternType {
687 FrequentShortBorrows,
689 LongLivedBorrows,
691 ConflictProne,
693}
694
695#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
697pub enum PatternImpact {
698 Low,
700 Medium,
702 High,
704}
705
706#[derive(Debug, Clone, Serialize, Deserialize)]
708pub struct ClosureCapture {
709 pub closure_ptr: usize,
711 pub captured_vars: Vec<CapturedVariable>,
713 pub capture_timestamp: u64,
715 pub thread_id: String,
717}
718
719#[derive(Debug, Clone, Serialize, Deserialize)]
721pub struct CapturedVariable {
722 pub var_name: String,
724 pub var_ptr: usize,
726 pub capture_mode: CaptureMode,
728 pub var_type: String,
730 pub size: usize,
732}
733
734#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
736pub enum CaptureMode {
737 ByValue,
739 ByReference,
741 ByMutableReference,
743}
744
745#[derive(Debug, Clone, Default, Serialize, Deserialize)]
747pub struct LifecycleAnalysisReport {
748 pub drop_events: Vec<DropEvent>,
750 pub raii_patterns: Vec<RAIIPattern>,
752 pub borrow_analysis: BorrowAnalysis,
754 pub closure_captures: Vec<ClosureCapture>,
756 pub analysis_timestamp: u64,
758}
759
760fn current_timestamp() -> u64 {
762 SystemTime::now()
763 .duration_since(UNIX_EPOCH)
764 .unwrap_or_default()
765 .as_nanos() as u64
766}
767
768fn capture_call_stack() -> Vec<String> {
770 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 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 let analyzer1 = LifecycleAnalyzer::new();
800 let analyzer2 = LifecycleAnalyzer::new();
801
802 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 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 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 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 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 assert_eq!(
863 analyzer.identify_resource_type("JoinHandle<()>"),
864 Some(ResourceType::ThreadHandle)
865 );
866
867 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 assert_eq!(
883 analyzer.identify_resource_type("MutexGuard<T>"),
884 Some(ResourceType::SynchronizationPrimitive)
885 );
886
887 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); 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 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); 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 assert!(tracker.has_borrow_conflict(&mutable_borrow, &immutable_borrow));
1118 assert!(tracker.has_borrow_conflict(&immutable_borrow, &mutable_borrow));
1119
1120 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 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 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 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 for i in 0..15 {
1263 let borrow_id = tracker.track_borrow(0x1000 + i, BorrowType::Immutable, "test.rs");
1264 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, location: "test.rs".to_string(),
1272 thread_id: "thread-1".to_string(),
1273 },
1274 event_type: BorrowEventType::Released,
1275 timestamp: 1000 + 500_000, });
1277 }
1278
1279 let analysis = tracker.get_analysis();
1280
1281 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 assert_eq!(analysis.total_borrows, 30); }
1296
1297 #[test]
1298 fn test_enum_variants() {
1299 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}