1use crate::analysis::closure::types::*;
2use crate::capture::types::AllocationInfo;
3use std::collections::HashMap;
4use std::sync::{Arc, Mutex, OnceLock};
5
6static GLOBAL_CLOSURE_ANALYZER: OnceLock<Arc<ClosureAnalyzer>> = OnceLock::new();
7
8pub fn get_global_closure_analyzer() -> Arc<ClosureAnalyzer> {
9 GLOBAL_CLOSURE_ANALYZER
10 .get_or_init(|| Arc::new(ClosureAnalyzer::new()))
11 .clone()
12}
13
14pub struct ClosureAnalyzer {
15 closures: Mutex<HashMap<usize, ClosureInfo>>,
16 capture_events: Mutex<Vec<CaptureEvent>>,
17 lifetime_graph: Mutex<LifetimeGraph>,
18}
19
20impl ClosureAnalyzer {
21 pub fn new() -> Self {
22 Self {
23 closures: Mutex::new(HashMap::new()),
24 capture_events: Mutex::new(Vec::new()),
25 lifetime_graph: Mutex::new(LifetimeGraph::new()),
26 }
27 }
28
29 pub fn register_closure(&self, closure_ptr: usize, captures: Vec<CaptureInfo>) {
30 let closure_info = ClosureInfo {
31 ptr: closure_ptr,
32 captures: captures.clone(),
33 creation_timestamp: current_timestamp(),
34 thread_id: format!("{:?}", std::thread::current().id()),
35 call_site: capture_call_site(),
36 memory_footprint: self.calculate_closure_footprint(&captures),
37 optimization_potential: self.analyze_optimization_potential(&captures),
38 };
39
40 for capture in &captures {
41 let event = CaptureEvent {
42 closure_ptr,
43 captured_var: capture.clone(),
44 event_type: CaptureEventType::Captured,
45 timestamp: current_timestamp(),
46 };
47
48 if let Ok(mut events) = self.capture_events.lock() {
49 events.push(event);
50 }
51 }
52
53 if let Ok(mut graph) = self.lifetime_graph.lock() {
54 graph.add_closure_relationships(closure_ptr, &captures);
55 }
56
57 if let Ok(mut closures) = self.closures.lock() {
58 closures.insert(closure_ptr, closure_info);
59 }
60 }
61
62 pub fn track_closure_drop(&self, closure_ptr: usize) {
63 if let Ok(mut closures) = self.closures.lock() {
64 if let Some(closure_info) = closures.get_mut(&closure_ptr) {
65 for capture in &closure_info.captures {
66 let event = CaptureEvent {
67 closure_ptr,
68 captured_var: capture.clone(),
69 event_type: CaptureEventType::Released,
70 timestamp: current_timestamp(),
71 };
72
73 if let Ok(mut events) = self.capture_events.lock() {
74 events.push(event);
75 }
76 }
77 }
78 closures.remove(&closure_ptr);
79 }
80
81 if let Ok(mut graph) = self.lifetime_graph.lock() {
82 graph.remove_closure(closure_ptr);
83 }
84 }
85
86 pub fn analyze_closure_patterns(
87 &self,
88 allocations: &[AllocationInfo],
89 ) -> ClosureAnalysisReport {
90 let mut detected_closures = Vec::new();
91 let mut capture_statistics = CaptureStatistics::default();
92
93 for allocation in allocations {
94 if let Some(type_name) = &allocation.type_name {
95 if self.is_closure_type(type_name) {
96 if let Some(analysis) = self.analyze_closure_allocation(allocation) {
97 detected_closures.push(analysis);
98 }
99 }
100 }
101 }
102
103 if let Ok(closures) = self.closures.lock() {
104 capture_statistics = self.calculate_capture_statistics(&closures);
105 }
106
107 let optimization_suggestions = self.generate_optimization_suggestions(&detected_closures);
108 let lifetime_analysis = self.analyze_capture_lifetimes();
109
110 ClosureAnalysisReport {
111 detected_closures,
112 capture_statistics,
113 optimization_suggestions,
114 lifetime_analysis,
115 analysis_timestamp: current_timestamp(),
116 }
117 }
118
119 fn is_closure_type(&self, type_name: &str) -> bool {
120 type_name.contains("closure")
121 || type_name.contains("{{closure}}")
122 || type_name.starts_with("fn(")
123 || type_name.contains("dyn Fn")
124 || type_name.contains("impl Fn")
125 }
126
127 fn analyze_closure_allocation(&self, allocation: &AllocationInfo) -> Option<DetectedClosure> {
128 let type_name = allocation.type_name.as_ref()?;
129
130 Some(DetectedClosure {
131 ptr: allocation.ptr,
132 type_name: type_name.clone(),
133 size: allocation.size,
134 estimated_captures: self.estimate_captures_from_size(allocation.size),
135 closure_type: self.classify_closure_type(type_name),
136 creation_context: CreationContext {
137 scope_name: allocation.scope_name.clone(),
138 thread_id: format!("{:?}", allocation.thread_id),
139 timestamp: allocation.timestamp_alloc,
140 },
141 memory_impact: self.assess_memory_impact(allocation.size),
142 })
143 }
144
145 fn estimate_captures_from_size(&self, size: usize) -> usize {
146 if size <= 8 {
147 0
148 } else if size <= 32 {
149 2
150 } else if size <= 128 {
151 8
152 } else {
153 size / 16
154 }
155 }
156
157 fn classify_closure_type(&self, type_name: &str) -> ClosureType {
158 if type_name.contains("FnOnce") {
159 ClosureType::FnOnce
160 } else if type_name.contains("FnMut") {
161 ClosureType::FnMut
162 } else if type_name.contains("Fn") {
163 ClosureType::Fn
164 } else {
165 ClosureType::Unknown
166 }
167 }
168
169 fn assess_memory_impact(&self, size: usize) -> MemoryImpact {
170 match size {
171 0..=16 => MemoryImpact::Minimal,
172 17..=64 => MemoryImpact::Low,
173 65..=256 => MemoryImpact::Medium,
174 257..=1024 => MemoryImpact::High,
175 _ => MemoryImpact::VeryHigh,
176 }
177 }
178
179 fn calculate_closure_footprint(&self, captures: &[CaptureInfo]) -> ClosureFootprint {
180 let total_size = captures.iter().map(|c| c.size).sum();
181 let by_value_count = captures
182 .iter()
183 .filter(|c| c.mode == CaptureMode::ByValue)
184 .count();
185 let by_ref_count = captures
186 .iter()
187 .filter(|c| c.mode == CaptureMode::ByReference)
188 .count();
189 let by_mut_ref_count = captures
190 .iter()
191 .filter(|c| c.mode == CaptureMode::ByMutableReference)
192 .count();
193
194 ClosureFootprint {
195 total_size,
196 capture_count: captures.len(),
197 by_value_count,
198 by_ref_count,
199 by_mut_ref_count,
200 estimated_heap_usage: self.estimate_heap_usage(captures),
201 }
202 }
203
204 fn estimate_heap_usage(&self, captures: &[CaptureInfo]) -> usize {
205 captures
206 .iter()
207 .filter(|c| c.mode == CaptureMode::ByValue)
208 .filter(|c| self.is_heap_allocated_type(&c.var_type))
209 .map(|c| c.size)
210 .sum()
211 }
212
213 fn is_heap_allocated_type(&self, type_name: &str) -> bool {
214 type_name.contains("Vec")
215 || type_name.contains("String")
216 || type_name.contains("HashMap")
217 || type_name.contains("Box")
218 || type_name.contains("Arc")
219 || type_name.contains("Rc")
220 }
221
222 fn analyze_optimization_potential(&self, captures: &[CaptureInfo]) -> OptimizationPotential {
223 let mut suggestions = Vec::new();
224 let mut potential_savings = 0;
225
226 for capture in captures {
227 if capture.mode == CaptureMode::ByValue && capture.size > 64 {
228 suggestions.push(format!(
229 "Consider capturing '{}' by reference instead of by value to save {} bytes",
230 capture.var_name, capture.size
231 ));
232 potential_savings += capture.size;
233 }
234 }
235
236 let mut_captures = captures
237 .iter()
238 .filter(|c| c.mode == CaptureMode::ByMutableReference)
239 .count();
240 if mut_captures > captures.len() / 2 {
241 suggestions.push("Consider if all mutable captures are necessary".to_string());
242 }
243
244 let heap_captures = captures
245 .iter()
246 .filter(|c| c.mode == CaptureMode::ByValue && self.is_heap_allocated_type(&c.var_type))
247 .count();
248
249 if heap_captures > 0 {
250 suggestions
251 .push("Consider using move semantics for heap-allocated captures".to_string());
252 }
253
254 OptimizationPotential {
255 level: if potential_savings > 256 {
256 OptimizationLevel::High
257 } else if potential_savings > 64 {
258 OptimizationLevel::Medium
259 } else if !suggestions.is_empty() {
260 OptimizationLevel::Low
261 } else {
262 OptimizationLevel::None
263 },
264 potential_savings,
265 suggestions,
266 }
267 }
268
269 fn calculate_capture_statistics(
270 &self,
271 closures: &HashMap<usize, ClosureInfo>,
272 ) -> CaptureStatistics {
273 let total_closures = closures.len();
274 let total_captures = closures.values().map(|c| c.captures.len()).sum();
275
276 let mut by_mode = HashMap::new();
277 let mut by_type = HashMap::new();
278 let mut total_memory = 0;
279
280 for closure in closures.values() {
281 total_memory += closure.memory_footprint.total_size;
282
283 for capture in &closure.captures {
284 *by_mode.entry(capture.mode.clone()).or_insert(0) += 1;
285 *by_type.entry(capture.var_type.clone()).or_insert(0) += 1;
286 }
287 }
288
289 let avg_captures_per_closure = if total_closures > 0 {
290 total_captures as f64 / total_closures as f64
291 } else {
292 0.0
293 };
294
295 CaptureStatistics {
296 total_closures,
297 total_captures,
298 avg_captures_per_closure,
299 total_memory_usage: total_memory,
300 captures_by_mode: by_mode,
301 captures_by_type: by_type,
302 }
303 }
304
305 fn generate_optimization_suggestions(
306 &self,
307 closures: &[DetectedClosure],
308 ) -> Vec<OptimizationSuggestion> {
309 let mut suggestions = Vec::new();
310
311 let high_memory_closures = closures
312 .iter()
313 .filter(|c| matches!(c.memory_impact, MemoryImpact::High | MemoryImpact::VeryHigh))
314 .count();
315
316 if high_memory_closures > 0 {
317 suggestions.push(OptimizationSuggestion {
318 category: OptimizationCategory::Memory,
319 priority: SuggestionPriority::High,
320 description: format!(
321 "Found {high_memory_closures} closures with high memory usage",
322 ),
323 recommendation: "Consider reducing capture size or using references".to_string(),
324 estimated_impact: "20-50% memory reduction".to_string(),
325 });
326 }
327
328 let fnonce_count = closures
329 .iter()
330 .filter(|c| c.closure_type == ClosureType::FnOnce)
331 .count();
332
333 if fnonce_count > closures.len() / 2 {
334 suggestions.push(OptimizationSuggestion {
335 category: OptimizationCategory::Performance,
336 priority: SuggestionPriority::Medium,
337 description: "Many FnOnce closures detected".to_string(),
338 recommendation: "Consider if Fn or FnMut traits would be more appropriate"
339 .to_string(),
340 estimated_impact: "Improved reusability".to_string(),
341 });
342 }
343
344 suggestions
345 }
346
347 fn analyze_capture_lifetimes(&self) -> LifetimeAnalysis {
348 if let Ok(graph) = self.lifetime_graph.lock() {
349 graph.analyze_lifetimes()
350 } else {
351 LifetimeAnalysis::default()
352 }
353 }
354}
355
356impl Default for ClosureAnalyzer {
357 fn default() -> Self {
358 Self::new()
359 }
360}
361
362fn current_timestamp() -> u64 {
363 std::time::SystemTime::now()
364 .duration_since(std::time::UNIX_EPOCH)
365 .unwrap_or_default()
366 .as_nanos() as u64
367}
368
369fn capture_call_site() -> String {
370 format!("{}:{}", file!(), line!())
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use std::thread;
377
378 #[test]
381 fn test_closure_analyzer_creation() {
382 let analyzer = ClosureAnalyzer::new();
383
384 let closures = analyzer.closures.lock().unwrap();
385 let events = analyzer.capture_events.lock().unwrap();
386
387 assert!(closures.is_empty(), "New analyzer should have no closures");
388 assert!(
389 events.is_empty(),
390 "New analyzer should have no capture events"
391 );
392 }
393
394 #[test]
397 fn test_closure_analyzer_default() {
398 let analyzer = ClosureAnalyzer::default();
399
400 let closures = analyzer.closures.lock().unwrap();
401 assert!(
402 closures.is_empty(),
403 "Default analyzer should have no closures"
404 );
405 }
406
407 #[test]
410 fn test_register_closure() {
411 let analyzer = ClosureAnalyzer::new();
412
413 let captures = vec![CaptureInfo {
414 var_name: "x".to_string(),
415 var_ptr: 0x1000,
416 mode: CaptureMode::ByValue,
417 var_type: "i32".to_string(),
418 size: 4,
419 lifetime_bound: None,
420 }];
421
422 analyzer.register_closure(0x5000, captures);
423
424 let closures = analyzer.closures.lock().unwrap();
425 assert!(
426 closures.contains_key(&0x5000),
427 "Should contain registered closure"
428 );
429
430 let events = analyzer.capture_events.lock().unwrap();
431 assert_eq!(events.len(), 1, "Should have one capture event");
432 assert_eq!(
433 events[0].event_type,
434 CaptureEventType::Captured,
435 "Event should be Captured"
436 );
437 }
438
439 #[test]
442 fn test_register_closure_multiple_captures() {
443 let analyzer = ClosureAnalyzer::new();
444
445 let captures = vec![
446 CaptureInfo {
447 var_name: "a".to_string(),
448 var_ptr: 0x1000,
449 mode: CaptureMode::ByValue,
450 var_type: "i32".to_string(),
451 size: 4,
452 lifetime_bound: None,
453 },
454 CaptureInfo {
455 var_name: "b".to_string(),
456 var_ptr: 0x2000,
457 mode: CaptureMode::ByReference,
458 var_type: "String".to_string(),
459 size: 24,
460 lifetime_bound: None,
461 },
462 CaptureInfo {
463 var_name: "c".to_string(),
464 var_ptr: 0x3000,
465 mode: CaptureMode::ByMutableReference,
466 var_type: "Vec<u8>".to_string(),
467 size: 24,
468 lifetime_bound: None,
469 },
470 ];
471
472 analyzer.register_closure(0x6000, captures);
473
474 let events = analyzer.capture_events.lock().unwrap();
475 assert_eq!(events.len(), 3, "Should have three capture events");
476 }
477
478 #[test]
481 fn test_track_closure_drop() {
482 let analyzer = ClosureAnalyzer::new();
483
484 let captures = vec![CaptureInfo {
485 var_name: "x".to_string(),
486 var_ptr: 0x1000,
487 mode: CaptureMode::ByValue,
488 var_type: "i32".to_string(),
489 size: 4,
490 lifetime_bound: None,
491 }];
492
493 analyzer.register_closure(0x5000, captures);
494 analyzer.track_closure_drop(0x5000);
495
496 let closures = analyzer.closures.lock().unwrap();
497 assert!(
498 !closures.contains_key(&0x5000),
499 "Should not contain dropped closure"
500 );
501
502 let events = analyzer.capture_events.lock().unwrap();
503 assert_eq!(events.len(), 2, "Should have captured and released events");
504 assert_eq!(
505 events[1].event_type,
506 CaptureEventType::Released,
507 "Second event should be Released"
508 );
509 }
510
511 #[test]
514 fn test_track_closure_drop_nonexistent() {
515 let analyzer = ClosureAnalyzer::new();
516
517 analyzer.track_closure_drop(0xdead);
518
519 let closures = analyzer.closures.lock().unwrap();
520 assert!(closures.is_empty(), "Should still be empty");
521 }
522
523 #[test]
526 fn test_is_closure_type() {
527 let analyzer = ClosureAnalyzer::new();
528
529 assert!(
530 analyzer.is_closure_type("closure"),
531 "Should detect 'closure'"
532 );
533 assert!(
534 analyzer.is_closure_type("{{closure}}"),
535 "Should detect '{{closure}}'"
536 );
537 assert!(
538 analyzer.is_closure_type("fn() -> i32"),
539 "Should detect 'fn('"
540 );
541 assert!(
542 analyzer.is_closure_type("dyn Fn()"),
543 "Should detect 'dyn Fn'"
544 );
545 assert!(
546 analyzer.is_closure_type("impl FnMut()"),
547 "Should detect 'impl Fn'"
548 );
549 assert!(
550 !analyzer.is_closure_type("String"),
551 "Should not detect 'String'"
552 );
553 assert!(
554 !analyzer.is_closure_type("Vec<u8>"),
555 "Should not detect 'Vec<u8>'"
556 );
557 }
558
559 #[test]
562 fn test_estimate_captures_from_size() {
563 let analyzer = ClosureAnalyzer::new();
564
565 assert_eq!(
566 analyzer.estimate_captures_from_size(0),
567 0,
568 "Size 0 should estimate 0 captures"
569 );
570 assert_eq!(
571 analyzer.estimate_captures_from_size(8),
572 0,
573 "Size <= 8 should estimate 0 captures"
574 );
575 assert_eq!(
576 analyzer.estimate_captures_from_size(16),
577 2,
578 "Size 9-32 should estimate 2 captures"
579 );
580 assert_eq!(
581 analyzer.estimate_captures_from_size(32),
582 2,
583 "Size 32 should estimate 2 captures"
584 );
585 assert_eq!(
586 analyzer.estimate_captures_from_size(64),
587 8,
588 "Size 33-128 should estimate 8 captures"
589 );
590 assert_eq!(
591 analyzer.estimate_captures_from_size(128),
592 8,
593 "Size 128 should estimate 8 captures"
594 );
595 assert_eq!(
596 analyzer.estimate_captures_from_size(256),
597 16,
598 "Size > 128 should estimate size/16 captures"
599 );
600 }
601
602 #[test]
605 fn test_classify_closure_type() {
606 let analyzer = ClosureAnalyzer::new();
607
608 assert_eq!(
609 analyzer.classify_closure_type("FnOnce"),
610 ClosureType::FnOnce,
611 "Should classify FnOnce"
612 );
613 assert_eq!(
614 analyzer.classify_closure_type("impl FnOnce"),
615 ClosureType::FnOnce,
616 "Should classify impl FnOnce"
617 );
618 assert_eq!(
619 analyzer.classify_closure_type("FnMut"),
620 ClosureType::FnMut,
621 "Should classify FnMut"
622 );
623 assert_eq!(
624 analyzer.classify_closure_type("dyn FnMut"),
625 ClosureType::FnMut,
626 "Should classify dyn FnMut"
627 );
628 assert_eq!(
629 analyzer.classify_closure_type("Fn"),
630 ClosureType::Fn,
631 "Should classify Fn"
632 );
633 assert_eq!(
634 analyzer.classify_closure_type("dyn Fn"),
635 ClosureType::Fn,
636 "Should classify dyn Fn"
637 );
638 assert_eq!(
639 analyzer.classify_closure_type("SomeType"),
640 ClosureType::Unknown,
641 "Should classify unknown"
642 );
643 }
644
645 #[test]
648 fn test_assess_memory_impact() {
649 let analyzer = ClosureAnalyzer::new();
650
651 assert_eq!(
652 analyzer.assess_memory_impact(0),
653 MemoryImpact::Minimal,
654 "Size 0 should be Minimal"
655 );
656 assert_eq!(
657 analyzer.assess_memory_impact(16),
658 MemoryImpact::Minimal,
659 "Size 16 should be Minimal"
660 );
661 assert_eq!(
662 analyzer.assess_memory_impact(32),
663 MemoryImpact::Low,
664 "Size 32 should be Low"
665 );
666 assert_eq!(
667 analyzer.assess_memory_impact(64),
668 MemoryImpact::Low,
669 "Size 64 should be Low"
670 );
671 assert_eq!(
672 analyzer.assess_memory_impact(128),
673 MemoryImpact::Medium,
674 "Size 128 should be Medium"
675 );
676 assert_eq!(
677 analyzer.assess_memory_impact(256),
678 MemoryImpact::Medium,
679 "Size 256 should be Medium"
680 );
681 assert_eq!(
682 analyzer.assess_memory_impact(512),
683 MemoryImpact::High,
684 "Size 512 should be High"
685 );
686 assert_eq!(
687 analyzer.assess_memory_impact(1024),
688 MemoryImpact::High,
689 "Size 1024 should be High"
690 );
691 assert_eq!(
692 analyzer.assess_memory_impact(2048),
693 MemoryImpact::VeryHigh,
694 "Size 2048 should be VeryHigh"
695 );
696 }
697
698 #[test]
701 fn test_calculate_closure_footprint() {
702 let analyzer = ClosureAnalyzer::new();
703
704 let captures = vec![
705 CaptureInfo {
706 var_name: "a".to_string(),
707 var_ptr: 0x1000,
708 mode: CaptureMode::ByValue,
709 var_type: "i32".to_string(),
710 size: 4,
711 lifetime_bound: None,
712 },
713 CaptureInfo {
714 var_name: "b".to_string(),
715 var_ptr: 0x2000,
716 mode: CaptureMode::ByReference,
717 var_type: "String".to_string(),
718 size: 8,
719 lifetime_bound: None,
720 },
721 CaptureInfo {
722 var_name: "c".to_string(),
723 var_ptr: 0x3000,
724 mode: CaptureMode::ByMutableReference,
725 var_type: "Vec<u8>".to_string(),
726 size: 8,
727 lifetime_bound: None,
728 },
729 ];
730
731 let footprint = analyzer.calculate_closure_footprint(&captures);
732
733 assert_eq!(footprint.total_size, 20, "Total size should be 20");
734 assert_eq!(footprint.capture_count, 3, "Capture count should be 3");
735 assert_eq!(footprint.by_value_count, 1, "By value count should be 1");
736 assert_eq!(footprint.by_ref_count, 1, "By ref count should be 1");
737 assert_eq!(
738 footprint.by_mut_ref_count, 1,
739 "By mut ref count should be 1"
740 );
741 }
742
743 #[test]
746 fn test_is_heap_allocated_type() {
747 let analyzer = ClosureAnalyzer::new();
748
749 assert!(
750 analyzer.is_heap_allocated_type("Vec<u8>"),
751 "Vec should be heap type"
752 );
753 assert!(
754 analyzer.is_heap_allocated_type("String"),
755 "String should be heap type"
756 );
757 assert!(
758 analyzer.is_heap_allocated_type("HashMap<K, V>"),
759 "HashMap should be heap type"
760 );
761 assert!(
762 analyzer.is_heap_allocated_type("Box<T>"),
763 "Box should be heap type"
764 );
765 assert!(
766 analyzer.is_heap_allocated_type("Arc<T>"),
767 "Arc should be heap type"
768 );
769 assert!(
770 analyzer.is_heap_allocated_type("Rc<T>"),
771 "Rc should be heap type"
772 );
773 assert!(
774 !analyzer.is_heap_allocated_type("i32"),
775 "i32 should not be heap type"
776 );
777 assert!(
778 !analyzer.is_heap_allocated_type("&str"),
779 "&str should not be heap type"
780 );
781 }
782
783 #[test]
786 fn test_estimate_heap_usage() {
787 let analyzer = ClosureAnalyzer::new();
788
789 let captures = vec![
790 CaptureInfo {
791 var_name: "vec".to_string(),
792 var_ptr: 0x1000,
793 mode: CaptureMode::ByValue,
794 var_type: "Vec<u8>".to_string(),
795 size: 24,
796 lifetime_bound: None,
797 },
798 CaptureInfo {
799 var_name: "s".to_string(),
800 var_ptr: 0x2000,
801 mode: CaptureMode::ByValue,
802 var_type: "String".to_string(),
803 size: 24,
804 lifetime_bound: None,
805 },
806 CaptureInfo {
807 var_name: "num".to_string(),
808 var_ptr: 0x3000,
809 mode: CaptureMode::ByValue,
810 var_type: "i32".to_string(),
811 size: 4,
812 lifetime_bound: None,
813 },
814 CaptureInfo {
815 var_name: "ref_vec".to_string(),
816 var_ptr: 0x4000,
817 mode: CaptureMode::ByReference,
818 var_type: "Vec<u8>".to_string(),
819 size: 8,
820 lifetime_bound: None,
821 },
822 ];
823
824 let heap_usage = analyzer.estimate_heap_usage(&captures);
825
826 assert_eq!(heap_usage, 48, "Should sum Vec and String sizes (24+24)");
827 }
828
829 #[test]
832 fn test_analyze_optimization_potential_large_value() {
833 let analyzer = ClosureAnalyzer::new();
834
835 let captures = vec![CaptureInfo {
836 var_name: "large_data".to_string(),
837 var_ptr: 0x1000,
838 mode: CaptureMode::ByValue,
839 var_type: "[u8; 128]".to_string(),
840 size: 128,
841 lifetime_bound: None,
842 }];
843
844 let potential = analyzer.analyze_optimization_potential(&captures);
845
846 assert_eq!(
847 potential.level,
848 OptimizationLevel::Medium,
849 "Large capture should have Medium optimization level"
850 );
851 assert_eq!(
852 potential.potential_savings, 128,
853 "Potential savings should be 128"
854 );
855 assert!(
856 potential
857 .suggestions
858 .iter()
859 .any(|s| s.contains("reference")),
860 "Should suggest reference capture"
861 );
862 }
863
864 #[test]
867 fn test_analyze_optimization_potential_high_savings() {
868 let analyzer = ClosureAnalyzer::new();
869
870 let captures = vec![
871 CaptureInfo {
872 var_name: "data1".to_string(),
873 var_ptr: 0x1000,
874 mode: CaptureMode::ByValue,
875 var_type: "[u8; 128]".to_string(),
876 size: 128,
877 lifetime_bound: None,
878 },
879 CaptureInfo {
880 var_name: "data2".to_string(),
881 var_ptr: 0x2000,
882 mode: CaptureMode::ByValue,
883 var_type: "[u8; 128]".to_string(),
884 size: 128,
885 lifetime_bound: None,
886 },
887 CaptureInfo {
888 var_name: "data3".to_string(),
889 var_ptr: 0x3000,
890 mode: CaptureMode::ByValue,
891 var_type: "[u8; 128]".to_string(),
892 size: 128,
893 lifetime_bound: None,
894 },
895 ];
896
897 let potential = analyzer.analyze_optimization_potential(&captures);
898
899 assert_eq!(
900 potential.level,
901 OptimizationLevel::High,
902 "Savings > 256 should have High optimization level"
903 );
904 assert_eq!(
905 potential.potential_savings, 384,
906 "Potential savings should be 384 (128 * 3)"
907 );
908 }
909
910 #[test]
913 fn test_analyze_optimization_potential_many_mutable() {
914 let analyzer = ClosureAnalyzer::new();
915
916 let captures = vec![
917 CaptureInfo {
918 var_name: "a".to_string(),
919 var_ptr: 0x1000,
920 mode: CaptureMode::ByMutableReference,
921 var_type: "i32".to_string(),
922 size: 8,
923 lifetime_bound: None,
924 },
925 CaptureInfo {
926 var_name: "b".to_string(),
927 var_ptr: 0x2000,
928 mode: CaptureMode::ByMutableReference,
929 var_type: "i32".to_string(),
930 size: 8,
931 lifetime_bound: None,
932 },
933 CaptureInfo {
934 var_name: "c".to_string(),
935 var_ptr: 0x3000,
936 mode: CaptureMode::ByReference,
937 var_type: "i32".to_string(),
938 size: 8,
939 lifetime_bound: None,
940 },
941 ];
942
943 let potential = analyzer.analyze_optimization_potential(&captures);
944
945 assert!(
946 potential.suggestions.iter().any(|s| s.contains("mutable")),
947 "Should suggest reviewing mutable captures"
948 );
949 }
950
951 #[test]
954 fn test_analyze_optimization_potential_heap_captures() {
955 let analyzer = ClosureAnalyzer::new();
956
957 let captures = vec![CaptureInfo {
958 var_name: "vec".to_string(),
959 var_ptr: 0x1000,
960 mode: CaptureMode::ByValue,
961 var_type: "Vec<u8>".to_string(),
962 size: 24,
963 lifetime_bound: None,
964 }];
965
966 let potential = analyzer.analyze_optimization_potential(&captures);
967
968 assert!(
969 potential
970 .suggestions
971 .iter()
972 .any(|s| s.contains("move") || s.contains("heap")),
973 "Should suggest move semantics for heap captures"
974 );
975 }
976
977 #[test]
980 fn test_analyze_optimization_potential_none() {
981 let analyzer = ClosureAnalyzer::new();
982
983 let captures = vec![CaptureInfo {
984 var_name: "small".to_string(),
985 var_ptr: 0x1000,
986 mode: CaptureMode::ByReference,
987 var_type: "i32".to_string(),
988 size: 8,
989 lifetime_bound: None,
990 }];
991
992 let potential = analyzer.analyze_optimization_potential(&captures);
993
994 assert_eq!(
995 potential.level,
996 OptimizationLevel::None,
997 "Small reference capture should have None optimization level"
998 );
999 assert_eq!(
1000 potential.potential_savings, 0,
1001 "Potential savings should be 0"
1002 );
1003 }
1004
1005 #[test]
1008 fn test_calculate_capture_statistics() {
1009 let analyzer = ClosureAnalyzer::new();
1010
1011 let mut closures = HashMap::new();
1012
1013 closures.insert(
1014 0x1000,
1015 ClosureInfo {
1016 ptr: 0x1000,
1017 captures: vec![
1018 CaptureInfo {
1019 var_name: "a".to_string(),
1020 var_ptr: 0x1000,
1021 mode: CaptureMode::ByValue,
1022 var_type: "i32".to_string(),
1023 size: 4,
1024 lifetime_bound: None,
1025 },
1026 CaptureInfo {
1027 var_name: "b".to_string(),
1028 var_ptr: 0x2000,
1029 mode: CaptureMode::ByReference,
1030 var_type: "String".to_string(),
1031 size: 8,
1032 lifetime_bound: None,
1033 },
1034 ],
1035 creation_timestamp: 1000,
1036 thread_id: "main".to_string(),
1037 call_site: "test.rs:1".to_string(),
1038 memory_footprint: ClosureFootprint {
1039 total_size: 12,
1040 capture_count: 2,
1041 by_value_count: 1,
1042 by_ref_count: 1,
1043 by_mut_ref_count: 0,
1044 estimated_heap_usage: 0,
1045 },
1046 optimization_potential: OptimizationPotential {
1047 level: OptimizationLevel::None,
1048 potential_savings: 0,
1049 suggestions: vec![],
1050 },
1051 },
1052 );
1053
1054 let stats = analyzer.calculate_capture_statistics(&closures);
1055
1056 assert_eq!(stats.total_closures, 1, "Should have 1 closure");
1057 assert_eq!(stats.total_captures, 2, "Should have 2 captures");
1058 assert_eq!(stats.avg_captures_per_closure, 2.0, "Average should be 2.0");
1059 assert_eq!(stats.total_memory_usage, 12, "Total memory should be 12");
1060 }
1061
1062 #[test]
1065 fn test_calculate_capture_statistics_empty() {
1066 let analyzer = ClosureAnalyzer::new();
1067
1068 let closures = HashMap::new();
1069 let stats = analyzer.calculate_capture_statistics(&closures);
1070
1071 assert_eq!(stats.total_closures, 0, "Should have 0 closures");
1072 assert_eq!(stats.total_captures, 0, "Should have 0 captures");
1073 assert_eq!(stats.avg_captures_per_closure, 0.0, "Average should be 0.0");
1074 }
1075
1076 #[test]
1079 fn test_generate_optimization_suggestions_high_memory() {
1080 let analyzer = ClosureAnalyzer::new();
1081
1082 let closures = vec![DetectedClosure {
1083 ptr: 0x1000,
1084 type_name: "closure".to_string(),
1085 size: 1024,
1086 estimated_captures: 10,
1087 closure_type: ClosureType::Fn,
1088 creation_context: CreationContext {
1089 scope_name: None,
1090 thread_id: "main".to_string(),
1091 timestamp: 1000,
1092 },
1093 memory_impact: MemoryImpact::High,
1094 }];
1095
1096 let suggestions = analyzer.generate_optimization_suggestions(&closures);
1097
1098 assert!(
1099 suggestions
1100 .iter()
1101 .any(|s| s.category == OptimizationCategory::Memory),
1102 "Should have memory suggestion"
1103 );
1104 assert!(
1105 suggestions
1106 .iter()
1107 .any(|s| s.priority == SuggestionPriority::High),
1108 "Should have high priority suggestion"
1109 );
1110 }
1111
1112 #[test]
1115 fn test_generate_optimization_suggestions_many_fnonce() {
1116 let analyzer = ClosureAnalyzer::new();
1117
1118 let closures = vec![
1119 DetectedClosure {
1120 ptr: 0x1000,
1121 type_name: "FnOnce".to_string(),
1122 size: 64,
1123 estimated_captures: 2,
1124 closure_type: ClosureType::FnOnce,
1125 creation_context: CreationContext {
1126 scope_name: None,
1127 thread_id: "main".to_string(),
1128 timestamp: 1000,
1129 },
1130 memory_impact: MemoryImpact::Low,
1131 },
1132 DetectedClosure {
1133 ptr: 0x2000,
1134 type_name: "FnOnce".to_string(),
1135 size: 64,
1136 estimated_captures: 2,
1137 closure_type: ClosureType::FnOnce,
1138 creation_context: CreationContext {
1139 scope_name: None,
1140 thread_id: "main".to_string(),
1141 timestamp: 1000,
1142 },
1143 memory_impact: MemoryImpact::Low,
1144 },
1145 DetectedClosure {
1146 ptr: 0x3000,
1147 type_name: "Fn".to_string(),
1148 size: 64,
1149 estimated_captures: 2,
1150 closure_type: ClosureType::Fn,
1151 creation_context: CreationContext {
1152 scope_name: None,
1153 thread_id: "main".to_string(),
1154 timestamp: 1000,
1155 },
1156 memory_impact: MemoryImpact::Low,
1157 },
1158 ];
1159
1160 let suggestions = analyzer.generate_optimization_suggestions(&closures);
1161
1162 assert!(
1163 suggestions.iter().any(|s| s.description.contains("FnOnce")),
1164 "Should mention FnOnce closures"
1165 );
1166 }
1167
1168 #[test]
1171 fn test_analyze_capture_lifetimes() {
1172 let analyzer = ClosureAnalyzer::new();
1173
1174 let captures = vec![
1175 CaptureInfo {
1176 var_name: "a".to_string(),
1177 var_ptr: 0x1000,
1178 mode: CaptureMode::ByValue,
1179 var_type: "i32".to_string(),
1180 size: 4,
1181 lifetime_bound: None,
1182 },
1183 CaptureInfo {
1184 var_name: "b".to_string(),
1185 var_ptr: 0x2000,
1186 mode: CaptureMode::ByReference,
1187 var_type: "String".to_string(),
1188 size: 8,
1189 lifetime_bound: None,
1190 },
1191 ];
1192
1193 analyzer.register_closure(0x5000, captures);
1194 let analysis = analyzer.analyze_capture_lifetimes();
1195
1196 assert_eq!(
1197 analysis.total_relationships, 1,
1198 "Should have 1 relationship"
1199 );
1200 }
1201
1202 #[test]
1205 fn test_global_closure_analyzer_singleton() {
1206 let analyzer1 = get_global_closure_analyzer();
1207 let analyzer2 = get_global_closure_analyzer();
1208
1209 assert!(
1210 Arc::ptr_eq(&analyzer1, &analyzer2),
1211 "Should return same instance"
1212 );
1213 }
1214
1215 #[test]
1218 fn test_current_timestamp() {
1219 let ts = current_timestamp();
1220 assert!(ts > 0, "Timestamp should be positive");
1221 }
1222
1223 #[test]
1226 fn test_capture_call_site() {
1227 let site = capture_call_site();
1228 assert!(!site.is_empty(), "Call site should not be empty");
1229 assert!(
1230 site.contains(".rs"),
1231 "Call site should contain .rs extension"
1232 );
1233 }
1234
1235 #[test]
1238 fn test_analyze_closure_patterns_with_allocations() {
1239 let analyzer = ClosureAnalyzer::new();
1240
1241 let allocations = vec![AllocationInfo {
1242 ptr: 0x1000,
1243 size: 64,
1244 var_name: Some("closure_var".to_string()),
1245 type_name: Some("closure".to_string()),
1246 scope_name: Some("test_scope".to_string()),
1247 timestamp_alloc: 1000,
1248 timestamp_dealloc: None,
1249 thread_id: thread::current().id(),
1250 thread_id_u64: 1,
1251 borrow_count: 0,
1252 stack_trace: None,
1253 is_leaked: false,
1254 lifetime_ms: None,
1255 borrow_info: None,
1256 clone_info: None,
1257 ownership_history_available: false,
1258 smart_pointer_info: None,
1259 memory_layout: None,
1260 generic_info: None,
1261 dynamic_type_info: None,
1262 runtime_state: None,
1263 stack_allocation: None,
1264 temporary_object: None,
1265 fragmentation_analysis: None,
1266 generic_instantiation: None,
1267 type_relationships: None,
1268 type_usage: None,
1269 function_call_tracking: None,
1270 lifecycle_tracking: None,
1271 access_tracking: None,
1272 drop_chain_analysis: None,
1273 }];
1274
1275 let report = analyzer.analyze_closure_patterns(&allocations);
1276
1277 assert_eq!(report.detected_closures.len(), 1, "Should detect 1 closure");
1278 assert!(report.analysis_timestamp > 0, "Should have timestamp");
1279 }
1280
1281 #[test]
1284 fn test_analyze_closure_patterns_non_closure() {
1285 let analyzer = ClosureAnalyzer::new();
1286
1287 let allocations = vec![AllocationInfo {
1288 ptr: 0x1000,
1289 size: 64,
1290 var_name: Some("string_var".to_string()),
1291 type_name: Some("String".to_string()),
1292 scope_name: Some("test_scope".to_string()),
1293 timestamp_alloc: 1000,
1294 timestamp_dealloc: None,
1295 thread_id: thread::current().id(),
1296 thread_id_u64: 1,
1297 borrow_count: 0,
1298 stack_trace: None,
1299 is_leaked: false,
1300 lifetime_ms: None,
1301 borrow_info: None,
1302 clone_info: None,
1303 ownership_history_available: false,
1304 smart_pointer_info: None,
1305 memory_layout: None,
1306 generic_info: None,
1307 dynamic_type_info: None,
1308 runtime_state: None,
1309 stack_allocation: None,
1310 temporary_object: None,
1311 fragmentation_analysis: None,
1312 generic_instantiation: None,
1313 type_relationships: None,
1314 type_usage: None,
1315 function_call_tracking: None,
1316 lifecycle_tracking: None,
1317 access_tracking: None,
1318 drop_chain_analysis: None,
1319 }];
1320
1321 let report = analyzer.analyze_closure_patterns(&allocations);
1322
1323 assert_eq!(
1324 report.detected_closures.len(),
1325 0,
1326 "Should not detect non-closure"
1327 );
1328 }
1329
1330 #[test]
1333 fn test_analyze_closure_allocation() {
1334 let analyzer = ClosureAnalyzer::new();
1335
1336 let allocation = AllocationInfo {
1337 ptr: 0x1000,
1338 size: 128,
1339 var_name: Some("my_closure".to_string()),
1340 type_name: Some("dyn FnMut()".to_string()),
1341 scope_name: Some("test_scope".to_string()),
1342 timestamp_alloc: 1000,
1343 timestamp_dealloc: None,
1344 thread_id: thread::current().id(),
1345 thread_id_u64: 1,
1346 borrow_count: 0,
1347 stack_trace: None,
1348 is_leaked: false,
1349 lifetime_ms: None,
1350 borrow_info: None,
1351 clone_info: None,
1352 ownership_history_available: false,
1353 smart_pointer_info: None,
1354 memory_layout: None,
1355 generic_info: None,
1356 dynamic_type_info: None,
1357 runtime_state: None,
1358 stack_allocation: None,
1359 temporary_object: None,
1360 fragmentation_analysis: None,
1361 generic_instantiation: None,
1362 type_relationships: None,
1363 type_usage: None,
1364 function_call_tracking: None,
1365 lifecycle_tracking: None,
1366 access_tracking: None,
1367 drop_chain_analysis: None,
1368 };
1369
1370 let detected = analyzer.analyze_closure_allocation(&allocation);
1371
1372 assert!(detected.is_some(), "Should detect closure");
1373 let closure = detected.unwrap();
1374 assert_eq!(closure.ptr, 0x1000, "Pointer should match");
1375 assert_eq!(
1376 closure.closure_type,
1377 ClosureType::FnMut,
1378 "Should classify as FnMut"
1379 );
1380 assert_eq!(
1381 closure.memory_impact,
1382 MemoryImpact::Medium,
1383 "Size 128 should be Medium impact"
1384 );
1385 }
1386
1387 #[test]
1390 fn test_concurrent_access() {
1391 let analyzer = Arc::new(ClosureAnalyzer::new());
1392 let mut handles = vec![];
1393
1394 for i in 0..5 {
1395 let analyzer_clone = analyzer.clone();
1396 let handle = thread::spawn(move || {
1397 let captures = vec![CaptureInfo {
1398 var_name: format!("var{}", i),
1399 var_ptr: 0x1000 + i,
1400 mode: CaptureMode::ByValue,
1401 var_type: "i32".to_string(),
1402 size: 4,
1403 lifetime_bound: None,
1404 }];
1405 analyzer_clone.register_closure(0x5000 + i, captures);
1406 });
1407 handles.push(handle);
1408 }
1409
1410 for handle in handles {
1411 handle.join().unwrap();
1412 }
1413
1414 let closures = analyzer.closures.lock().unwrap();
1415 assert_eq!(closures.len(), 5, "Should have 5 closures from 5 threads");
1416 }
1417}