Skip to main content

memscope_rs/analyzer/
core.rs

1//! Analyzer - Unified analysis entry point.
2
3use 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
11/// Unified analysis entry point.
12///
13/// Provides access to all analysis modules through a single interface.
14/// Uses lazy initialization for expensive operations.
15pub 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    /// Create analyzer from GlobalTracker.
25    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    /// Create analyzer from view.
33    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    /// Get graph analysis (lazy).
46    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    /// Get detection analysis (lazy).
57    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    /// Get metrics analysis (lazy).
68    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    /// Get timeline analysis (lazy).
79    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    /// Get export engine.
90    pub fn export(&self) -> ExportEngine<'_> {
91        ExportEngine::new(&self.view)
92    }
93
94    /// Get underlying view.
95    pub fn view(&self) -> &MemoryView {
96        &self.view
97    }
98
99    /// Run full analysis on the memory data.
100    ///
101    /// This method performs a comprehensive analysis of all tracked memory
102    /// allocations and returns a complete report.
103    ///
104    /// # Analysis Pipeline
105    ///
106    /// The analysis is performed in the following order:
107    ///
108    /// 1. **Statistics Collection** (O(1))
109    ///    - Total allocation count
110    ///    - Total bytes allocated
111    ///    - Peak memory usage
112    ///    - Thread count
113    ///
114    /// 2. **Leak Detection** (O(n))
115    ///    - Identifies allocations that were never deallocated
116    ///    - Reports leaked bytes and allocation details
117    ///
118    /// 3. **Cycle Detection** (O(V + E))
119    ///    - Uses DFS-based algorithm to detect reference cycles
120    ///    - Groups allocations by type for potential cycle identification
121    ///
122    /// 4. **Metrics Summary** (O(n))
123    ///    - Aggregates allocation statistics by type
124    ///    - Reports top allocations by size
125    ///
126    /// # Performance Characteristics
127    ///
128    /// - **Time Complexity**: O(n + V + E) where n is the number of allocations,
129    ///   V is the number of unique pointers, and E is the number of edges
130    /// - **Space Complexity**: O(n) for storing analysis results
131    /// - **Memory Overhead**: Minimal, results are computed on-demand
132    ///
133    /// # Example
134    ///
135    /// ```ignore
136    /// let mut analyzer = analyzer(&tracker)?;
137    /// let report = analyzer.analyze();
138    ///
139    /// println!("Allocations: {}", report.stats.allocation_count);
140    /// println!("Leaks: {}", report.leaks.leak_count);
141    /// println!("Cycles: {}", report.cycles.cycle_count);
142    /// ```
143    ///
144    /// # Returns
145    ///
146    /// An `AnalysisReport` containing:
147    /// - `stats`: Basic memory statistics
148    /// - `leaks`: Memory leak detection results
149    /// - `cycles`: Reference cycle detection results
150    /// - `metrics`: Aggregated metrics summary
151    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    /// Quick leak check.
189    pub fn quick_leak_check(&mut self) -> LeakReport {
190        self.detect().leaks()
191    }
192
193    /// Quick cycle check.
194    pub fn quick_cycle_check(&mut self) -> CycleReport {
195        self.graph().cycles()
196    }
197
198    /// Quick metrics.
199    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        // Only test detection (no heap scanning required)
230        let leaks = analyzer.quick_leak_check();
231        assert_eq!(leaks.leak_count, 2);
232        // Test metrics (no heap scanning required)
233        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}