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