memscope_rs/analysis/relation_inference/
mod.rs1mod clone_detector;
33mod container_detector;
34mod graph_builder;
35mod pointer_scan;
36mod range_map;
37mod shared_detector;
38mod slice_detector;
39
40pub use clone_detector::{detect_clones, CloneConfig};
41pub use container_detector::{detect_containers, ContainerConfig};
42pub use graph_builder::{GraphBuilderConfig, RelationGraphBuilder};
43pub use pointer_scan::{detect_owner, InferenceRecord};
44pub use range_map::RangeMap;
45pub use shared_detector::detect_shared;
46pub use slice_detector::detect_slice;
47
48#[derive(Debug, Clone, PartialEq, Eq, Hash)]
50pub enum Relation {
51 Owns,
53 Contains,
55 Shares,
57 Slice,
59 Clone,
61 Evolution,
63}
64
65impl std::fmt::Display for Relation {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match self {
68 Relation::Owns => write!(f, "Owns"),
69 Relation::Contains => write!(f, "Contains"),
70 Relation::Shares => write!(f, "Shares"),
71 Relation::Slice => write!(f, "Slice"),
72 Relation::Clone => write!(f, "Clone"),
73 Relation::Evolution => write!(f, "Evolution"),
74 }
75 }
76}
77
78#[derive(Debug, Clone)]
80pub struct RelationEdge {
81 pub from: usize,
83 pub to: usize,
85 pub relation: Relation,
87}
88
89#[derive(Debug, Default, Clone)]
91pub struct RelationGraph {
92 pub edges: Vec<RelationEdge>,
94}
95
96impl RelationGraph {
97 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn from_view(view: &crate::view::MemoryView) -> Self {
107 use crate::snapshot::types::ActiveAllocation;
108
109 let allocations: Vec<ActiveAllocation> =
110 view.allocations().iter().map(|a| (*a).clone()).collect();
111
112 RelationGraphBuilder::build(&allocations, None)
113 }
114
115 pub fn add_edge(&mut self, from: usize, to: usize, relation: Relation) {
119 if from == to {
121 return;
122 }
123 let exists = self
125 .edges
126 .iter()
127 .any(|e| e.from == from && e.to == to && e.relation == relation);
128 if !exists {
129 self.edges.push(RelationEdge { from, to, relation });
130 }
131 }
132
133 pub fn add_edges(&mut self, edges: Vec<RelationEdge>) {
135 for edge in edges {
136 self.add_edge(edge.from, edge.to, edge.relation);
137 }
138 }
139
140 pub fn get_inbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
142 self.edges.iter().filter(|e| e.to == node).collect()
143 }
144
145 pub fn get_outbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
147 self.edges.iter().filter(|e| e.from == node).collect()
148 }
149
150 pub fn edge_count(&self) -> usize {
152 self.edges.len()
153 }
154
155 pub fn all_nodes(&self) -> Vec<usize> {
157 let mut nodes: Vec<usize> = self.edges.iter().flat_map(|e| [e.from, e.to]).collect();
158 nodes.sort();
159 nodes.dedup();
160 nodes
161 }
162
163 pub fn detect_cycles(&self) -> Vec<(usize, usize, Relation)> {
168 if self.edges.is_empty() {
169 return Vec::new();
170 }
171
172 use crate::analysis::relationship_cycle_detector::detect_cycles_direct;
173
174 let relationships: Vec<(usize, usize)> =
175 self.edges.iter().map(|e| (e.from, e.to)).collect();
176
177 let cycle_indices = detect_cycles_direct(&relationships);
178
179 cycle_indices
180 .into_iter()
181 .filter_map(|(from_idx, to_idx)| {
182 self.edges.iter().find_map(|e| {
183 if e.from == from_idx && e.to == to_idx {
184 Some((e.from, e.to, e.relation.clone()))
185 } else {
186 None
187 }
188 })
189 })
190 .collect()
191 }
192}
193
194mod tests {
195 #[allow(unused_imports)]
196 use crate::{Relation, RelationEdge, RelationGraph};
197
198 #[test]
199 fn test_graph_add_edge() {
200 let mut graph = RelationGraph::new();
201
202 graph.add_edge(0, 1, Relation::Owns);
203
204 assert_eq!(graph.edge_count(), 1);
205
206 assert_eq!(graph.edges[0].from, 0);
207
208 assert_eq!(graph.edges[0].to, 1);
209
210 assert_eq!(graph.edges[0].relation, Relation::Owns);
211 }
212
213 #[test]
214
215 fn test_graph_no_self_edges() {
216 let mut graph = RelationGraph::new();
217
218 graph.add_edge(0, 0, Relation::Owns);
219
220 assert_eq!(graph.edge_count(), 0);
221 }
222
223 #[test]
224
225 fn test_graph_no_duplicate_edges() {
226 let mut graph = RelationGraph::new();
227
228 graph.add_edge(0, 1, Relation::Owns);
229
230 graph.add_edge(0, 1, Relation::Owns);
231
232 assert_eq!(graph.edge_count(), 1);
233 }
234
235 #[test]
236
237 fn test_graph_different_relations_allowed() {
238 let mut graph = RelationGraph::new();
239
240 graph.add_edge(0, 1, Relation::Owns);
241
242 graph.add_edge(0, 1, Relation::Clone);
243
244 assert_eq!(graph.edge_count(), 2);
245 }
246
247 #[test]
248
249 fn test_graph_inbound_outbound_edges() {
250 let mut graph = RelationGraph::new();
251
252 graph.add_edge(0, 2, Relation::Owns);
253
254 graph.add_edge(1, 2, Relation::Owns);
255
256 graph.add_edge(2, 3, Relation::Slice);
257
258 let inbound_to_2 = graph.get_inbound_edges(2);
259
260 assert_eq!(inbound_to_2.len(), 2);
261
262 let outbound_from_2 = graph.get_outbound_edges(2);
263
264 assert_eq!(outbound_from_2.len(), 1);
265 }
266
267 #[test]
268
269 fn test_graph_all_nodes() {
270 let mut graph = RelationGraph::new();
271
272 graph.add_edge(3, 1, Relation::Owns);
273
274 graph.add_edge(1, 2, Relation::Slice);
275
276 let nodes = graph.all_nodes();
277
278 assert_eq!(nodes, vec![1, 2, 3]);
279 }
280
281 #[test]
282
283 fn test_graph_add_edges_batch() {
284 let mut graph = RelationGraph::new();
285
286 graph.add_edges(vec![
287 RelationEdge {
288 from: 0,
289
290 to: 1,
291
292 relation: Relation::Owns,
293 },
294 RelationEdge {
295 from: 1,
296
297 to: 2,
298
299 relation: Relation::Slice,
300 },
301 ]);
302
303 assert_eq!(graph.edge_count(), 2);
304 }
305
306 #[test]
307
308 fn test_graph_detect_cycles_none() {
309 let mut graph = RelationGraph::new();
312
313 graph.add_edge(0, 1, Relation::Owns);
314
315 graph.add_edge(1, 2, Relation::Slice);
316
317 let cycles = graph.detect_cycles();
318
319 assert!(cycles.is_empty());
320 }
321
322 #[test]
323
324 fn test_graph_detect_cycles_simple() {
325 let mut graph = RelationGraph::new();
328
329 graph.add_edge(0, 1, Relation::Owns);
330
331 graph.add_edge(1, 0, Relation::Owns);
332
333 let cycles = graph.detect_cycles();
334
335 assert!(!cycles.is_empty());
338
339 let edge_pairs: Vec<_> = cycles.iter().map(|(f, t, _)| (*f, *t)).collect();
342
343 assert!(edge_pairs.contains(&(0, 1)) || edge_pairs.contains(&(1, 0)));
344 }
345
346 #[test]
347
348 fn test_graph_detect_cycles_longer() {
349 let mut graph = RelationGraph::new();
352
353 graph.add_edge(0, 1, Relation::Owns);
354
355 graph.add_edge(1, 2, Relation::Slice);
356
357 graph.add_edge(2, 0, Relation::Clone);
358
359 let cycles = graph.detect_cycles();
360
361 assert!(!cycles.is_empty());
362
363 let has_back_edge = cycles.iter().any(|(f, t, _)| *f == 2 && *t == 0);
366
367 assert!(has_back_edge, "cycle should contain back-edge (2, 0)");
368 }
369
370 #[test]
371
372 fn test_graph_detect_cycles_empty() {
373 let graph = RelationGraph::new();
374
375 let cycles = graph.detect_cycles();
376
377 assert!(cycles.is_empty());
378 }
379
380 #[test]
381
382 fn test_graph_detect_cycles_self_loop_blocked() {
383 let mut graph = RelationGraph::new();
386
387 graph.add_edge(0, 0, Relation::Owns);
388
389 assert_eq!(graph.edge_count(), 0);
390
391 let cycles = graph.detect_cycles();
392
393 assert!(cycles.is_empty());
394 }
395
396 #[test]
397
398 fn test_graph_detect_cycles_diamond() {
399 let mut graph = RelationGraph::new();
404
405 graph.add_edge(0, 1, Relation::Owns);
406
407 graph.add_edge(0, 2, Relation::Owns);
408
409 graph.add_edge(1, 3, Relation::Slice);
410
411 graph.add_edge(2, 3, Relation::Slice);
412
413 graph.add_edge(3, 0, Relation::Clone);
414
415 let cycles = graph.detect_cycles();
416
417 assert!(!cycles.is_empty());
418 }
419}