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 {
119 use crate::snapshot::types::ActiveAllocation;
120
121 let allocations: Vec<ActiveAllocation> =
122 view.allocations().iter().map(|a| (*a).clone()).collect();
123
124 RelationGraphBuilder::build(&allocations, None)
125 }
126
127 pub fn add_edge(&mut self, from: usize, to: usize, relation: Relation) {
131 if from == to {
133 return;
134 }
135 let exists = self
137 .edges
138 .iter()
139 .any(|e| e.from == from && e.to == to && e.relation == relation);
140 if !exists {
141 self.edges.push(RelationEdge { from, to, relation });
142 }
143 }
144
145 pub fn add_edges(&mut self, edges: Vec<RelationEdge>) {
147 for edge in edges {
148 self.add_edge(edge.from, edge.to, edge.relation);
149 }
150 }
151
152 pub fn get_inbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
154 self.edges.iter().filter(|e| e.to == node).collect()
155 }
156
157 pub fn get_outbound_edges(&self, node: usize) -> Vec<&RelationEdge> {
159 self.edges.iter().filter(|e| e.from == node).collect()
160 }
161
162 pub fn edge_count(&self) -> usize {
164 self.edges.len()
165 }
166
167 pub fn all_nodes(&self) -> Vec<usize> {
169 let mut nodes: Vec<usize> = self.edges.iter().flat_map(|e| [e.from, e.to]).collect();
170 nodes.sort();
171 nodes.dedup();
172 nodes
173 }
174
175 pub fn detect_cycles(&self) -> Vec<(usize, usize, Relation)> {
180 if self.edges.is_empty() {
181 return Vec::new();
182 }
183
184 use crate::analysis::relationship_cycle_detector::detect_cycles_direct;
185
186 let relationships: Vec<(usize, usize)> =
187 self.edges.iter().map(|e| (e.from, e.to)).collect();
188
189 let cycle_indices = detect_cycles_direct(&relationships);
190
191 cycle_indices
192 .into_iter()
193 .filter_map(|(from_idx, to_idx)| {
194 self.edges.iter().find_map(|e| {
195 if e.from == from_idx && e.to == to_idx {
196 Some((e.from, e.to, e.relation.clone()))
197 } else {
198 None
199 }
200 })
201 })
202 .collect()
203 }
204}
205
206mod tests {
207 #[allow(unused_imports)]
208 use crate::{Relation, RelationEdge, RelationGraph};
209
210 #[test]
211 fn test_graph_add_edge() {
212 let mut graph = RelationGraph::new();
213
214 graph.add_edge(0, 1, Relation::Owns);
215
216 assert_eq!(graph.edge_count(), 1);
217
218 assert_eq!(graph.edges[0].from, 0);
219
220 assert_eq!(graph.edges[0].to, 1);
221
222 assert_eq!(graph.edges[0].relation, Relation::Owns);
223 }
224
225 #[test]
226
227 fn test_graph_no_self_edges() {
228 let mut graph = RelationGraph::new();
229
230 graph.add_edge(0, 0, Relation::Owns);
231
232 assert_eq!(graph.edge_count(), 0);
233 }
234
235 #[test]
236
237 fn test_graph_no_duplicate_edges() {
238 let mut graph = RelationGraph::new();
239
240 graph.add_edge(0, 1, Relation::Owns);
241
242 graph.add_edge(0, 1, Relation::Owns);
243
244 assert_eq!(graph.edge_count(), 1);
245 }
246
247 #[test]
248
249 fn test_graph_different_relations_allowed() {
250 let mut graph = RelationGraph::new();
251
252 graph.add_edge(0, 1, Relation::Owns);
253
254 graph.add_edge(0, 1, Relation::Clone);
255
256 assert_eq!(graph.edge_count(), 2);
257 }
258
259 #[test]
260
261 fn test_graph_inbound_outbound_edges() {
262 let mut graph = RelationGraph::new();
263
264 graph.add_edge(0, 2, Relation::Owns);
265
266 graph.add_edge(1, 2, Relation::Owns);
267
268 graph.add_edge(2, 3, Relation::Slice);
269
270 let inbound_to_2 = graph.get_inbound_edges(2);
271
272 assert_eq!(inbound_to_2.len(), 2);
273
274 let outbound_from_2 = graph.get_outbound_edges(2);
275
276 assert_eq!(outbound_from_2.len(), 1);
277 }
278
279 #[test]
280
281 fn test_graph_all_nodes() {
282 let mut graph = RelationGraph::new();
283
284 graph.add_edge(3, 1, Relation::Owns);
285
286 graph.add_edge(1, 2, Relation::Slice);
287
288 let nodes = graph.all_nodes();
289
290 assert_eq!(nodes, vec![1, 2, 3]);
291 }
292
293 #[test]
294
295 fn test_graph_add_edges_batch() {
296 let mut graph = RelationGraph::new();
297
298 graph.add_edges(vec![
299 RelationEdge {
300 from: 0,
301
302 to: 1,
303
304 relation: Relation::Owns,
305 },
306 RelationEdge {
307 from: 1,
308
309 to: 2,
310
311 relation: Relation::Slice,
312 },
313 ]);
314
315 assert_eq!(graph.edge_count(), 2);
316 }
317
318 #[test]
319
320 fn test_graph_detect_cycles_none() {
321 let mut graph = RelationGraph::new();
324
325 graph.add_edge(0, 1, Relation::Owns);
326
327 graph.add_edge(1, 2, Relation::Slice);
328
329 let cycles = graph.detect_cycles();
330
331 assert!(cycles.is_empty());
332 }
333
334 #[test]
335
336 fn test_graph_detect_cycles_simple() {
337 let mut graph = RelationGraph::new();
340
341 graph.add_edge(0, 1, Relation::Owns);
342
343 graph.add_edge(1, 0, Relation::Owns);
344
345 let cycles = graph.detect_cycles();
346
347 assert!(!cycles.is_empty());
350
351 let edge_pairs: Vec<_> = cycles.iter().map(|(f, t, _)| (*f, *t)).collect();
354
355 assert!(edge_pairs.contains(&(0, 1)) || edge_pairs.contains(&(1, 0)));
356 }
357
358 #[test]
359
360 fn test_graph_detect_cycles_longer() {
361 let mut graph = RelationGraph::new();
364
365 graph.add_edge(0, 1, Relation::Owns);
366
367 graph.add_edge(1, 2, Relation::Slice);
368
369 graph.add_edge(2, 0, Relation::Clone);
370
371 let cycles = graph.detect_cycles();
372
373 assert!(!cycles.is_empty());
374
375 let has_back_edge = cycles.iter().any(|(f, t, _)| *f == 2 && *t == 0);
378
379 assert!(has_back_edge, "cycle should contain back-edge (2, 0)");
380 }
381
382 #[test]
383
384 fn test_graph_detect_cycles_empty() {
385 let graph = RelationGraph::new();
386
387 let cycles = graph.detect_cycles();
388
389 assert!(cycles.is_empty());
390 }
391
392 #[test]
393
394 fn test_graph_detect_cycles_self_loop_blocked() {
395 let mut graph = RelationGraph::new();
398
399 graph.add_edge(0, 0, Relation::Owns);
400
401 assert_eq!(graph.edge_count(), 0);
402
403 let cycles = graph.detect_cycles();
404
405 assert!(cycles.is_empty());
406 }
407
408 #[test]
409
410 fn test_graph_detect_cycles_diamond() {
411 let mut graph = RelationGraph::new();
416
417 graph.add_edge(0, 1, Relation::Owns);
418
419 graph.add_edge(0, 2, Relation::Owns);
420
421 graph.add_edge(1, 3, Relation::Slice);
422
423 graph.add_edge(2, 3, Relation::Slice);
424
425 graph.add_edge(3, 0, Relation::Clone);
426
427 let cycles = graph.detect_cycles();
428
429 assert!(!cycles.is_empty());
430 }
431}