1use crate::core::types::{AllocationInfo, SmartPointerInfo, SmartPointerType};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct CircularReference {
13 pub cycle_path: Vec<CircularReferenceNode>,
15
16 pub suggested_weak_positions: Vec<usize>,
18
19 pub estimated_leaked_memory: usize,
21
22 pub severity: CircularReferenceSeverity,
24
25 pub cycle_type: CircularReferenceType,
27}
28
29#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
31pub struct CircularReferenceNode {
32 pub ptr: usize,
34
35 pub data_ptr: usize,
37
38 pub var_name: Option<String>,
40
41 pub type_name: Option<String>,
43
44 pub pointer_type: SmartPointerType,
46
47 pub ref_count: usize,
49}
50
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub enum CircularReferenceSeverity {
54 Low,
56 Medium,
58 High,
60 Critical,
62}
63
64#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
66pub enum CircularReferenceType {
67 Simple,
69 Complex,
71 SelfReference,
73 Nested,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct CircularReferenceAnalysis {
80 pub circular_references: Vec<CircularReference>,
82
83 pub total_smart_pointers: usize,
85
86 pub pointers_in_cycles: usize,
88
89 pub total_leaked_memory: usize,
91
92 pub statistics: CircularReferenceStatistics,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct CircularReferenceStatistics {
99 pub by_severity: HashMap<String, usize>,
101
102 pub by_type: HashMap<String, usize>,
104
105 pub by_pointer_type: HashMap<String, usize>,
107
108 pub average_cycle_length: f64,
110
111 pub largest_cycle_size: usize,
113}
114
115#[derive(Debug)]
117struct ReferenceGraph {
118 adjacency: HashMap<usize, Vec<usize>>,
120
121 reverse_refs: HashMap<usize, Vec<usize>>,
123
124 smart_pointers: HashMap<usize, SmartPointerInfo>,
126
127 allocations: HashMap<usize, AllocationInfo>,
129}
130
131impl ReferenceGraph {
132 fn new(allocations: &[AllocationInfo]) -> Self {
134 let mut graph = ReferenceGraph {
135 adjacency: HashMap::new(),
136 reverse_refs: HashMap::new(),
137 smart_pointers: HashMap::new(),
138 allocations: HashMap::new(),
139 };
140
141 for allocation in allocations {
143 if let Some(ref smart_info) = allocation.smart_pointer_info {
144 if smart_info.is_weak_reference {
146 continue;
147 }
148
149 graph
150 .smart_pointers
151 .insert(allocation.ptr, smart_info.clone());
152 graph.allocations.insert(allocation.ptr, allocation.clone());
153
154 graph.adjacency.entry(allocation.ptr).or_default();
156
157 graph
159 .reverse_refs
160 .entry(smart_info.data_ptr)
161 .or_default()
162 .push(allocation.ptr);
163
164 for &clone_ptr in &smart_info.clones {
166 graph
167 .adjacency
168 .entry(allocation.ptr)
169 .or_default()
170 .push(clone_ptr);
171 }
172
173 if let Some(source_ptr) = smart_info.cloned_from {
175 graph
176 .adjacency
177 .entry(source_ptr)
178 .or_default()
179 .push(allocation.ptr);
180 }
181 }
182 }
183
184 graph
185 }
186
187 fn detect_cycles(&self) -> Vec<Vec<usize>> {
189 let mut cycles = Vec::new();
190 let mut visited = HashSet::new();
191 let mut rec_stack = HashSet::new();
192 let mut path = Vec::new();
193
194 for &ptr in self.smart_pointers.keys() {
195 if !visited.contains(&ptr) {
196 self.dfs_detect_cycles(ptr, &mut visited, &mut rec_stack, &mut path, &mut cycles);
197 }
198 }
199
200 cycles
201 }
202
203 fn dfs_detect_cycles(
205 &self,
206 ptr: usize,
207 visited: &mut HashSet<usize>,
208 rec_stack: &mut HashSet<usize>,
209 path: &mut Vec<usize>,
210 cycles: &mut Vec<Vec<usize>>,
211 ) {
212 visited.insert(ptr);
213 rec_stack.insert(ptr);
214 path.push(ptr);
215
216 if let Some(neighbors) = self.adjacency.get(&ptr) {
217 for &neighbor in neighbors {
218 if !visited.contains(&neighbor) {
219 self.dfs_detect_cycles(neighbor, visited, rec_stack, path, cycles);
220 } else if rec_stack.contains(&neighbor) {
221 if let Some(cycle_start) = path.iter().position(|&x| x == neighbor) {
223 let cycle = path[cycle_start..].to_vec();
224 cycles.push(cycle);
225 }
226 }
227 }
228 }
229
230 path.pop();
231 rec_stack.remove(&ptr);
232 }
233}
234
235pub fn detect_circular_references(allocations: &[AllocationInfo]) -> CircularReferenceAnalysis {
237 let graph = ReferenceGraph::new(allocations);
238 let raw_cycles = graph.detect_cycles();
239
240 let mut circular_references = Vec::new();
241 let mut total_leaked_memory = 0;
242 let mut pointers_in_cycles = HashSet::new();
243
244 for cycle_path in raw_cycles {
246 if cycle_path.len() < 2 {
247 continue; }
249
250 let circular_ref = analyze_cycle(&cycle_path, &graph);
251 total_leaked_memory += circular_ref.estimated_leaked_memory;
252
253 for node in &circular_ref.cycle_path {
254 pointers_in_cycles.insert(node.ptr);
255 }
256
257 circular_references.push(circular_ref);
258 }
259
260 let statistics = generate_statistics(&circular_references);
262
263 CircularReferenceAnalysis {
264 circular_references,
265 total_smart_pointers: graph.smart_pointers.len(),
266 pointers_in_cycles: pointers_in_cycles.len(),
267 total_leaked_memory,
268 statistics,
269 }
270}
271
272fn analyze_cycle(cycle_path: &[usize], graph: &ReferenceGraph) -> CircularReference {
274 let mut nodes = Vec::new();
275 let mut total_memory = 0;
276
277 for &ptr in cycle_path {
279 if let (Some(smart_info), Some(allocation)) =
280 (graph.smart_pointers.get(&ptr), graph.allocations.get(&ptr))
281 {
282 let node = CircularReferenceNode {
283 ptr,
284 data_ptr: smart_info.data_ptr,
285 var_name: allocation.var_name.clone(),
286 type_name: allocation.type_name.clone(),
287 pointer_type: smart_info.pointer_type.clone(),
288 ref_count: smart_info
289 .latest_ref_counts()
290 .map(|snapshot| snapshot.strong_count)
291 .unwrap_or(1),
292 };
293
294 total_memory += allocation.size;
295 nodes.push(node);
296 }
297 }
298
299 let cycle_type = if cycle_path.len() == 1 {
301 CircularReferenceType::SelfReference
302 } else if cycle_path.len() == 2 {
303 CircularReferenceType::Simple
304 } else {
305 CircularReferenceType::Complex
306 };
307
308 let severity = if total_memory > 1024 * 1024 {
310 CircularReferenceSeverity::Critical
312 } else if total_memory > 64 * 1024 {
313 CircularReferenceSeverity::High
315 } else if total_memory > 4 * 1024 {
316 CircularReferenceSeverity::Medium
318 } else {
319 CircularReferenceSeverity::Low
320 };
321
322 let suggested_weak_positions = suggest_weak_positions(&nodes);
324
325 CircularReference {
326 cycle_path: nodes,
327 suggested_weak_positions,
328 estimated_leaked_memory: total_memory,
329 severity,
330 cycle_type,
331 }
332}
333
334fn suggest_weak_positions(nodes: &[CircularReferenceNode]) -> Vec<usize> {
336 if let Some((index, _)) = nodes
339 .iter()
340 .enumerate()
341 .max_by_key(|(_, node)| node.ref_count)
342 {
343 vec![index]
344 } else {
345 vec![0] }
347}
348
349fn generate_statistics(circular_references: &[CircularReference]) -> CircularReferenceStatistics {
351 let mut by_severity = HashMap::new();
352 let mut by_type = HashMap::new();
353 let mut by_pointer_type = HashMap::new();
354 let mut total_cycle_length = 0;
355 let mut largest_cycle_size = 0;
356
357 for circular_ref in circular_references {
358 let severity_key = format!("{:?}", circular_ref.severity);
360 *by_severity.entry(severity_key).or_insert(0) += 1;
361
362 let type_key = format!("{:?}", circular_ref.cycle_type);
364 *by_type.entry(type_key).or_insert(0) += 1;
365
366 for node in &circular_ref.cycle_path {
368 let pointer_type_key = format!("{:?}", node.pointer_type);
369 *by_pointer_type.entry(pointer_type_key).or_insert(0) += 1;
370 }
371
372 let cycle_length = circular_ref.cycle_path.len();
374 total_cycle_length += cycle_length;
375 largest_cycle_size = largest_cycle_size.max(cycle_length);
376 }
377
378 let average_cycle_length = if circular_references.is_empty() {
379 0.0
380 } else {
381 total_cycle_length as f64 / circular_references.len() as f64
382 };
383
384 CircularReferenceStatistics {
385 by_severity,
386 by_type,
387 by_pointer_type,
388 average_cycle_length,
389 largest_cycle_size,
390 }
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::core::types::{RefCountSnapshot, SmartPointerInfo, SmartPointerType};
397
398 #[test]
399 fn test_circular_reference_node_creation() {
400 let node = CircularReferenceNode {
401 ptr: 0x1000,
402 data_ptr: 0x2000,
403 var_name: Some("test_node".to_string()),
404 type_name: Some("Rc<RefCell<Node>>".to_string()),
405 pointer_type: SmartPointerType::Rc,
406 ref_count: 1,
407 };
408
409 assert_eq!(node.ptr, 0x1000);
410 assert_eq!(node.data_ptr, 0x2000);
411 assert_eq!(node.var_name, Some("test_node".to_string()));
412 assert_eq!(node.type_name, Some("Rc<RefCell<Node>>".to_string()));
413 assert_eq!(node.pointer_type, SmartPointerType::Rc);
414 assert_eq!(node.ref_count, 1);
415 }
416
417 #[test]
418 fn test_circular_reference_creation() {
419 let cycle_path = vec![
420 CircularReferenceNode {
421 ptr: 0x1000,
422 data_ptr: 0x2000,
423 var_name: Some("node_a".to_string()),
424 type_name: Some("Rc<RefCell<Node>>".to_string()),
425 pointer_type: SmartPointerType::Rc,
426 ref_count: 1,
427 },
428 CircularReferenceNode {
429 ptr: 0x2000,
430 data_ptr: 0x1000,
431 var_name: Some("node_b".to_string()),
432 type_name: Some("Rc<RefCell<Node>>".to_string()),
433 pointer_type: SmartPointerType::Rc,
434 ref_count: 1,
435 },
436 ];
437
438 let circular_ref = CircularReference {
439 cycle_path: cycle_path.clone(),
440 suggested_weak_positions: vec![1],
441 estimated_leaked_memory: 1024,
442 severity: CircularReferenceSeverity::Medium,
443 cycle_type: CircularReferenceType::Simple,
444 };
445
446 assert_eq!(circular_ref.cycle_path.len(), 2);
447 assert_eq!(circular_ref.suggested_weak_positions, vec![1]);
448 assert_eq!(circular_ref.estimated_leaked_memory, 1024);
449 assert_eq!(circular_ref.severity, CircularReferenceSeverity::Medium);
450 assert_eq!(circular_ref.cycle_type, CircularReferenceType::Simple);
451 }
452
453 #[test]
454 fn test_circular_reference_severity_variants() {
455 let severities = [
456 CircularReferenceSeverity::Low,
457 CircularReferenceSeverity::Medium,
458 CircularReferenceSeverity::High,
459 CircularReferenceSeverity::Critical,
460 ];
461
462 assert_eq!(severities[0], CircularReferenceSeverity::Low);
464 assert_eq!(severities[1], CircularReferenceSeverity::Medium);
465 assert_eq!(severities[2], CircularReferenceSeverity::High);
466 assert_eq!(severities[3], CircularReferenceSeverity::Critical);
467 }
468
469 #[test]
470 fn test_circular_reference_type_variants() {
471 let types = [
472 CircularReferenceType::Simple,
473 CircularReferenceType::Complex,
474 CircularReferenceType::SelfReference,
475 CircularReferenceType::Nested,
476 ];
477
478 assert_eq!(types[0], CircularReferenceType::Simple);
480 assert_eq!(types[1], CircularReferenceType::Complex);
481 assert_eq!(types[2], CircularReferenceType::SelfReference);
482 assert_eq!(types[3], CircularReferenceType::Nested);
483 }
484
485 #[test]
486 fn test_circular_reference_statistics_generation() {
487 let empty_cycles = vec![];
489 let stats = generate_statistics(&empty_cycles);
490
491 assert_eq!(stats.average_cycle_length, 0.0);
492 assert_eq!(stats.largest_cycle_size, 0);
493 assert!(stats.by_severity.is_empty());
494 assert!(stats.by_type.is_empty());
495 assert!(stats.by_pointer_type.is_empty());
496
497 let cycles = vec![
499 CircularReference {
500 cycle_path: vec![CircularReferenceNode {
501 ptr: 0x1000,
502 data_ptr: 0x2000,
503 var_name: Some("node_a".to_string()),
504 type_name: Some("Rc<Node>".to_string()),
505 pointer_type: SmartPointerType::Rc,
506 ref_count: 2,
507 }],
508 suggested_weak_positions: vec![0],
509 estimated_leaked_memory: 1024,
510 severity: CircularReferenceSeverity::Low,
511 cycle_type: CircularReferenceType::SelfReference,
512 },
513 CircularReference {
514 cycle_path: vec![
515 CircularReferenceNode {
516 ptr: 0x2000,
517 data_ptr: 0x3000,
518 var_name: Some("node_b".to_string()),
519 type_name: Some("Arc<Node>".to_string()),
520 pointer_type: SmartPointerType::Arc,
521 ref_count: 3,
522 },
523 CircularReferenceNode {
524 ptr: 0x3000,
525 data_ptr: 0x2000,
526 var_name: Some("node_c".to_string()),
527 type_name: Some("Arc<Node>".to_string()),
528 pointer_type: SmartPointerType::Arc,
529 ref_count: 1,
530 },
531 ],
532 suggested_weak_positions: vec![0],
533 estimated_leaked_memory: 2048,
534 severity: CircularReferenceSeverity::Medium,
535 cycle_type: CircularReferenceType::Simple,
536 },
537 ];
538
539 let stats = generate_statistics(&cycles);
540
541 assert_eq!(stats.average_cycle_length, 1.5); assert_eq!(stats.largest_cycle_size, 2);
543 assert!(!stats.by_severity.is_empty());
544 assert!(!stats.by_type.is_empty());
545 assert!(!stats.by_pointer_type.is_empty());
546
547 assert_eq!(*stats.by_severity.get("Low").unwrap_or(&0), 1);
549 assert_eq!(*stats.by_severity.get("Medium").unwrap_or(&0), 1);
550 assert_eq!(*stats.by_type.get("SelfReference").unwrap_or(&0), 1);
551 assert_eq!(*stats.by_type.get("Simple").unwrap_or(&0), 1);
552 assert_eq!(*stats.by_pointer_type.get("Rc").unwrap_or(&0), 1);
553 assert_eq!(*stats.by_pointer_type.get("Arc").unwrap_or(&0), 2);
554 }
555
556 #[test]
557 fn test_suggest_weak_positions() {
558 let empty_nodes = vec![];
560 let positions = suggest_weak_positions(&empty_nodes);
561 assert_eq!(positions, vec![0]); let single_node = vec![CircularReferenceNode {
565 ptr: 0x1000,
566 data_ptr: 0x2000,
567 var_name: Some("node".to_string()),
568 type_name: Some("Rc<Node>".to_string()),
569 pointer_type: SmartPointerType::Rc,
570 ref_count: 1,
571 }];
572 let positions = suggest_weak_positions(&single_node);
573 assert_eq!(positions, vec![0]);
574
575 let multiple_nodes = vec![
577 CircularReferenceNode {
578 ptr: 0x1000,
579 data_ptr: 0x2000,
580 var_name: Some("node_a".to_string()),
581 type_name: Some("Rc<Node>".to_string()),
582 pointer_type: SmartPointerType::Rc,
583 ref_count: 1,
584 },
585 CircularReferenceNode {
586 ptr: 0x2000,
587 data_ptr: 0x3000,
588 var_name: Some("node_b".to_string()),
589 type_name: Some("Rc<Node>".to_string()),
590 pointer_type: SmartPointerType::Rc,
591 ref_count: 3, },
593 CircularReferenceNode {
594 ptr: 0x3000,
595 data_ptr: 0x1000,
596 var_name: Some("node_c".to_string()),
597 type_name: Some("Rc<Node>".to_string()),
598 pointer_type: SmartPointerType::Rc,
599 ref_count: 2,
600 },
601 ];
602 let positions = suggest_weak_positions(&multiple_nodes);
603 assert_eq!(positions, vec![1]); }
605
606 #[test]
607 fn test_analyze_cycle() {
608 let mut graph = ReferenceGraph {
610 adjacency: HashMap::new(),
611 reverse_refs: HashMap::new(),
612 smart_pointers: HashMap::new(),
613 allocations: HashMap::new(),
614 };
615
616 let smart_info_a = SmartPointerInfo {
618 data_ptr: 0x2000,
619 pointer_type: SmartPointerType::Rc,
620 is_weak_reference: false,
621 clones: vec![],
622 cloned_from: None,
623 ref_count_history: vec![RefCountSnapshot {
624 strong_count: 1,
625 weak_count: 0,
626 timestamp: 0,
627 }],
628 weak_count: None,
629 is_data_owner: true,
630 is_implicitly_deallocated: false,
631 };
632
633 let smart_info_b = SmartPointerInfo {
634 data_ptr: 0x1000,
635 pointer_type: SmartPointerType::Rc,
636 is_weak_reference: false,
637 clones: vec![],
638 cloned_from: None,
639 ref_count_history: vec![RefCountSnapshot {
640 strong_count: 1,
641 weak_count: 0,
642 timestamp: 0,
643 }],
644 weak_count: None,
645 is_data_owner: true,
646 is_implicitly_deallocated: false,
647 };
648
649 let allocation_a = AllocationInfo {
650 ptr: 0x1000,
651 size: 1024,
652 var_name: Some("node_a".to_string()),
653 type_name: Some("Rc<Node>".to_string()),
654 smart_pointer_info: Some(smart_info_a.clone()),
655 scope_name: None,
656 timestamp_alloc: 0,
657 timestamp_dealloc: None,
658 thread_id: "main".to_string(),
659 borrow_count: 0,
660 stack_trace: None,
661 is_leaked: false,
662 lifetime_ms: None,
663 borrow_info: None,
664 clone_info: None,
665 ownership_history_available: false,
666 memory_layout: None,
667 generic_info: None,
668 dynamic_type_info: None,
669 runtime_state: None,
670 stack_allocation: None,
671 temporary_object: None,
672 fragmentation_analysis: None,
673 generic_instantiation: None,
674 type_relationships: None,
675 type_usage: None,
676 function_call_tracking: None,
677 lifecycle_tracking: None,
678 access_tracking: None,
679 drop_chain_analysis: None,
680 };
681
682 let allocation_b = AllocationInfo {
683 ptr: 0x2000,
684 size: 2048,
685 var_name: Some("node_b".to_string()),
686 type_name: Some("Rc<Node>".to_string()),
687 smart_pointer_info: Some(smart_info_b.clone()),
688 scope_name: None,
689 timestamp_alloc: 0,
690 timestamp_dealloc: None,
691 thread_id: "main".to_string(),
692 borrow_count: 0,
693 stack_trace: None,
694 is_leaked: false,
695 lifetime_ms: None,
696 borrow_info: None,
697 clone_info: None,
698 ownership_history_available: false,
699 memory_layout: None,
700 generic_info: None,
701 dynamic_type_info: None,
702 runtime_state: None,
703 stack_allocation: None,
704 temporary_object: None,
705 fragmentation_analysis: None,
706 generic_instantiation: None,
707 type_relationships: None,
708 type_usage: None,
709 function_call_tracking: None,
710 lifecycle_tracking: None,
711 access_tracking: None,
712 drop_chain_analysis: None,
713 };
714
715 graph.smart_pointers.insert(0x1000, smart_info_a);
716 graph.smart_pointers.insert(0x2000, smart_info_b);
717 graph.allocations.insert(0x1000, allocation_a);
718 graph.allocations.insert(0x2000, allocation_b);
719
720 let cycle_path = vec![0x1000, 0x2000];
722 let circular_ref = analyze_cycle(&cycle_path, &graph);
723
724 assert_eq!(circular_ref.cycle_path.len(), 2);
725 assert_eq!(circular_ref.estimated_leaked_memory, 3072); assert_eq!(circular_ref.cycle_type, CircularReferenceType::Simple);
727 assert_eq!(circular_ref.severity, CircularReferenceSeverity::Low); assert!(!circular_ref.suggested_weak_positions.is_empty());
729 }
730
731 #[test]
732 fn test_reference_graph_creation() {
733 let empty_allocations = vec![];
735 let graph = ReferenceGraph::new(&empty_allocations);
736
737 assert!(graph.adjacency.is_empty());
738 assert!(graph.reverse_refs.is_empty());
739 assert!(graph.smart_pointers.is_empty());
740 assert!(graph.allocations.is_empty());
741
742 let allocations_without_smart = vec![AllocationInfo {
744 ptr: 0x1000,
745 size: 1024,
746 scope_name: None,
747 timestamp_alloc: 0,
748 timestamp_dealloc: None,
749 thread_id: "main".to_string(),
750 borrow_count: 0,
751 stack_trace: None,
752 is_leaked: false,
753 lifetime_ms: None,
754 var_name: None,
755 type_name: None,
756 borrow_info: None,
757 clone_info: None,
758 ownership_history_available: false,
759 smart_pointer_info: None,
760 memory_layout: None,
761 generic_info: None,
762 dynamic_type_info: None,
763 runtime_state: None,
764 stack_allocation: None,
765 temporary_object: None,
766 fragmentation_analysis: None,
767 generic_instantiation: None,
768 type_relationships: None,
769 type_usage: None,
770 function_call_tracking: None,
771 lifecycle_tracking: None,
772 access_tracking: None,
773 drop_chain_analysis: None,
774 }];
775 let graph = ReferenceGraph::new(&allocations_without_smart);
776
777 assert!(graph.adjacency.is_empty());
778 assert!(graph.reverse_refs.is_empty());
779 assert!(graph.smart_pointers.is_empty());
780 assert!(graph.allocations.is_empty());
781
782 let smart_info = SmartPointerInfo {
784 data_ptr: 0x2000,
785 pointer_type: SmartPointerType::Rc,
786 is_weak_reference: false,
787 clones: vec![],
788 cloned_from: None,
789 ref_count_history: vec![RefCountSnapshot {
790 strong_count: 1,
791 weak_count: 0,
792 timestamp: 0,
793 }],
794 weak_count: None,
795 is_data_owner: true,
796 is_implicitly_deallocated: false,
797 };
798
799 let allocations_with_smart = vec![AllocationInfo {
800 ptr: 0x1000,
801 size: 1024,
802 var_name: None,
803 type_name: None,
804 scope_name: None,
805 timestamp_alloc: 0,
806 timestamp_dealloc: None,
807 thread_id: "main".to_string(),
808 borrow_count: 0,
809 stack_trace: None,
810 is_leaked: false,
811 lifetime_ms: None,
812 borrow_info: None,
813 clone_info: None,
814 ownership_history_available: false,
815 smart_pointer_info: Some(smart_info.clone()),
816 memory_layout: None,
817 generic_info: None,
818 dynamic_type_info: None,
819 runtime_state: None,
820 stack_allocation: None,
821 temporary_object: None,
822 fragmentation_analysis: None,
823 generic_instantiation: None,
824 type_relationships: None,
825 type_usage: None,
826 function_call_tracking: None,
827 lifecycle_tracking: None,
828 access_tracking: None,
829 drop_chain_analysis: None,
830 }];
831
832 let graph = ReferenceGraph::new(&allocations_with_smart);
833
834 assert!(!graph.adjacency.is_empty());
835 assert!(!graph.reverse_refs.is_empty());
836 assert!(!graph.smart_pointers.is_empty());
837 assert!(!graph.allocations.is_empty());
838
839 assert!(graph.adjacency.contains_key(&0x1000));
840 assert!(graph.reverse_refs.contains_key(&0x2000));
841 assert!(graph.smart_pointers.contains_key(&0x1000));
842 assert!(graph.allocations.contains_key(&0x1000));
843 }
844
845 #[test]
846 fn test_reference_graph_with_weak_references() {
847 let weak_smart_info = SmartPointerInfo {
849 data_ptr: 0x2000,
850 pointer_type: SmartPointerType::Rc,
851 is_weak_reference: true, clones: vec![],
853 cloned_from: None,
854 ref_count_history: vec![RefCountSnapshot {
855 strong_count: 1,
856 weak_count: 1,
857 timestamp: 0,
858 }],
859 weak_count: Some(1),
860 is_data_owner: false,
861 is_implicitly_deallocated: false,
862 };
863
864 let allocations_with_weak = vec![AllocationInfo {
865 ptr: 0x1000,
866 size: 1024,
867 var_name: None,
868 type_name: None,
869 scope_name: None,
870 timestamp_alloc: 0,
871 timestamp_dealloc: None,
872 thread_id: "main".to_string(),
873 borrow_count: 0,
874 stack_trace: None,
875 is_leaked: false,
876 lifetime_ms: None,
877 borrow_info: None,
878 clone_info: None,
879 ownership_history_available: false,
880 smart_pointer_info: Some(weak_smart_info),
881 memory_layout: None,
882 generic_info: None,
883 dynamic_type_info: None,
884 runtime_state: None,
885 stack_allocation: None,
886 temporary_object: None,
887 fragmentation_analysis: None,
888 generic_instantiation: None,
889 type_relationships: None,
890 type_usage: None,
891 function_call_tracking: None,
892 lifecycle_tracking: None,
893 access_tracking: None,
894 drop_chain_analysis: None,
895 }];
896
897 let graph = ReferenceGraph::new(&allocations_with_weak);
898
899 assert!(graph.adjacency.is_empty());
901 assert!(graph.reverse_refs.is_empty());
902 assert!(graph.smart_pointers.is_empty());
903 assert!(graph.allocations.is_empty());
904 }
905
906 #[test]
907 fn test_detect_circular_references_empty() {
908 let empty_allocations = vec![];
909 let analysis = detect_circular_references(&empty_allocations);
910
911 assert_eq!(analysis.circular_references.len(), 0);
912 assert_eq!(analysis.total_smart_pointers, 0);
913 assert_eq!(analysis.pointers_in_cycles, 0);
914 assert_eq!(analysis.total_leaked_memory, 0);
915
916 assert_eq!(analysis.statistics.average_cycle_length, 0.0);
918 assert_eq!(analysis.statistics.largest_cycle_size, 0);
919 }
920
921 #[test]
922 fn test_circular_reference_analysis_structure() {
923 let analysis = CircularReferenceAnalysis {
924 circular_references: vec![],
925 total_smart_pointers: 10,
926 pointers_in_cycles: 5,
927 total_leaked_memory: 10240,
928 statistics: CircularReferenceStatistics {
929 by_severity: HashMap::new(),
930 by_type: HashMap::new(),
931 by_pointer_type: HashMap::new(),
932 average_cycle_length: 0.0,
933 largest_cycle_size: 0,
934 },
935 };
936
937 assert_eq!(analysis.total_smart_pointers, 10);
938 assert_eq!(analysis.pointers_in_cycles, 5);
939 assert_eq!(analysis.total_leaked_memory, 10240);
940 }
941
942 #[test]
943 fn test_circular_reference_severity_determination() {
944 let memory_size = 1024; let low_severity = if memory_size > 1024 * 1024 {
947 CircularReferenceSeverity::Critical
949 } else if memory_size > 64 * 1024 {
950 CircularReferenceSeverity::High
952 } else if memory_size > 4 * 1024 {
953 CircularReferenceSeverity::Medium
955 } else {
956 CircularReferenceSeverity::Low
957 };
958 assert_eq!(low_severity, CircularReferenceSeverity::Low);
959
960 let memory_size = 5000; let medium_severity = if memory_size > 1024 * 1024 {
963 CircularReferenceSeverity::Critical
965 } else if memory_size > 64 * 1024 {
966 CircularReferenceSeverity::High
968 } else if memory_size > 4 * 1024 {
969 CircularReferenceSeverity::Medium
971 } else {
972 CircularReferenceSeverity::Low
973 };
974 assert_eq!(medium_severity, CircularReferenceSeverity::Medium);
975
976 let memory_size = 70000; let high_severity = if memory_size > 1024 * 1024 {
979 CircularReferenceSeverity::Critical
981 } else if memory_size > 64 * 1024 {
982 CircularReferenceSeverity::High
984 } else if memory_size > 4 * 1024 {
985 CircularReferenceSeverity::Medium
987 } else {
988 CircularReferenceSeverity::Low
989 };
990 assert_eq!(high_severity, CircularReferenceSeverity::High);
991
992 let memory_size = 2000000; let critical_severity = if memory_size > 1024 * 1024 {
995 CircularReferenceSeverity::Critical
997 } else if memory_size > 64 * 1024 {
998 CircularReferenceSeverity::High
1000 } else if memory_size > 4 * 1024 {
1001 CircularReferenceSeverity::Medium
1003 } else {
1004 CircularReferenceSeverity::Low
1005 };
1006 assert_eq!(critical_severity, CircularReferenceSeverity::Critical);
1007 }
1008
1009 #[test]
1010 fn test_circular_reference_type_determination() {
1011 let cycle_length = 1;
1013 let self_ref_type = if cycle_length == 1 {
1014 CircularReferenceType::SelfReference
1015 } else if cycle_length == 2 {
1016 CircularReferenceType::Simple
1017 } else {
1018 CircularReferenceType::Complex
1019 };
1020 assert_eq!(self_ref_type, CircularReferenceType::SelfReference);
1021
1022 let cycle_length = 2;
1024 let simple_type = if cycle_length == 1 {
1025 CircularReferenceType::SelfReference
1026 } else if cycle_length == 2 {
1027 CircularReferenceType::Simple
1028 } else {
1029 CircularReferenceType::Complex
1030 };
1031 assert_eq!(simple_type, CircularReferenceType::Simple);
1032
1033 let cycle_length = 5;
1035 let complex_type = if cycle_length == 1 {
1036 CircularReferenceType::SelfReference
1037 } else if cycle_length == 2 {
1038 CircularReferenceType::Simple
1039 } else {
1040 CircularReferenceType::Complex
1041 };
1042 assert_eq!(complex_type, CircularReferenceType::Complex);
1043 }
1044}