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 ArcClone,
65 RcClone,
67 ImmutableBorrow,
69 MutableBorrow,
71}
72
73impl std::fmt::Display for Relation {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 match self {
76 Relation::Owns => write!(f, "Owns"),
77 Relation::Contains => write!(f, "Contains"),
78 Relation::Shares => write!(f, "Shares"),
79 Relation::Slice => write!(f, "Slice"),
80 Relation::Clone => write!(f, "Clone"),
81 Relation::Evolution => write!(f, "Evolution"),
82 Relation::ArcClone => write!(f, "ArcClone"),
83 Relation::RcClone => write!(f, "RcClone"),
84 Relation::ImmutableBorrow => write!(f, "ImmutableBorrow"),
85 Relation::MutableBorrow => write!(f, "MutableBorrow"),
86 }
87 }
88}
89
90#[derive(Debug, Clone)]
92pub struct RelationEdge {
93 pub from: usize,
95 pub to: usize,
97 pub relation: Relation,
99}
100
101#[derive(Debug, Default, Clone)]
103pub struct RelationGraph {
104 pub edges: Vec<RelationEdge>,
106}
107
108impl RelationGraph {
109 pub fn new() -> Self {
111 Self::default()
112 }
113
114 pub fn from_view(view: &crate::view::MemoryView) -> Self {
120 use crate::snapshot::types::ActiveAllocation;
121
122 let allocations: Vec<ActiveAllocation> =
123 view.allocations().iter().map(|a| (*a).clone()).collect();
124
125 RelationGraphBuilder::from_observations(&allocations)
126 }
127
128 pub fn add_edge(&mut self, from: usize, to: usize, relation: Relation) {
132 if from == to {
134 return;
135 }
136 let exists = self
138 .edges
139 .iter()
140 .any(|e| e.from == from && e.to == to && e.relation == relation);
141 if !exists {
142 self.edges.push(RelationEdge { from, to, relation });
143 }
144 }
145
146 pub fn add_edges(&mut self, edges: Vec<RelationEdge>) {
148 for edge in edges {
149 self.add_edge(edge.from, edge.to, edge.relation);
150 }
151 }
152
153 pub fn get_inbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
155 self.edges.iter().filter(|e| e.to == node).collect()
156 }
157
158 pub fn get_outbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
160 self.edges.iter().filter(|e| e.from == node).collect()
161 }
162
163 pub fn edge_count(&self) -> usize {
165 self.edges.len()
166 }
167
168 pub fn all_nodes(&self) -> Vec<usize> {
170 let mut nodes: Vec<usize> = self.edges.iter().flat_map(|e| [e.from, e.to]).collect();
171 nodes.sort();
172 nodes.dedup();
173 nodes
174 }
175
176 pub fn detect_cycles(&self) -> Vec<(usize, usize, Relation)> {
181 if self.edges.is_empty() {
182 return Vec::new();
183 }
184
185 use crate::analysis::relationship_cycle_detector::detect_cycles_direct;
186
187 let relationships: Vec<(usize, usize)> =
188 self.edges.iter().map(|e| (e.from, e.to)).collect();
189
190 let cycle_indices = detect_cycles_direct(&relationships);
191
192 cycle_indices
193 .into_iter()
194 .filter_map(|(from_idx, to_idx)| {
195 self.edges.iter().find_map(|e| {
196 if e.from == from_idx && e.to == to_idx {
197 Some((e.from, e.to, e.relation.clone()))
198 } else {
199 None
200 }
201 })
202 })
203 .collect()
204 }
205}
206
207mod tests {
208 #[allow(unused_imports)]
209 use crate::{Relation, RelationEdge, RelationGraph};
210
211 #[test]
212 fn test_graph_add_edge() {
213 let mut graph = RelationGraph::new();
214
215 graph.add_edge(0, 1, Relation::Owns);
216
217 assert_eq!(graph.edge_count(), 1);
218
219 assert_eq!(graph.edges[0].from, 0);
220
221 assert_eq!(graph.edges[0].to, 1);
222
223 assert_eq!(graph.edges[0].relation, Relation::Owns);
224 }
225
226 #[test]
227
228 fn test_graph_no_self_edges() {
229 let mut graph = RelationGraph::new();
230
231 graph.add_edge(0, 0, Relation::Owns);
232
233 assert_eq!(graph.edge_count(), 0);
234 }
235
236 #[test]
237
238 fn test_graph_no_duplicate_edges() {
239 let mut graph = RelationGraph::new();
240
241 graph.add_edge(0, 1, Relation::Owns);
242
243 graph.add_edge(0, 1, Relation::Owns);
244
245 assert_eq!(graph.edge_count(), 1);
246 }
247
248 #[test]
249
250 fn test_graph_different_relations_allowed() {
251 let mut graph = RelationGraph::new();
252
253 graph.add_edge(0, 1, Relation::Owns);
254
255 graph.add_edge(0, 1, Relation::Clone);
256
257 assert_eq!(graph.edge_count(), 2);
258 }
259
260 #[test]
261
262 fn test_graph_inbound_outbound_edges() {
263 let mut graph = RelationGraph::new();
264
265 graph.add_edge(0, 2, Relation::Owns);
266
267 graph.add_edge(1, 2, Relation::Owns);
268
269 graph.add_edge(2, 3, Relation::Slice);
270
271 let inbound_to_2 = graph.get_inbound_edges(2);
272
273 assert_eq!(inbound_to_2.len(), 2);
274
275 let outbound_from_2 = graph.get_outbound_edges(2);
276
277 assert_eq!(outbound_from_2.len(), 1);
278 }
279
280 #[test]
281
282 fn test_graph_all_nodes() {
283 let mut graph = RelationGraph::new();
284
285 graph.add_edge(3, 1, Relation::Owns);
286
287 graph.add_edge(1, 2, Relation::Slice);
288
289 let nodes = graph.all_nodes();
290
291 assert_eq!(nodes, vec![1, 2, 3]);
292 }
293
294 #[test]
295
296 fn test_graph_add_edges_batch() {
297 let mut graph = RelationGraph::new();
298
299 graph.add_edges(vec![
300 RelationEdge {
301 from: 0,
302
303 to: 1,
304
305 relation: Relation::Owns,
306 },
307 RelationEdge {
308 from: 1,
309
310 to: 2,
311
312 relation: Relation::Slice,
313 },
314 ]);
315
316 assert_eq!(graph.edge_count(), 2);
317 }
318
319 #[test]
320
321 fn test_graph_detect_cycles_none() {
322 let mut graph = RelationGraph::new();
325
326 graph.add_edge(0, 1, Relation::Owns);
327
328 graph.add_edge(1, 2, Relation::Slice);
329
330 let cycles = graph.detect_cycles();
331
332 assert!(cycles.is_empty());
333 }
334
335 #[test]
336
337 fn test_graph_detect_cycles_simple() {
338 let mut graph = RelationGraph::new();
341
342 graph.add_edge(0, 1, Relation::Owns);
343
344 graph.add_edge(1, 0, Relation::Owns);
345
346 let cycles = graph.detect_cycles();
347
348 assert!(!cycles.is_empty());
351
352 let edge_pairs: Vec<_> = cycles.iter().map(|(f, t, _)| (*f, *t)).collect();
355
356 assert!(edge_pairs.contains(&(0, 1)) || edge_pairs.contains(&(1, 0)));
357 }
358
359 #[test]
360
361 fn test_graph_detect_cycles_longer() {
362 let mut graph = RelationGraph::new();
365
366 graph.add_edge(0, 1, Relation::Owns);
367
368 graph.add_edge(1, 2, Relation::Slice);
369
370 graph.add_edge(2, 0, Relation::Clone);
371
372 let cycles = graph.detect_cycles();
373
374 assert!(!cycles.is_empty());
375
376 let has_back_edge = cycles.iter().any(|(f, t, _)| *f == 2 && *t == 0);
379
380 assert!(has_back_edge, "cycle should contain back-edge (2, 0)");
381 }
382
383 #[test]
384
385 fn test_graph_detect_cycles_empty() {
386 let graph = RelationGraph::new();
387
388 let cycles = graph.detect_cycles();
389
390 assert!(cycles.is_empty());
391 }
392
393 #[test]
394
395 fn test_graph_detect_cycles_self_loop_blocked() {
396 let mut graph = RelationGraph::new();
399
400 graph.add_edge(0, 0, Relation::Owns);
401
402 assert_eq!(graph.edge_count(), 0);
403
404 let cycles = graph.detect_cycles();
405
406 assert!(cycles.is_empty());
407 }
408
409 #[test]
410
411 fn test_graph_detect_cycles_diamond() {
412 let mut graph = RelationGraph::new();
417
418 graph.add_edge(0, 1, Relation::Owns);
419
420 graph.add_edge(0, 2, Relation::Owns);
421
422 graph.add_edge(1, 3, Relation::Slice);
423
424 graph.add_edge(2, 3, Relation::Slice);
425
426 graph.add_edge(3, 0, Relation::Clone);
427
428 let cycles = graph.detect_cycles();
429
430 assert!(!cycles.is_empty());
431 }
432}