1use crate::core::types::AllocationInfo;
7use crate::export::binary::error::BinaryExportError;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct VariableRelationshipAnalysis {
14 pub graph: RelationshipGraph,
16 pub summary: RelationshipSummary,
18 pub patterns: Vec<RelationshipPattern>,
20 pub optimization: GraphOptimization,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct RelationshipGraph {
27 pub nodes: Vec<GraphNode>,
29 pub links: Vec<GraphEdge>,
31 pub metadata: GraphMetadata,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct GraphNode {
38 pub id: String,
40 pub name: String,
42 pub address: usize,
44 pub size: usize,
46 pub type_name: String,
48 pub scope: String,
50 pub category: NodeCategory,
52 pub ownership: OwnershipStatus,
54 pub lifetime: LifetimeInfo,
56 pub visual: NodeVisual,
58 pub stats: NodeStats,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct GraphEdge {
65 pub source: String,
67 pub target: String,
69 pub relationship: RelationshipType,
71 pub strength: f64,
73 pub direction: EdgeDirection,
75 pub visual: EdgeVisual,
77 pub metadata: EdgeMetadata,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct GraphMetadata {
84 pub node_count: usize,
86 pub edge_count: usize,
88 pub density: f64,
90 pub clustering_coefficient: f64,
92 pub average_path_length: f64,
94 pub layout: LayoutConfig,
96 pub performance: PerformanceHints,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub enum NodeCategory {
103 Stack,
105 Heap,
107 SmartPointer,
109 Reference,
111 RawPointer,
113 Collection,
115 Primitive,
117 Custom,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
123pub enum OwnershipStatus {
124 Owner,
126 BorrowedImmutable,
128 BorrowedMutable,
130 Shared,
132 Weak,
134 Unknown,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct LifetimeInfo {
141 pub start: u64,
143 pub end: Option<u64>,
145 pub duration_ms: Option<u64>,
147 pub category: LifetimeCategory,
149 pub is_active: bool,
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
155pub enum LifetimeCategory {
156 Instant,
158 Short,
160 Medium,
162 Long,
164 Persistent,
166 Active,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct NodeVisual {
173 pub x: Option<f64>,
175 pub y: Option<f64>,
177 pub radius: f64,
179 pub color: String,
181 pub opacity: f64,
183 pub css_class: String,
185 pub fixed: bool,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct NodeStats {
192 pub in_degree: usize,
194 pub out_degree: usize,
196 pub centrality: f64,
198 pub clustering: f64,
200 pub page_rank: f64,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
206pub enum RelationshipType {
207 Ownership,
209 Borrowing,
211 Reference,
213 Containment,
215 Dependency,
217 SharedOwnership,
219 Temporal,
221 MemoryAdjacency,
223 TypeSimilarity,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
229pub enum EdgeDirection {
230 Directed,
232 Undirected,
234 Bidirectional,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
240pub struct EdgeVisual {
241 pub width: f64,
243 pub color: String,
245 pub opacity: f64,
247 pub style: String,
249 pub css_class: String,
251 pub marker: Option<String>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct EdgeMetadata {
258 pub created_at: u64,
260 pub ended_at: Option<u64>,
262 pub confidence: f64,
264 pub source: String,
266 pub properties: HashMap<String, String>,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct LayoutConfig {
273 pub algorithm: String,
275 pub force: ForceConfig,
277 pub hierarchical: Option<HierarchicalConfig>,
279 pub viewport: ViewportConfig,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ForceConfig {
286 pub link_strength: f64,
288 pub charge_strength: f64,
290 pub center_strength: f64,
292 pub collision_radius: f64,
294 pub alpha_decay: f64,
296 pub velocity_decay: f64,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct HierarchicalConfig {
303 pub direction: String,
305 pub level_separation: f64,
307 pub node_separation: f64,
309 pub tree_separation: f64,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct ViewportConfig {
316 pub width: f64,
318 pub height: f64,
320 pub zoom: f64,
322 pub pan_x: f64,
324 pub pan_y: f64,
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct PerformanceHints {
331 pub use_canvas: bool,
333 pub use_lod: bool,
335 pub max_visible_nodes: usize,
337 pub enable_clustering: bool,
339 pub clustering_threshold: f64,
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct RelationshipSummary {
346 pub total_variables: usize,
348 pub total_relationships: usize,
350 pub relationship_distribution: HashMap<RelationshipType, usize>,
352 pub ownership_distribution: HashMap<OwnershipStatus, usize>,
354 pub average_relationships: f64,
356 pub complexity_score: u32,
358 pub memory_insights: MemoryInsights,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct RelationshipPattern {
365 pub name: String,
367 pub description: String,
369 pub nodes: Vec<String>,
371 pub confidence: f64,
373 pub category: PatternCategory,
375 pub recommendations: Vec<String>,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
381pub enum PatternCategory {
382 OwnershipChain,
384 CircularReference,
386 Hub,
388 Cluster,
390 Tree,
392 MemoryLeak,
394 OptimizationOpportunity,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
400pub struct MemoryInsights {
401 pub total_memory: usize,
403 pub fragmentation_score: f64,
405 pub sharing_efficiency: f64,
407 pub lifetime_distribution: HashMap<LifetimeCategory, usize>,
409 pub optimizations: Vec<String>,
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct GraphOptimization {
416 pub simplified: Option<RelationshipGraph>,
418 pub clusters: Vec<NodeCluster>,
420 pub lod_levels: Vec<LodLevel>,
422 pub rendering: RenderingHints,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct NodeCluster {
429 pub id: String,
431 pub nodes: Vec<String>,
433 pub center: (f64, f64),
435 pub radius: f64,
437 pub representative: String,
439 pub stats: ClusterStats,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct ClusterStats {
446 pub node_count: usize,
448 pub internal_edges: usize,
450 pub external_edges: usize,
452 pub density: f64,
454 pub total_memory: usize,
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize)]
460pub struct LodLevel {
461 pub zoom_threshold: f64,
463 pub max_nodes: usize,
465 pub edge_simplification: f64,
467 pub label_threshold: f64,
469}
470
471#[derive(Debug, Clone, Serialize, Deserialize)]
473pub struct RenderingHints {
474 pub use_webgl: bool,
476 pub use_instancing: bool,
478 pub batch_size: usize,
480 pub target_fps: u32,
482 pub memory_budget: usize,
484}
485
486pub struct VariableRelationshipAnalyzer {
488 nodes: HashMap<String, GraphNode>,
490 edges: Vec<GraphEdge>,
492 node_counter: usize,
494 relationship_detector: RelationshipDetector,
496 pattern_detector: PatternDetector,
498 optimizer: GraphOptimizer,
500}
501
502#[derive(Debug, Clone)]
504struct RelationshipDetector {
505 address_map: HashMap<usize, String>,
507 type_map: HashMap<String, Vec<String>>,
509 scope_map: HashMap<String, Vec<String>>,
511 temporal_order: Vec<(String, u64)>,
513}
514
515#[derive(Debug, Clone)]
517struct PatternDetector {
518 patterns: Vec<RelationshipPattern>,
520}
521
522#[derive(Debug, Clone)]
524struct GraphOptimizer {}
525
526impl VariableRelationshipAnalyzer {
527 pub fn new() -> Self {
529 Self {
530 nodes: HashMap::new(),
531 edges: Vec::new(),
532 node_counter: 0,
533 relationship_detector: RelationshipDetector::new(),
534 pattern_detector: PatternDetector::new(),
535 optimizer: GraphOptimizer::new(),
536 }
537 }
538
539 pub fn analyze_allocations(
541 allocations: &[AllocationInfo],
542 ) -> Result<VariableRelationshipAnalysis, BinaryExportError> {
543 let mut analyzer = Self::new();
544
545 for allocation in allocations {
547 analyzer.create_node_from_allocation(allocation)?;
548 }
549
550 analyzer.detect_relationships(allocations)?;
552
553 analyzer.detect_patterns()?;
555
556 analyzer.optimize_graph()?;
558
559 analyzer.generate_analysis()
561 }
562
563 fn create_node_from_allocation(
565 &mut self,
566 allocation: &AllocationInfo,
567 ) -> Result<(), BinaryExportError> {
568 let node_id = format!("node_{}", self.node_counter);
569 self.node_counter += 1;
570
571 let name = allocation
572 .var_name
573 .clone()
574 .unwrap_or_else(|| format!("alloc_{:x}", allocation.ptr));
575
576 let type_name = allocation
577 .type_name
578 .clone()
579 .unwrap_or_else(|| "unknown".to_string());
580
581 let scope = allocation
582 .scope_name
583 .clone()
584 .unwrap_or_else(|| "global".to_string());
585
586 let category = self.determine_node_category(&type_name, allocation);
587 let ownership = self.determine_ownership_status(allocation);
588 let lifetime = self.create_lifetime_info(allocation);
589 let visual = self.create_node_visual(&category, allocation.size);
590 let stats = NodeStats {
591 in_degree: 0,
592 out_degree: 0,
593 centrality: 0.0,
594 clustering: 0.0,
595 page_rank: 0.0,
596 };
597
598 let node = GraphNode {
599 id: node_id.clone(),
600 name,
601 address: allocation.ptr,
602 size: allocation.size,
603 type_name: type_name.clone(),
604 scope: scope.clone(),
605 category,
606 ownership,
607 lifetime,
608 visual,
609 stats,
610 };
611
612 self.relationship_detector
614 .address_map
615 .insert(allocation.ptr, node_id.clone());
616 self.relationship_detector
617 .type_map
618 .entry(type_name)
619 .or_default()
620 .push(node_id.clone());
621 self.relationship_detector
622 .scope_map
623 .entry(scope)
624 .or_default()
625 .push(node_id.clone());
626 self.relationship_detector
627 .temporal_order
628 .push((node_id.clone(), allocation.timestamp_alloc));
629
630 self.nodes.insert(node_id, node);
631 Ok(())
632 }
633
634 fn determine_node_category(
636 &self,
637 type_name: &str,
638 allocation: &AllocationInfo,
639 ) -> NodeCategory {
640 if type_name.starts_with("Box<")
641 || type_name.starts_with("Rc<")
642 || type_name.starts_with("Arc<")
643 {
644 NodeCategory::SmartPointer
645 } else if type_name.starts_with("&") {
646 NodeCategory::Reference
647 } else if type_name.starts_with("*const") || type_name.starts_with("*mut") {
648 NodeCategory::RawPointer
649 } else if type_name.starts_with("Vec<")
650 || type_name.starts_with("HashMap<")
651 || type_name.starts_with("HashSet<")
652 {
653 NodeCategory::Collection
654 } else if self.is_primitive_type(type_name) {
655 NodeCategory::Primitive
656 } else if allocation.size < 1024 {
657 NodeCategory::Stack
659 } else {
660 NodeCategory::Heap
662 }
663 }
664
665 fn is_primitive_type(&self, type_name: &str) -> bool {
667 matches!(
668 type_name,
669 "i8" | "i16"
670 | "i32"
671 | "i64"
672 | "i128"
673 | "isize"
674 | "u8"
675 | "u16"
676 | "u32"
677 | "u64"
678 | "u128"
679 | "usize"
680 | "f32"
681 | "f64"
682 | "bool"
683 | "char"
684 | "()"
685 )
686 }
687
688 fn determine_ownership_status(&self, allocation: &AllocationInfo) -> OwnershipStatus {
690 if let Some(ref type_name) = allocation.type_name {
691 if type_name.starts_with("&mut") {
692 OwnershipStatus::BorrowedMutable
693 } else if type_name.starts_with("&") {
694 OwnershipStatus::BorrowedImmutable
695 } else if type_name.starts_with("Rc<") || type_name.starts_with("Arc<") {
696 OwnershipStatus::Shared
697 } else if type_name.contains("Weak<") {
698 OwnershipStatus::Weak
699 } else {
700 OwnershipStatus::Owner
701 }
702 } else {
703 OwnershipStatus::Unknown
704 }
705 }
706
707 fn create_lifetime_info(&self, allocation: &AllocationInfo) -> LifetimeInfo {
709 let start = allocation.timestamp_alloc;
710 let end = allocation.timestamp_dealloc;
711 let duration_ms = allocation.lifetime_ms;
712
713 let category = if let Some(duration) = duration_ms {
714 if duration < 1 {
715 LifetimeCategory::Instant
716 } else if duration < 100 {
717 LifetimeCategory::Short
718 } else if duration < 1000 {
719 LifetimeCategory::Medium
720 } else if duration < 10000 {
721 LifetimeCategory::Long
722 } else {
723 LifetimeCategory::Persistent
724 }
725 } else {
726 LifetimeCategory::Active
727 };
728
729 LifetimeInfo {
730 start,
731 end,
732 duration_ms,
733 category,
734 is_active: end.is_none(),
735 }
736 }
737
738 fn create_node_visual(&self, category: &NodeCategory, size: usize) -> NodeVisual {
740 let radius = (size as f64).log10().clamp(3.0, 20.0);
741
742 let color = match category {
743 NodeCategory::Stack => "#4CAF50".to_string(), NodeCategory::Heap => "#2196F3".to_string(), NodeCategory::SmartPointer => "#FF9800".to_string(), NodeCategory::Reference => "#9C27B0".to_string(), NodeCategory::RawPointer => "#F44336".to_string(), NodeCategory::Collection => "#00BCD4".to_string(), NodeCategory::Primitive => "#8BC34A".to_string(), NodeCategory::Custom => "#607D8B".to_string(), };
752
753 let css_class = format!("node-{category:?}").to_lowercase();
754
755 NodeVisual {
756 x: None,
757 y: None,
758 radius,
759 color,
760 opacity: 0.8,
761 css_class,
762 fixed: false,
763 }
764 }
765
766 fn detect_relationships(
768 &mut self,
769 _allocations: &[AllocationInfo],
770 ) -> Result<(), BinaryExportError> {
771 self.relationship_detector
773 .temporal_order
774 .sort_by_key(|(_, timestamp)| *timestamp);
775
776 self.detect_ownership_relationships()?;
778 self.detect_type_relationships()?;
779 self.detect_scope_relationships()?;
780 self.detect_memory_adjacency_relationships()?;
781 self.detect_temporal_relationships()?;
782
783 Ok(())
784 }
785
786 fn detect_ownership_relationships(&mut self) -> Result<(), BinaryExportError> {
788 for (node_id, node) in &self.nodes {
790 if matches!(node.category, NodeCategory::SmartPointer) {
791 for (other_id, other_node) in &self.nodes {
793 if node_id != other_id && self.could_be_owned_by(node, other_node) {
794 let edge = self.create_edge(
795 node_id.clone(),
796 other_id.clone(),
797 RelationshipType::Ownership,
798 0.8,
799 EdgeDirection::Directed,
800 );
801 self.edges.push(edge);
802 }
803 }
804 }
805 }
806 Ok(())
807 }
808
809 fn detect_type_relationships(&mut self) -> Result<(), BinaryExportError> {
811 for node_ids in self.relationship_detector.type_map.values() {
812 if node_ids.len() > 1 {
813 for i in 0..node_ids.len() {
815 for j in i + 1..node_ids.len() {
816 let edge = self.create_edge(
817 node_ids[i].clone(),
818 node_ids[j].clone(),
819 RelationshipType::TypeSimilarity,
820 0.5,
821 EdgeDirection::Undirected,
822 );
823 self.edges.push(edge);
824 }
825 }
826 }
827 }
828 Ok(())
829 }
830
831 fn detect_scope_relationships(&mut self) -> Result<(), BinaryExportError> {
833 for node_ids in self.relationship_detector.scope_map.values() {
834 if node_ids.len() > 1 {
835 for i in 0..node_ids.len() {
837 for j in i + 1..node_ids.len() {
838 let edge = self.create_edge(
839 node_ids[i].clone(),
840 node_ids[j].clone(),
841 RelationshipType::Containment,
842 0.3,
843 EdgeDirection::Undirected,
844 );
845 self.edges.push(edge);
846 }
847 }
848 }
849 }
850 Ok(())
851 }
852
853 fn detect_memory_adjacency_relationships(&mut self) -> Result<(), BinaryExportError> {
855 let mut addresses: Vec<(usize, String)> = self
856 .nodes
857 .iter()
858 .map(|(id, node)| (node.address, id.clone()))
859 .collect();
860 addresses.sort_by_key(|(addr, _)| *addr);
861
862 for i in 0..addresses.len().saturating_sub(1) {
863 let (addr1, id1) = &addresses[i];
864 let (addr2, id2) = &addresses[i + 1];
865
866 let node1 = &self.nodes[id1];
867 let _node2 = &self.nodes[id2];
868
869 if addr1 + node1.size == *addr2 {
871 let edge = self.create_edge(
872 id1.clone(),
873 id2.clone(),
874 RelationshipType::MemoryAdjacency,
875 0.6,
876 EdgeDirection::Undirected,
877 );
878 self.edges.push(edge);
879 }
880 }
881 Ok(())
882 }
883
884 fn detect_temporal_relationships(&mut self) -> Result<(), BinaryExportError> {
886 for i in 0..self
887 .relationship_detector
888 .temporal_order
889 .len()
890 .saturating_sub(1)
891 {
892 let (id1, _) = &self.relationship_detector.temporal_order[i];
893 let (id2, _) = &self.relationship_detector.temporal_order[i + 1];
894
895 let edge = self.create_edge(
897 id1.clone(),
898 id2.clone(),
899 RelationshipType::Temporal,
900 0.2,
901 EdgeDirection::Directed,
902 );
903 self.edges.push(edge);
904 }
905 Ok(())
906 }
907
908 fn could_be_owned_by(&self, owner: &GraphNode, owned: &GraphNode) -> bool {
910 matches!(owner.category, NodeCategory::SmartPointer)
912 && matches!(owned.category, NodeCategory::Heap | NodeCategory::Custom)
913 && owner.scope == owned.scope
914 }
915
916 fn create_edge(
918 &self,
919 source: String,
920 target: String,
921 relationship: RelationshipType,
922 strength: f64,
923 direction: EdgeDirection,
924 ) -> GraphEdge {
925 let visual = self.create_edge_visual(&relationship, strength);
926 let metadata = EdgeMetadata {
927 created_at: 0, ended_at: None,
929 confidence: strength,
930 source: "relationship_analyzer".to_string(),
931 properties: HashMap::new(),
932 };
933
934 GraphEdge {
935 source,
936 target,
937 relationship,
938 strength,
939 direction,
940 visual,
941 metadata,
942 }
943 }
944
945 fn create_edge_visual(&self, relationship: &RelationshipType, strength: f64) -> EdgeVisual {
947 let width = (strength * 3.0).max(1.0);
948
949 let color = match relationship {
950 RelationshipType::Ownership => "#FF5722".to_string(), RelationshipType::Borrowing => "#3F51B5".to_string(), RelationshipType::Reference => "#9C27B0".to_string(), RelationshipType::Containment => "#4CAF50".to_string(), RelationshipType::Dependency => "#FF9800".to_string(), RelationshipType::SharedOwnership => "#E91E63".to_string(), RelationshipType::Temporal => "#607D8B".to_string(), RelationshipType::MemoryAdjacency => "#00BCD4".to_string(), RelationshipType::TypeSimilarity => "#795548".to_string(), };
960
961 let style = match relationship {
962 RelationshipType::Temporal => "dashed".to_string(),
963 RelationshipType::TypeSimilarity => "dotted".to_string(),
964 _ => "solid".to_string(),
965 };
966
967 EdgeVisual {
968 width,
969 color,
970 opacity: (strength * 0.8).max(0.3),
971 style,
972 css_class: format!("edge-{relationship:?}").to_lowercase(),
973 marker: Some("arrow".to_string()),
974 }
975 }
976
977 fn detect_patterns(&mut self) -> Result<(), BinaryExportError> {
979 self.pattern_detector
980 .detect_ownership_chains(&self.nodes, &self.edges)?;
981 self.pattern_detector
982 .detect_circular_references(&self.nodes, &self.edges)?;
983 self.pattern_detector
984 .detect_hub_patterns(&self.nodes, &self.edges)?;
985 self.pattern_detector
986 .detect_memory_leaks(&self.nodes, &self.edges)?;
987 Ok(())
988 }
989
990 fn optimize_graph(&mut self) -> Result<(), BinaryExportError> {
992 self.optimizer
993 .create_clusters(&mut self.nodes, &self.edges)?;
994 self.optimizer
995 .create_simplified_graph(&self.nodes, &self.edges)?;
996 self.optimizer.create_lod_levels(&self.nodes, &self.edges)?;
997 Ok(())
998 }
999
1000 fn generate_analysis(&self) -> Result<VariableRelationshipAnalysis, BinaryExportError> {
1002 let nodes: Vec<GraphNode> = self.nodes.values().cloned().collect();
1003 let links = self.edges.clone();
1004
1005 let metadata = self.create_graph_metadata(&nodes, &links);
1006
1007 let graph = RelationshipGraph {
1008 nodes,
1009 links,
1010 metadata,
1011 };
1012
1013 let summary = self.create_relationship_summary(&graph)?;
1014 let patterns = self.pattern_detector.patterns.clone();
1015 let optimization = self.optimizer.create_optimization_data(&graph)?;
1016
1017 Ok(VariableRelationshipAnalysis {
1018 graph,
1019 summary,
1020 patterns,
1021 optimization,
1022 })
1023 }
1024
1025 fn create_graph_metadata(&self, nodes: &[GraphNode], edges: &[GraphEdge]) -> GraphMetadata {
1027 let node_count = nodes.len();
1028 let edge_count = edges.len();
1029 let max_edges = if node_count > 1 {
1030 node_count * (node_count - 1) / 2
1031 } else {
1032 1 };
1034 let density = if max_edges > 0 {
1035 (edge_count as f64 / max_edges as f64).min(1.0) } else {
1037 0.0
1038 };
1039
1040 let layout = LayoutConfig {
1041 algorithm: "force".to_string(),
1042 force: ForceConfig {
1043 link_strength: 0.1,
1044 charge_strength: -300.0,
1045 center_strength: 0.1,
1046 collision_radius: 5.0,
1047 alpha_decay: 0.0228,
1048 velocity_decay: 0.4,
1049 },
1050 hierarchical: None,
1051 viewport: ViewportConfig {
1052 width: 800.0,
1053 height: 600.0,
1054 zoom: 1.0,
1055 pan_x: 0.0,
1056 pan_y: 0.0,
1057 },
1058 };
1059
1060 let performance = PerformanceHints {
1061 use_canvas: node_count > 1000,
1062 use_lod: node_count > 500,
1063 max_visible_nodes: 1000,
1064 enable_clustering: node_count > 200,
1065 clustering_threshold: 0.7,
1066 };
1067
1068 GraphMetadata {
1069 node_count,
1070 edge_count,
1071 density,
1072 clustering_coefficient: 0.0, average_path_length: 0.0, layout,
1075 performance,
1076 }
1077 }
1078
1079 fn create_relationship_summary(
1081 &self,
1082 graph: &RelationshipGraph,
1083 ) -> Result<RelationshipSummary, BinaryExportError> {
1084 let total_variables = graph.nodes.len();
1085 let total_relationships = graph.links.len();
1086
1087 let mut relationship_distribution = HashMap::new();
1088 let mut ownership_distribution = HashMap::new();
1089
1090 for edge in &graph.links {
1091 *relationship_distribution
1092 .entry(edge.relationship.clone())
1093 .or_insert(0) += 1;
1094 }
1095
1096 for node in &graph.nodes {
1097 *ownership_distribution
1098 .entry(node.ownership.clone())
1099 .or_insert(0) += 1;
1100 }
1101
1102 let average_relationships = if total_variables > 0 {
1103 total_relationships as f64 / total_variables as f64
1104 } else {
1105 0.0
1106 };
1107
1108 let complexity_score = self.calculate_complexity_score(graph);
1109 let memory_insights = self.create_memory_insights(graph);
1110
1111 Ok(RelationshipSummary {
1112 total_variables,
1113 total_relationships,
1114 relationship_distribution,
1115 ownership_distribution,
1116 average_relationships,
1117 complexity_score,
1118 memory_insights,
1119 })
1120 }
1121
1122 fn calculate_complexity_score(&self, graph: &RelationshipGraph) -> u32 {
1124 let base_score = (graph.metadata.density * 50.0) as u32;
1125 let edge_penalty = (graph.links.len() / 10).min(30) as u32;
1126 let node_penalty = (graph.nodes.len() / 50).min(20) as u32;
1127
1128 (base_score + edge_penalty + node_penalty).min(100)
1129 }
1130
1131 fn create_memory_insights(&self, graph: &RelationshipGraph) -> MemoryInsights {
1133 let total_memory: usize = graph.nodes.iter().map(|n| n.size).sum();
1134
1135 let mut lifetime_distribution = HashMap::new();
1136 for node in &graph.nodes {
1137 *lifetime_distribution
1138 .entry(node.lifetime.category.clone())
1139 .or_insert(0) += 1;
1140 }
1141
1142 MemoryInsights {
1143 total_memory,
1144 fragmentation_score: 0.5, sharing_efficiency: 0.7, lifetime_distribution,
1147 optimizations: vec![
1148 "Consider using Rc/Arc for shared data".to_string(),
1149 "Review long-lived allocations".to_string(),
1150 ],
1151 }
1152 }
1153}
1154
1155impl RelationshipDetector {
1156 fn new() -> Self {
1157 Self {
1158 address_map: HashMap::new(),
1159 type_map: HashMap::new(),
1160 scope_map: HashMap::new(),
1161 temporal_order: Vec::new(),
1162 }
1163 }
1164}
1165
1166impl PatternDetector {
1167 fn new() -> Self {
1168 Self {
1169 patterns: Vec::new(),
1170 }
1171 }
1172
1173 fn detect_ownership_chains(
1174 &mut self,
1175 _nodes: &HashMap<String, GraphNode>,
1176 _edges: &[GraphEdge],
1177 ) -> Result<(), BinaryExportError> {
1178 Ok(())
1180 }
1181
1182 fn detect_circular_references(
1183 &mut self,
1184 _nodes: &HashMap<String, GraphNode>,
1185 _edges: &[GraphEdge],
1186 ) -> Result<(), BinaryExportError> {
1187 Ok(())
1189 }
1190
1191 fn detect_hub_patterns(
1192 &mut self,
1193 _nodes: &HashMap<String, GraphNode>,
1194 _edges: &[GraphEdge],
1195 ) -> Result<(), BinaryExportError> {
1196 Ok(())
1198 }
1199
1200 fn detect_memory_leaks(
1201 &mut self,
1202 nodes: &HashMap<String, GraphNode>,
1203 _edges: &[GraphEdge],
1204 ) -> Result<(), BinaryExportError> {
1205 let leaked_nodes: Vec<String> = nodes
1207 .iter()
1208 .filter(|(_, node)| {
1209 !node.lifetime.is_active && node.lifetime.category == LifetimeCategory::Persistent
1210 })
1211 .map(|(id, _)| id.clone())
1212 .collect();
1213
1214 if !leaked_nodes.is_empty() {
1215 let pattern = RelationshipPattern {
1216 name: "Potential Memory Leak".to_string(),
1217 description: "Long-lived allocations that may indicate memory leaks".to_string(),
1218 nodes: leaked_nodes,
1219 confidence: 0.6,
1220 category: PatternCategory::MemoryLeak,
1221 recommendations: vec![
1222 "Review allocation lifetimes".to_string(),
1223 "Consider using RAII patterns".to_string(),
1224 ],
1225 };
1226 self.patterns.push(pattern);
1227 }
1228
1229 Ok(())
1230 }
1231}
1232
1233impl GraphOptimizer {
1234 fn new() -> Self {
1235 Self {}
1236 }
1237
1238 fn create_clusters(
1239 &mut self,
1240 _nodes: &mut HashMap<String, GraphNode>,
1241 _edges: &[GraphEdge],
1242 ) -> Result<(), BinaryExportError> {
1243 Ok(())
1245 }
1246
1247 fn create_simplified_graph(
1248 &mut self,
1249 _nodes: &HashMap<String, GraphNode>,
1250 _edges: &[GraphEdge],
1251 ) -> Result<(), BinaryExportError> {
1252 Ok(())
1254 }
1255
1256 fn create_lod_levels(
1257 &mut self,
1258 _nodes: &HashMap<String, GraphNode>,
1259 _edges: &[GraphEdge],
1260 ) -> Result<(), BinaryExportError> {
1261 Ok(())
1263 }
1264
1265 fn create_optimization_data(
1266 &self,
1267 _graph: &RelationshipGraph,
1268 ) -> Result<GraphOptimization, BinaryExportError> {
1269 Ok(GraphOptimization {
1270 simplified: None,
1271 clusters: Vec::new(),
1272 lod_levels: vec![
1273 LodLevel {
1274 zoom_threshold: 0.5,
1275 max_nodes: 100,
1276 edge_simplification: 0.8,
1277 label_threshold: 0.3,
1278 },
1279 LodLevel {
1280 zoom_threshold: 1.0,
1281 max_nodes: 500,
1282 edge_simplification: 0.5,
1283 label_threshold: 0.6,
1284 },
1285 ],
1286 rendering: RenderingHints {
1287 use_webgl: true,
1288 use_instancing: true,
1289 batch_size: 1000,
1290 target_fps: 60,
1291 memory_budget: 256,
1292 },
1293 })
1294 }
1295}
1296
1297impl Default for VariableRelationshipAnalyzer {
1298 fn default() -> Self {
1299 Self::new()
1300 }
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305 use super::*;
1306
1307 fn create_test_allocation(
1308 ptr: usize,
1309 size: usize,
1310 var_name: Option<&str>,
1311 type_name: Option<&str>,
1312 scope_name: Option<&str>,
1313 ) -> AllocationInfo {
1314 AllocationInfo {
1315 ptr,
1316 size,
1317 var_name: var_name.map(|s| s.to_string()),
1318 type_name: type_name.map(|s| s.to_string()),
1319 scope_name: scope_name.map(|s| s.to_string()),
1320 timestamp_alloc: 1000,
1321 timestamp_dealloc: None,
1322 thread_id: "main".to_string(),
1323 borrow_count: 0,
1324 stack_trace: None,
1325 is_leaked: false,
1326 lifetime_ms: Some(100),
1327 borrow_info: None,
1328 clone_info: None,
1329 ownership_history_available: false,
1330 smart_pointer_info: None,
1331 memory_layout: None,
1332 generic_info: None,
1333 dynamic_type_info: None,
1334 runtime_state: None,
1335 stack_allocation: None,
1336 temporary_object: None,
1337 fragmentation_analysis: None,
1338 generic_instantiation: None,
1339 type_relationships: None,
1340 type_usage: None,
1341 function_call_tracking: None,
1342 lifecycle_tracking: None,
1343 access_tracking: None,
1344 drop_chain_analysis: None,
1345 }
1346 }
1347
1348 #[test]
1349 fn test_node_category_determination() {
1350 let analyzer = VariableRelationshipAnalyzer::new();
1351 let allocation =
1352 create_test_allocation(0x1000, 1024, Some("test"), Some("Vec<u8>"), Some("main"));
1353
1354 let category = analyzer.determine_node_category("Vec<u8>", &allocation);
1355 assert_eq!(category, NodeCategory::Collection);
1356
1357 let category = analyzer.determine_node_category("Box<i32>", &allocation);
1358 assert_eq!(category, NodeCategory::SmartPointer);
1359
1360 let category = analyzer.determine_node_category("&str", &allocation);
1361 assert_eq!(category, NodeCategory::Reference);
1362
1363 let category = analyzer.determine_node_category("*mut u8", &allocation);
1364 assert_eq!(category, NodeCategory::RawPointer);
1365 }
1366
1367 #[test]
1368 fn test_ownership_status_determination() {
1369 let analyzer = VariableRelationshipAnalyzer::new();
1370
1371 let allocation =
1372 create_test_allocation(0x1000, 1024, Some("test"), Some("&mut i32"), Some("main"));
1373 let ownership = analyzer.determine_ownership_status(&allocation);
1374 assert_eq!(ownership, OwnershipStatus::BorrowedMutable);
1375
1376 let allocation =
1377 create_test_allocation(0x1000, 1024, Some("test"), Some("&i32"), Some("main"));
1378 let ownership = analyzer.determine_ownership_status(&allocation);
1379 assert_eq!(ownership, OwnershipStatus::BorrowedImmutable);
1380
1381 let allocation =
1382 create_test_allocation(0x1000, 1024, Some("test"), Some("Rc<i32>"), Some("main"));
1383 let ownership = analyzer.determine_ownership_status(&allocation);
1384 assert_eq!(ownership, OwnershipStatus::Shared);
1385 }
1386
1387 #[test]
1388 fn test_lifetime_category_determination() {
1389 let analyzer = VariableRelationshipAnalyzer::new();
1390
1391 let mut allocation =
1392 create_test_allocation(0x1000, 1024, Some("test"), Some("i32"), Some("main"));
1393 allocation.lifetime_ms = Some(0);
1394 let lifetime = analyzer.create_lifetime_info(&allocation);
1395 assert_eq!(lifetime.category, LifetimeCategory::Instant);
1396
1397 allocation.lifetime_ms = Some(50);
1398 let lifetime = analyzer.create_lifetime_info(&allocation);
1399 assert_eq!(lifetime.category, LifetimeCategory::Short);
1400
1401 allocation.lifetime_ms = Some(500);
1402 let lifetime = analyzer.create_lifetime_info(&allocation);
1403 assert_eq!(lifetime.category, LifetimeCategory::Medium);
1404
1405 allocation.lifetime_ms = None;
1406 let lifetime = analyzer.create_lifetime_info(&allocation);
1407 assert_eq!(lifetime.category, LifetimeCategory::Active);
1408 }
1409
1410 #[test]
1411 fn test_variable_relationship_analysis() {
1412 let allocations = vec![
1413 create_test_allocation(0x1000, 1024, Some("vec1"), Some("Vec<u8>"), Some("main")),
1414 create_test_allocation(0x2000, 2048, Some("box1"), Some("Box<i32>"), Some("main")),
1415 create_test_allocation(0x3000, 512, Some("ref1"), Some("&str"), Some("main")),
1416 create_test_allocation(0x4000, 256, Some("int1"), Some("i32"), Some("helper")),
1417 ];
1418
1419 let analysis = VariableRelationshipAnalyzer::analyze_allocations(&allocations)
1420 .expect("Failed to get test value");
1421
1422 assert_eq!(analysis.graph.nodes.len(), 4);
1423 assert_eq!(analysis.summary.total_variables, 4);
1424 assert!(analysis.summary.total_relationships > 0);
1425 assert!(!analysis.summary.relationship_distribution.is_empty());
1426 assert!(!analysis.summary.ownership_distribution.is_empty());
1427 }
1428
1429 #[test]
1430 fn test_primitive_type_detection() {
1431 let analyzer = VariableRelationshipAnalyzer::new();
1432
1433 assert!(analyzer.is_primitive_type("i32"));
1434 assert!(analyzer.is_primitive_type("f64"));
1435 assert!(analyzer.is_primitive_type("bool"));
1436 assert!(analyzer.is_primitive_type("char"));
1437 assert!(!analyzer.is_primitive_type("String"));
1438 assert!(!analyzer.is_primitive_type("Vec<u8>"));
1439 }
1440
1441 #[test]
1442 fn test_graph_metadata_creation() {
1443 let nodes = vec![GraphNode {
1444 id: "node1".to_string(),
1445 name: "test1".to_string(),
1446 address: 0x1000,
1447 size: 1024,
1448 type_name: "i32".to_string(),
1449 scope: "main".to_string(),
1450 category: NodeCategory::Primitive,
1451 ownership: OwnershipStatus::Owner,
1452 lifetime: LifetimeInfo {
1453 start: 1000,
1454 end: None,
1455 duration_ms: None,
1456 category: LifetimeCategory::Active,
1457 is_active: true,
1458 },
1459 visual: NodeVisual {
1460 x: None,
1461 y: None,
1462 radius: 5.0,
1463 color: "#4CAF50".to_string(),
1464 opacity: 0.8,
1465 css_class: "node-primitive".to_string(),
1466 fixed: false,
1467 },
1468 stats: NodeStats {
1469 in_degree: 0,
1470 out_degree: 1,
1471 centrality: 0.5,
1472 clustering: 0.0,
1473 page_rank: 0.25,
1474 },
1475 }];
1476
1477 let edges = vec![];
1478 let analyzer = VariableRelationshipAnalyzer::new();
1479 let metadata = analyzer.create_graph_metadata(&nodes, &edges);
1480
1481 assert_eq!(metadata.node_count, 1);
1482 assert_eq!(metadata.edge_count, 0);
1483 assert_eq!(metadata.density, 0.0);
1484 assert_eq!(metadata.layout.algorithm, "force");
1485 }
1486
1487 #[test]
1488 fn test_edge_creation() {
1489 let analyzer = VariableRelationshipAnalyzer::new();
1490
1491 let edge = analyzer.create_edge(
1492 "node1".to_string(),
1493 "node2".to_string(),
1494 RelationshipType::Ownership,
1495 0.8,
1496 EdgeDirection::Directed,
1497 );
1498
1499 assert_eq!(edge.source, "node1");
1500 assert_eq!(edge.target, "node2");
1501 assert_eq!(edge.relationship, RelationshipType::Ownership);
1502 assert_eq!(edge.strength, 0.8);
1503 assert_eq!(edge.direction, EdgeDirection::Directed);
1504 }
1505
1506 #[test]
1507 fn test_memory_insights_creation() {
1508 let graph = RelationshipGraph {
1509 nodes: vec![GraphNode {
1510 id: "node1".to_string(),
1511 name: "test1".to_string(),
1512 address: 0x1000,
1513 size: 1024,
1514 type_name: "i32".to_string(),
1515 scope: "main".to_string(),
1516 category: NodeCategory::Primitive,
1517 ownership: OwnershipStatus::Owner,
1518 lifetime: LifetimeInfo {
1519 start: 1000,
1520 end: None,
1521 duration_ms: Some(100),
1522 category: LifetimeCategory::Short,
1523 is_active: false,
1524 },
1525 visual: NodeVisual {
1526 x: None,
1527 y: None,
1528 radius: 5.0,
1529 color: "#4CAF50".to_string(),
1530 opacity: 0.8,
1531 css_class: "node-primitive".to_string(),
1532 fixed: false,
1533 },
1534 stats: NodeStats {
1535 in_degree: 0,
1536 out_degree: 0,
1537 centrality: 0.0,
1538 clustering: 0.0,
1539 page_rank: 0.0,
1540 },
1541 }],
1542 links: vec![],
1543 metadata: GraphMetadata {
1544 node_count: 1,
1545 edge_count: 0,
1546 density: 0.0,
1547 clustering_coefficient: 0.0,
1548 average_path_length: 0.0,
1549 layout: LayoutConfig {
1550 algorithm: "force".to_string(),
1551 force: ForceConfig {
1552 link_strength: 0.1,
1553 charge_strength: -300.0,
1554 center_strength: 0.1,
1555 collision_radius: 5.0,
1556 alpha_decay: 0.0228,
1557 velocity_decay: 0.4,
1558 },
1559 hierarchical: None,
1560 viewport: ViewportConfig {
1561 width: 800.0,
1562 height: 600.0,
1563 zoom: 1.0,
1564 pan_x: 0.0,
1565 pan_y: 0.0,
1566 },
1567 },
1568 performance: PerformanceHints {
1569 use_canvas: false,
1570 use_lod: false,
1571 max_visible_nodes: 1000,
1572 enable_clustering: false,
1573 clustering_threshold: 0.7,
1574 },
1575 },
1576 };
1577
1578 let analyzer = VariableRelationshipAnalyzer::new();
1579 let insights = analyzer.create_memory_insights(&graph);
1580
1581 assert_eq!(insights.total_memory, 1024);
1582 assert!(!insights.lifetime_distribution.is_empty());
1583 assert!(!insights.optimizations.is_empty());
1584 }
1585}