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