memscope_rs/analyzer/
core.rs1use crate::analyzer::report::{
4 AnalysisReport, CycleReport, LeakReport, MemoryStatsReport, MetricsReport,
5};
6use crate::view::MemoryView;
7use tracing::{debug, info};
8
9use super::{DetectionAnalysis, ExportEngine, GraphAnalysis, MetricsAnalysis, TimelineAnalysis};
10
11pub struct Analyzer {
16 view: MemoryView,
17 graph: Option<GraphAnalysis>,
18 detect: Option<DetectionAnalysis>,
19 metrics: Option<MetricsAnalysis>,
20 timeline: Option<TimelineAnalysis>,
21}
22
23impl Analyzer {
24 pub fn from_tracker(
26 tracker: &crate::capture::backends::global_tracking::GlobalTracker,
27 ) -> Self {
28 info!("Creating Analyzer from GlobalTracker");
29 Self::from_view(MemoryView::from_tracker(tracker))
30 }
31
32 pub fn from_view(view: MemoryView) -> Self {
34 let alloc_count = view.len();
35 info!("Creating Analyzer with {} allocations", alloc_count);
36 Self {
37 view,
38 graph: None,
39 detect: None,
40 metrics: None,
41 timeline: None,
42 }
43 }
44
45 pub fn graph(&mut self) -> &mut GraphAnalysis {
47 if self.graph.is_none() {
48 debug!("Initializing GraphAnalysis (lazy)");
49 self.graph = Some(GraphAnalysis::from_view(&self.view));
50 }
51 self.graph
52 .as_mut()
53 .expect("GraphAnalysis should be initialized after lazy initialization")
54 }
55
56 pub fn detect(&mut self) -> &DetectionAnalysis {
58 if self.detect.is_none() {
59 debug!("Initializing DetectionAnalysis (lazy)");
60 self.detect = Some(DetectionAnalysis::from_view(&self.view));
61 }
62 self.detect
63 .as_ref()
64 .expect("DetectionAnalysis should be initialized after lazy initialization")
65 }
66
67 pub fn metrics(&mut self) -> &MetricsAnalysis {
69 if self.metrics.is_none() {
70 debug!("Initializing MetricsAnalysis (lazy)");
71 self.metrics = Some(MetricsAnalysis::from_view(&self.view));
72 }
73 self.metrics
74 .as_ref()
75 .expect("MetricsAnalysis should be initialized after lazy initialization")
76 }
77
78 pub fn timeline(&mut self) -> &TimelineAnalysis {
80 if self.timeline.is_none() {
81 debug!("Initializing TimelineAnalysis (lazy)");
82 self.timeline = Some(TimelineAnalysis::from_view(&self.view));
83 }
84 self.timeline
85 .as_ref()
86 .expect("TimelineAnalysis should be initialized after lazy initialization")
87 }
88
89 pub fn export(&self) -> ExportEngine<'_> {
91 ExportEngine::new(&self.view)
92 }
93
94 pub fn view(&self) -> &MemoryView {
96 &self.view
97 }
98
99 pub fn analyze(&mut self) -> AnalysisReport {
152 info!("Starting full analysis");
153 let stats = MemoryStatsReport {
154 allocation_count: self.view.len(),
155 total_bytes: self.view.total_memory(),
156 peak_bytes: self.view.snapshot().stats.peak_memory,
157 thread_count: self.view.snapshot().thread_stats.len(),
158 };
159 debug!(
160 "Stats: {} allocations, {} bytes, {} threads",
161 stats.allocation_count, stats.total_bytes, stats.thread_count
162 );
163
164 let leaks = self.detect().leaks();
165 if leaks.leak_count > 0 {
166 info!(
167 "Leak detection found {} leaks ({} bytes)",
168 leaks.leak_count, leaks.total_leaked_bytes
169 );
170 }
171
172 let cycles = self.graph().cycles();
173 if cycles.cycle_count > 0 {
174 info!("Cycle detection found {} cycles", cycles.cycle_count);
175 }
176
177 let metrics = self.metrics().summary();
178 info!("Analysis complete");
179
180 AnalysisReport {
181 stats,
182 leaks,
183 cycles,
184 metrics,
185 }
186 }
187
188 pub fn quick_leak_check(&mut self) -> LeakReport {
190 self.detect().leaks()
191 }
192
193 pub fn quick_cycle_check(&mut self) -> CycleReport {
195 self.graph().cycles()
196 }
197
198 pub fn quick_metrics(&mut self) -> MetricsReport {
200 self.metrics().summary()
201 }
202}
203
204impl Clone for Analyzer {
205 fn clone(&self) -> Self {
206 Self {
207 view: self.view.clone(),
208 graph: None,
209 detect: None,
210 metrics: None,
211 timeline: None,
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::event_store::MemoryEvent;
220
221 #[test]
222 fn test_analyzer_from_events() {
223 let events = vec![
224 MemoryEvent::allocate(0x1000, 64, 1),
225 MemoryEvent::allocate(0x2000, 128, 2),
226 ];
227 let view = MemoryView::from_events(events);
228 let mut analyzer = Analyzer::from_view(view);
229 let leaks = analyzer.quick_leak_check();
231 assert_eq!(leaks.leak_count, 2);
232 let metrics = analyzer.quick_metrics();
234 assert_eq!(metrics.allocation_count, 2);
235 }
236
237 #[test]
238 fn test_quick_leak_check() {
239 let events = vec![MemoryEvent::allocate(0x1000, 64, 1)];
240 let view = MemoryView::from_events(events);
241 let mut analyzer = Analyzer::from_view(view);
242 let leaks = analyzer.quick_leak_check();
243 assert_eq!(leaks.leak_count, 1);
244 }
245}