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 }
66}
67
68pub struct DetectorToAnalyzer<D: Detector + Send + Sync> {
73 detector: Arc<D>,
74}
75
76impl<D: Detector + Send + Sync> DetectorToAnalyzer<D> {
77 pub fn new(detector: D) -> Self {
79 Self {
80 detector: Arc::new(detector),
81 }
82 }
83
84 pub fn from_boxed(detector: Box<D>) -> Self {
86 Self {
87 detector: Arc::from(detector),
88 }
89 }
90
91 pub fn detector(&self) -> &D {
93 &self.detector
94 }
95}
96
97impl<D: Detector + Send + Sync + 'static> Analyzer for DetectorToAnalyzer<D> {
98 fn name(&self) -> &str {
99 self.detector.name()
100 }
101
102 fn analyze(&self, snapshot: &crate::snapshot::MemorySnapshot) -> AnalysisResult {
103 let allocations: Vec<AllocationInfo> = snapshot
105 .active_allocations
106 .values()
107 .map(active_to_allocation_info)
108 .collect();
109
110 let detection_result = self.detector.detect(&allocations);
112
113 let findings: Vec<Finding> = detection_result
115 .issues
116 .into_iter()
117 .map(|issue| Finding {
118 issue_type: format!("{:?}", issue.category),
119 description: issue.description,
120 ptr: issue.allocation_ptr,
121 size: None, context: issue.suggested_fix.unwrap_or_default(),
123 })
124 .collect();
125
126 let severity = findings
128 .iter()
129 .map(|f| &f.issue_type)
130 .fold(Severity::Info, |acc, _| acc);
131
132 AnalysisResult {
133 analyzer_name: detection_result.detector_name,
134 issue_count: findings.len(),
135 severity,
136 description: format!(
137 "Found {} issues in {} allocations (took {}ms)",
138 findings.len(),
139 detection_result.statistics.total_allocations,
140 detection_result.detection_time_ms
141 ),
142 findings,
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::analysis::detectors::{LeakDetector, LeakDetectorConfig};
151 use crate::snapshot::MemorySnapshot;
152
153 #[test]
154 fn test_detector_adapter_creation() {
155 let detector = LeakDetector::new(LeakDetectorConfig::default());
156 let adapter = DetectorToAnalyzer::new(detector);
157 assert_eq!(adapter.name(), "LeakDetector");
158 }
159
160 #[test]
161 fn test_detector_adapter_analyze() {
162 let detector = LeakDetector::new(LeakDetectorConfig::default());
163 let adapter = DetectorToAnalyzer::new(detector);
164
165 let snapshot = MemorySnapshot::new();
166 let result = adapter.analyze(&snapshot);
167
168 assert_eq!(result.analyzer_name, "LeakDetector");
169 assert_eq!(result.issue_count, 0);
170 }
171
172 #[test]
173 fn test_active_to_allocation_info() {
174 let active = ActiveAllocation {
175 ptr: Some(0x1000),
176 kind: crate::core::types::TrackKind::HeapOwner {
177 ptr: 0x1000,
178 size: 1024,
179 },
180 size: 1024,
181 allocated_at: 1000,
182 var_name: Some("test_var".to_string()),
183 type_name: Some("Vec<u8>".to_string()),
184 thread_id: 42,
185 call_stack_hash: None,
186 module_path: None,
187 stack_ptr: None,
188 };
189
190 let alloc = active_to_allocation_info(&active);
191
192 assert_eq!(alloc.ptr, 0x1000);
193 assert_eq!(alloc.size, 1024);
194 assert_eq!(alloc.timestamp_alloc, 1000);
195 assert_eq!(alloc.var_name, Some("test_var".to_string()));
196 assert_eq!(alloc.type_name, Some("Vec<u8>".to_string()));
197 assert_eq!(alloc.borrow_count, 0);
199 assert!(!alloc.is_leaked);
200 }
201}