memscope_rs/analysis_engine/
detector_adapter.rs1use crate::analysis::detectors::Detector;
7use crate::analysis_engine::analyzer::{AnalysisResult, Analyzer, Finding, Severity};
8use crate::capture::types::AllocationInfo;
9use crate::snapshot::ActiveAllocation;
10use std::sync::Arc;
11
12fn active_to_allocation_info(active: &ActiveAllocation) -> AllocationInfo {
14 let thread_id_u64 = active.thread_id;
15 let thread_id = std::thread::current().id();
20
21 let ptr = match active.kind {
25 crate::core::types::TrackKind::HeapOwner { ptr, .. } => ptr,
26 crate::core::types::TrackKind::StackOwner { heap_ptr, .. } => heap_ptr,
27 crate::core::types::TrackKind::Container | crate::core::types::TrackKind::Value => 0,
28 };
29
30 AllocationInfo {
31 ptr,
32 size: active.size,
33 var_name: active.var_name.clone(),
34 type_name: active.type_name.clone(),
35 scope_name: None,
36 timestamp_alloc: active.allocated_at,
37 timestamp_dealloc: None,
38 thread_id,
39 thread_id_u64,
40 borrow_count: 0,
41 stack_trace: None,
42 is_leaked: false,
43 lifetime_ms: None,
44 module_path: None,
45 borrow_info: None,
46 clone_info: None,
47 ownership_history_available: false,
48 smart_pointer_info: None,
49 memory_layout: None,
50 generic_info: None,
51 dynamic_type_info: None,
52 runtime_state: None,
53 stack_allocation: None,
54 temporary_object: None,
55 fragmentation_analysis: None,
56 generic_instantiation: None,
57 type_relationships: None,
58 type_usage: None,
59 function_call_tracking: None,
60 lifecycle_tracking: None,
61 access_tracking: None,
62 drop_chain_analysis: None,
63 stack_ptr: active.stack_ptr,
64 task_id: None,
65 generation_id: 0,
66 }
67}
68
69pub struct DetectorToAnalyzer<D: Detector + Send + Sync> {
74 detector: Arc<D>,
75}
76
77impl<D: Detector + Send + Sync> DetectorToAnalyzer<D> {
78 pub fn new(detector: D) -> Self {
80 Self {
81 detector: Arc::new(detector),
82 }
83 }
84
85 pub fn from_boxed(detector: Box<D>) -> Self {
87 Self {
88 detector: Arc::from(detector),
89 }
90 }
91
92 pub fn detector(&self) -> &D {
94 &self.detector
95 }
96}
97
98impl<D: Detector + Send + Sync + 'static> Analyzer for DetectorToAnalyzer<D> {
99 fn name(&self) -> &str {
100 self.detector.name()
101 }
102
103 fn analyze(&self, snapshot: &crate::snapshot::MemorySnapshot) -> AnalysisResult {
104 let allocations: Vec<AllocationInfo> = snapshot
106 .active_allocations
107 .values()
108 .map(active_to_allocation_info)
109 .collect();
110
111 let detection_result = self.detector.detect(&allocations);
113
114 let findings: Vec<Finding> = detection_result
116 .issues
117 .into_iter()
118 .map(|issue| Finding {
119 issue_type: format!("{:?}", issue.category),
120 description: issue.description,
121 ptr: issue.allocation_ptr,
122 size: None, context: issue.suggested_fix.unwrap_or_default(),
124 })
125 .collect();
126
127 let severity = findings
129 .iter()
130 .map(|f| &f.issue_type)
131 .fold(Severity::Info, |acc, _| acc);
132
133 AnalysisResult {
134 analyzer_name: detection_result.detector_name,
135 issue_count: findings.len(),
136 severity,
137 description: format!(
138 "Found {} issues in {} allocations (took {}ms)",
139 findings.len(),
140 detection_result.statistics.total_allocations,
141 detection_result.detection_time_ms
142 ),
143 findings,
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::analysis::detectors::{LeakDetector, LeakDetectorConfig};
152 use crate::snapshot::MemorySnapshot;
153
154 #[test]
155 fn test_detector_adapter_creation() {
156 let detector = LeakDetector::new(LeakDetectorConfig::default());
157 let adapter = DetectorToAnalyzer::new(detector);
158 assert_eq!(adapter.name(), "LeakDetector");
159 }
160
161 #[test]
162 fn test_detector_adapter_analyze() {
163 let detector = LeakDetector::new(LeakDetectorConfig::default());
164 let adapter = DetectorToAnalyzer::new(detector);
165
166 let snapshot = MemorySnapshot::new();
167 let result = adapter.analyze(&snapshot);
168
169 assert_eq!(result.analyzer_name, "LeakDetector");
170 assert_eq!(result.issue_count, 0);
171 }
172
173 #[test]
174 fn test_active_to_allocation_info() {
175 let active = ActiveAllocation {
176 ptr: Some(0x1000),
177 kind: crate::core::types::TrackKind::HeapOwner {
178 ptr: 0x1000,
179 size: 1024,
180 },
181 size: 1024,
182 allocated_at: 1000,
183 var_name: Some("test_var".to_string()),
184 type_name: Some("Vec<u8>".to_string()),
185 thread_id: 42,
186 call_stack_hash: None,
187 module_path: None,
188 stack_ptr: None,
189 };
190
191 let alloc = active_to_allocation_info(&active);
192
193 assert_eq!(alloc.ptr, 0x1000);
194 assert_eq!(alloc.size, 1024);
195 assert_eq!(alloc.timestamp_alloc, 1000);
196 assert_eq!(alloc.var_name, Some("test_var".to_string()));
197 assert_eq!(alloc.type_name, Some("Vec<u8>".to_string()));
198 assert_eq!(alloc.borrow_count, 0);
200 assert!(!alloc.is_leaked);
201 }
202}