memscope_rs/analysis/relation_inference/
mod.rs1mod clone_detector;
33mod graph_builder;
34mod pointer_scan;
35mod range_map;
36mod shared_detector;
37mod slice_detector;
38
39pub use clone_detector::{detect_clones, CloneConfig};
40pub use graph_builder::{GraphBuilderConfig, RelationGraphBuilder};
41pub use pointer_scan::{detect_owner, InferenceRecord};
42pub use range_map::RangeMap;
43pub use shared_detector::detect_shared;
44pub use slice_detector::detect_slice;
45
46#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub enum Relation {
49 Owner,
51 Slice,
53 Clone,
55 Shared,
57}
58
59impl std::fmt::Display for Relation {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 Relation::Owner => write!(f, "Owner"),
63 Relation::Slice => write!(f, "Slice"),
64 Relation::Clone => write!(f, "Clone"),
65 Relation::Shared => write!(f, "Shared"),
66 }
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct RelationEdge {
73 pub from: usize,
75 pub to: usize,
77 pub relation: Relation,
79}
80
81#[derive(Debug, Default, Clone)]
83pub struct RelationGraph {
84 pub edges: Vec<RelationEdge>,
86}
87
88impl RelationGraph {
89 pub fn new() -> Self {
91 Self::default()
92 }
93
94 pub fn add_edge(&mut self, from: usize, to: usize, relation: Relation) {
98 if from == to {
100 return;
101 }
102 let exists = self
104 .edges
105 .iter()
106 .any(|e| e.from == from && e.to == to && e.relation == relation);
107 if !exists {
108 self.edges.push(RelationEdge { from, to, relation });
109 }
110 }
111
112 pub fn add_edges(&mut self, edges: Vec<RelationEdge>) {
114 for edge in edges {
115 self.add_edge(edge.from, edge.to, edge.relation);
116 }
117 }
118
119 pub fn get_inbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
121 self.edges.iter().filter(|e| e.to == node).collect()
122 }
123
124 pub fn get_outbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
126 self.edges.iter().filter(|e| e.from == node).collect()
127 }
128
129 pub fn edge_count(&self) -> usize {
131 self.edges.len()
132 }
133
134 pub fn all_nodes(&self) -> Vec<usize> {
136 let mut nodes: Vec<usize> = self.edges.iter().flat_map(|e| [e.from, e.to]).collect();
137 nodes.sort();
138 nodes.dedup();
139 nodes
140 }
141
142 pub fn detect_cycles(&self) -> Vec<(usize, usize, Relation)> {
147 if self.edges.is_empty() {
148 return Vec::new();
149 }
150
151 use crate::analysis::relationship_cycle_detector::detect_cycles_direct;
152
153 let relationships: Vec<(usize, usize)> =
154 self.edges.iter().map(|e| (e.from, e.to)).collect();
155
156 let cycle_indices = detect_cycles_direct(&relationships);
157
158 cycle_indices
159 .into_iter()
160 .filter_map(|(from_idx, to_idx)| {
161 self.edges.iter().find_map(|e| {
162 if e.from == from_idx && e.to == to_idx {
163 Some((e.from, e.to, e.relation.clone()))
164 } else {
165 None
166 }
167 })
168 })
169 .collect()
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_graph_add_edge() {
179 let mut graph = RelationGraph::new();
180 graph.add_edge(0, 1, Relation::Owner);
181
182 assert_eq!(graph.edge_count(), 1);
183 assert_eq!(graph.edges[0].from, 0);
184 assert_eq!(graph.edges[0].to, 1);
185 assert_eq!(graph.edges[0].relation, Relation::Owner);
186 }
187
188 #[test]
189 fn test_graph_no_self_edges() {
190 let mut graph = RelationGraph::new();
191 graph.add_edge(0, 0, Relation::Owner);
192 assert_eq!(graph.edge_count(), 0);
193 }
194
195 #[test]
196 fn test_graph_no_duplicate_edges() {
197 let mut graph = RelationGraph::new();
198 graph.add_edge(0, 1, Relation::Owner);
199 graph.add_edge(0, 1, Relation::Owner);
200 assert_eq!(graph.edge_count(), 1);
201 }
202
203 #[test]
204 fn test_graph_different_relations_allowed() {
205 let mut graph = RelationGraph::new();
206 graph.add_edge(0, 1, Relation::Owner);
207 graph.add_edge(0, 1, Relation::Clone);
208 assert_eq!(graph.edge_count(), 2);
209 }
210
211 #[test]
212 fn test_graph_inbound_outbound_edges() {
213 let mut graph = RelationGraph::new();
214 graph.add_edge(0, 2, Relation::Owner);
215 graph.add_edge(1, 2, Relation::Owner);
216 graph.add_edge(2, 3, Relation::Slice);
217
218 let inbound_to_2 = graph.get_inbound_edges(2);
219 assert_eq!(inbound_to_2.len(), 2);
220
221 let outbound_from_2 = graph.get_outbound_edges(2);
222 assert_eq!(outbound_from_2.len(), 1);
223 }
224
225 #[test]
226 fn test_graph_all_nodes() {
227 let mut graph = RelationGraph::new();
228 graph.add_edge(3, 1, Relation::Owner);
229 graph.add_edge(1, 2, Relation::Slice);
230
231 let nodes = graph.all_nodes();
232 assert_eq!(nodes, vec![1, 2, 3]);
233 }
234
235 #[test]
236 fn test_graph_add_edges_batch() {
237 let mut graph = RelationGraph::new();
238 graph.add_edges(vec![
239 RelationEdge {
240 from: 0,
241 to: 1,
242 relation: Relation::Owner,
243 },
244 RelationEdge {
245 from: 1,
246 to: 2,
247 relation: Relation::Slice,
248 },
249 ]);
250 assert_eq!(graph.edge_count(), 2);
251 }
252
253 #[test]
254 fn test_graph_detect_cycles_none() {
255 let mut graph = RelationGraph::new();
257 graph.add_edge(0, 1, Relation::Owner);
258 graph.add_edge(1, 2, Relation::Slice);
259
260 let cycles = graph.detect_cycles();
261 assert!(cycles.is_empty());
262 }
263
264 #[test]
265 fn test_graph_detect_cycles_simple() {
266 let mut graph = RelationGraph::new();
268 graph.add_edge(0, 1, Relation::Owner);
269 graph.add_edge(1, 0, Relation::Owner);
270
271 let cycles = graph.detect_cycles();
272 assert!(!cycles.is_empty());
274 let edge_pairs: Vec<_> = cycles.iter().map(|(f, t, _)| (*f, *t)).collect();
276 assert!(edge_pairs.contains(&(0, 1)) || edge_pairs.contains(&(1, 0)));
277 }
278
279 #[test]
280 fn test_graph_detect_cycles_longer() {
281 let mut graph = RelationGraph::new();
283 graph.add_edge(0, 1, Relation::Owner);
284 graph.add_edge(1, 2, Relation::Slice);
285 graph.add_edge(2, 0, Relation::Clone);
286
287 let cycles = graph.detect_cycles();
288 assert!(!cycles.is_empty());
289 let has_back_edge = cycles.iter().any(|(f, t, _)| *f == 2 && *t == 0);
291 assert!(has_back_edge, "cycle should contain back-edge (2, 0)");
292 }
293
294 #[test]
295 fn test_graph_detect_cycles_empty() {
296 let graph = RelationGraph::new();
297 let cycles = graph.detect_cycles();
298 assert!(cycles.is_empty());
299 }
300
301 #[test]
302 fn test_graph_detect_cycles_self_loop_blocked() {
303 let mut graph = RelationGraph::new();
305 graph.add_edge(0, 0, Relation::Owner);
306 assert_eq!(graph.edge_count(), 0);
307
308 let cycles = graph.detect_cycles();
309 assert!(cycles.is_empty());
310 }
311
312 #[test]
313 fn test_graph_detect_cycles_diamond() {
314 let mut graph = RelationGraph::new();
317 graph.add_edge(0, 1, Relation::Owner);
318 graph.add_edge(0, 2, Relation::Owner);
319 graph.add_edge(1, 3, Relation::Slice);
320 graph.add_edge(2, 3, Relation::Slice);
321 graph.add_edge(3, 0, Relation::Clone);
322
323 let cycles = graph.detect_cycles();
324 assert!(!cycles.is_empty());
325 }
326}