memscope_rs/analysis/relation_inference/
graph_builder.rs1use crate::analysis::heap_scanner::HeapScanner;
7use crate::analysis::relation_inference::clone_detector::{detect_clones, CloneConfig};
8use crate::analysis::relation_inference::pointer_scan::{detect_owner, InferenceRecord};
9use crate::analysis::relation_inference::shared_detector::detect_shared;
10use crate::analysis::relation_inference::slice_detector::detect_slice;
11use crate::analysis::relation_inference::{RangeMap, RelationGraph};
12use crate::analysis::unsafe_inference::{
13 MemoryView, OwnedMemoryView, TypeKind, UnsafeInferenceEngine,
14};
15use crate::snapshot::types::ActiveAllocation;
16
17#[derive(Debug, Clone, Default)]
49pub struct GraphBuilderConfig {
50 pub clone_config: CloneConfig,
62}
63
64pub struct RelationGraphBuilder;
84
85impl RelationGraphBuilder {
86 pub fn build(
97 allocations: &[ActiveAllocation],
98 config: Option<GraphBuilderConfig>,
99 ) -> RelationGraph {
100 let config = config.unwrap_or_default();
101
102 if allocations.is_empty() {
103 return RelationGraph::new();
104 }
105
106 let scan_results = HeapScanner::scan(allocations);
108
109 let records: Vec<InferenceRecord> = scan_results
111 .into_iter()
112 .enumerate()
113 .map(|(id, scan)| {
114 let (type_kind, confidence) = if let Some(ref memory) = scan.memory {
115 let view = MemoryView::new(memory);
116 let guess = UnsafeInferenceEngine::infer_single(&view, scan.size);
117 (guess.kind, guess.confidence)
118 } else {
119 (TypeKind::Unknown, 0)
120 };
121
122 InferenceRecord {
123 id,
124 ptr: scan.ptr,
125 size: scan.size,
126 memory: scan.memory.map(OwnedMemoryView::new),
127 type_kind,
128 confidence,
129 call_stack_hash: allocations[id].call_stack_hash,
130 alloc_time: allocations[id].allocated_at,
131 }
132 })
133 .collect();
134
135 let range_map = RangeMap::new(allocations);
137
138 let mut graph = RelationGraph::new();
140
141 for record in &records {
143 let edges = detect_owner(record, &range_map);
144 graph.add_edges(edges);
145 }
146
147 let slice_edges = detect_slice(&records, allocations, &range_map);
149 graph.add_edges(slice_edges);
150
151 let clone_edges = detect_clones(&records, &config.clone_config);
153 graph.add_edges(clone_edges);
154
155 let shared_edges = detect_shared(&records, &graph.edges);
157 graph.add_edges(shared_edges);
158
159 graph
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use crate::analysis::relation_inference::Relation;
167
168 fn make_alloc(ptr: usize, size: usize) -> ActiveAllocation {
169 ActiveAllocation {
170 ptr,
171 size,
172 allocated_at: 1000,
173 var_name: None,
174 type_name: None,
175 thread_id: 0,
176 call_stack_hash: None,
177 }
178 }
179
180 #[test]
181 fn test_build_empty() {
182 let graph = RelationGraphBuilder::build(&[], None);
183 assert_eq!(graph.edge_count(), 0);
184 }
185
186 #[test]
187 fn test_build_basic_owner_relationship() {
188 let buf1 = [0u8; 24];
190 let buf2 = vec![0u8; 1024];
191 let ptr1 = buf1.as_ptr() as usize;
192 let ptr2 = buf2.as_ptr() as usize;
193
194 let allocs = vec![make_alloc(ptr1, 24), make_alloc(ptr2, 1024)];
195 let graph = RelationGraphBuilder::build(&allocs, None);
196
197 assert_eq!(graph.edge_count(), 0);
200 }
201
202 #[test]
203 fn test_build_with_real_vec_metadata() {
204 let inner = vec![42u8; 256];
206 let ptr = inner.as_ptr() as usize;
207 let len = inner.len();
208 let cap = inner.capacity();
209
210 let mut metadata = [0u8; 24];
211 metadata[0..8].copy_from_slice(&ptr.to_le_bytes());
212 metadata[8..16].copy_from_slice(&len.to_le_bytes());
213 metadata[16..24].copy_from_slice(&cap.to_le_bytes());
214
215 let meta_ptr = metadata.as_ptr() as usize;
216 let inner_ptr = inner.as_ptr() as usize;
217
218 let allocs = vec![make_alloc(meta_ptr, 24), make_alloc(inner_ptr, 256)];
219 let graph = RelationGraphBuilder::build(&allocs, None);
220
221 assert!(graph.edge_count() <= 2);
224 }
225
226 #[test]
227 fn test_build_single_allocation() {
228 let allocs = vec![make_alloc(0x1000, 64)];
229 let graph = RelationGraphBuilder::build(&allocs, None);
230
231 assert_eq!(graph.edge_count(), 0);
233 }
234
235 #[test]
236 fn test_builder_with_default_config() {
237 let allocs = vec![make_alloc(0x1000, 64)];
238 let graph = RelationGraphBuilder::build(&allocs, Some(GraphBuilderConfig::default()));
239 assert_eq!(graph.edge_count(), 0);
240 }
241
242 #[test]
243 fn test_graph_node_count() {
244 let mut graph = RelationGraph::new();
245 graph.add_edge(0, 1, Relation::Owner);
246 graph.add_edge(1, 2, Relation::Slice);
247
248 let nodes = graph.all_nodes();
249 assert_eq!(nodes, vec![0, 1, 2]);
250 }
251}