Skip to main content

memscope_rs/facade/
implementation.rs

1//! MemScope - Unified facade for all engines
2//!
3//! This module provides the MemScope facade which unifies all engines
4//! into a simple, easy-to-use interface.
5
6use crate::analysis::detectors::Detector;
7use crate::analysis_engine::{AnalysisEngine, Analyzer};
8use crate::capture::{CaptureBackendType, CaptureEngine};
9use crate::event_store::EventStore;
10use crate::metadata::MetadataEngine;
11use crate::query::QueryEngine;
12use crate::render_engine::RenderEngine;
13use crate::snapshot::SnapshotEngine;
14use crate::timeline::TimelineEngine;
15use std::sync::{Arc, Mutex};
16
17/// MemScope - Unified facade for all engines
18///
19/// MemScope provides a simple, unified interface for memory tracking,
20/// analysis, and visualization. It integrates all 9 engines into a
21/// single, easy-to-use API.
22///
23/// Key properties:
24/// - Simple: One-stop interface for all functionality
25/// - Powerful: Access to all engines when needed
26/// - Type-safe: Strong typing throughout
27/// - Thread-safe: All operations are thread-safe
28pub struct MemScope {
29    /// Event Store - Centralized event storage
30    pub event_store: Arc<EventStore>,
31    /// Capture Engine - Event capture backend
32    pub capture: Arc<CaptureEngine>,
33    /// Metadata Engine - Centralized metadata management
34    pub metadata: Arc<MetadataEngine>,
35    /// Snapshot Engine - Snapshot construction and aggregation
36    pub snapshot: Arc<SnapshotEngine>,
37    /// Query Engine - Unified query interface
38    pub query: Arc<QueryEngine>,
39    /// Analysis Engine - Memory analysis logic (wrapped in Mutex for interior mutability)
40    pub analysis: Arc<Mutex<AnalysisEngine>>,
41    /// Timeline Engine - Time-based memory analysis
42    pub timeline: Arc<TimelineEngine>,
43    /// Render Engine - Output rendering
44    pub render: Arc<RenderEngine>,
45}
46
47impl MemScope {
48    /// Convert ActiveAllocation to AllocationInfo (helper function)
49    fn to_allocation_info(
50        active: &crate::snapshot::ActiveAllocation,
51    ) -> crate::capture::types::AllocationInfo {
52        let thread_id_u64 = active.thread_id;
53        // Note: Rust's ThreadId type cannot be reconstructed from u64.
54        // We use the current thread's ID as a placeholder.
55        // The actual thread ID value is available in thread_id_u64 field.
56        // This is a known limitation that requires tracking ThreadId at allocation time.
57        let thread_id = std::thread::current().id();
58
59        // For Container/Value types, ptr is None (no real heap allocation).
60        // We use 0 as a sentinel value, which indicates "not a real pointer".
61        // Callers should check TrackKind to determine if this is a real allocation.
62        let ptr = match active.kind {
63            crate::core::types::TrackKind::HeapOwner { ptr, .. } => ptr,
64            crate::core::types::TrackKind::Container | crate::core::types::TrackKind::Value => 0,
65        };
66
67        crate::capture::types::AllocationInfo {
68            ptr,
69            size: active.size,
70            var_name: active.var_name.clone(),
71            type_name: active.type_name.clone(),
72            scope_name: None,
73            timestamp_alloc: active.allocated_at,
74            timestamp_dealloc: None,
75            thread_id,
76            thread_id_u64,
77            borrow_count: 0,
78            stack_trace: None,
79            is_leaked: false,
80            lifetime_ms: None,
81            borrow_info: None,
82            clone_info: None,
83            ownership_history_available: false,
84            smart_pointer_info: None,
85            memory_layout: None,
86            generic_info: None,
87            dynamic_type_info: None,
88            runtime_state: None,
89            stack_allocation: None,
90            temporary_object: None,
91            fragmentation_analysis: None,
92            generic_instantiation: None,
93            type_relationships: None,
94            type_usage: None,
95            function_call_tracking: None,
96            lifecycle_tracking: None,
97            access_tracking: None,
98            drop_chain_analysis: None,
99        }
100    }
101
102    /// Create a new MemScope instance
103    ///
104    /// This creates all engines with default settings and connects them
105    /// together in the correct configuration.
106    pub fn new() -> Self {
107        // Create EventStore (the foundation)
108        let event_store = Arc::new(EventStore::new());
109
110        // Create Capture Engine
111        let capture = Arc::new(CaptureEngine::new(
112            CaptureBackendType::Unified,
113            event_store.clone(),
114        ));
115
116        // Create Metadata Engine
117        let metadata = Arc::new(MetadataEngine::new());
118
119        // Create Snapshot Engine
120        let snapshot = Arc::new(SnapshotEngine::new(event_store.clone()));
121
122        // Create Query Engine
123        let query = Arc::new(QueryEngine::new(snapshot.clone()));
124
125        // Create Analysis Engine
126        let analysis = Arc::new(Mutex::new(AnalysisEngine::new(snapshot.clone())));
127
128        // Create Timeline Engine
129        let timeline = Arc::new(TimelineEngine::new(event_store.clone()));
130
131        // Create Render Engine
132        let render = Arc::new(RenderEngine::new(snapshot.clone()));
133
134        Self {
135            event_store,
136            capture,
137            metadata,
138            snapshot,
139            query,
140            analysis,
141            timeline,
142            render,
143        }
144    }
145
146    /// Create a new MemScope instance with a specific capture backend
147    ///
148    /// # Arguments
149    /// * `backend_type` - The type of capture backend to use
150    pub fn with_backend(backend_type: CaptureBackendType) -> Self {
151        // Create EventStore (the foundation)
152        let event_store = Arc::new(EventStore::new());
153
154        // Create Capture Engine with specified backend
155        let capture = Arc::new(CaptureEngine::new(backend_type, event_store.clone()));
156
157        // Create Metadata Engine
158        let metadata = Arc::new(MetadataEngine::new());
159
160        // Create Snapshot Engine
161        let snapshot = Arc::new(SnapshotEngine::new(event_store.clone()));
162
163        // Create Query Engine
164        let query = Arc::new(QueryEngine::new(snapshot.clone()));
165
166        // Create Analysis Engine
167        let analysis = Arc::new(Mutex::new(AnalysisEngine::new(snapshot.clone())));
168
169        // Create Timeline Engine
170        let timeline = Arc::new(TimelineEngine::new(event_store.clone()));
171
172        // Create Render Engine
173        let render = Arc::new(RenderEngine::new(snapshot.clone()));
174
175        Self {
176            event_store,
177            capture,
178            metadata,
179            snapshot,
180            query,
181            analysis,
182            timeline,
183            render,
184        }
185    }
186
187    /// Register an analyzer with the analysis engine
188    ///
189    /// # Arguments
190    /// * `analyzer` - The analyzer to register
191    pub fn register_analyzer(&self, analyzer: Box<dyn Analyzer>) {
192        if let Ok(mut analysis) = self.analysis.lock() {
193            analysis.register_analyzer(analyzer);
194            tracing::info!("Analyzer registered successfully");
195        } else {
196            tracing::error!("Failed to acquire analysis engine lock for registration");
197        }
198    }
199
200    /// Get a summary of current memory usage
201    pub fn summary(&self) -> crate::query::QueryResult {
202        self.query.summary()
203    }
204
205    /// Get top allocations by size
206    ///
207    /// # Arguments
208    /// * `limit` - Maximum number of allocations to return
209    pub fn top_allocations(&self, limit: usize) -> crate::query::QueryResult {
210        self.query.top_allocations(limit)
211    }
212
213    /// Render current snapshot to JSON
214    ///
215    /// # Arguments
216    /// * `verbose` - Whether to include verbose output
217    pub fn render_json(&self, verbose: bool) -> Result<crate::render_engine::RenderResult, String> {
218        let snapshot = self.snapshot.build_snapshot();
219        self.render.render_json(&snapshot, verbose)
220    }
221
222    /// Clear all events and reset state
223    pub fn clear(&self) {
224        self.event_store.clear();
225    }
226
227    /// Get the total number of events
228    pub fn event_count(&self) -> usize {
229        self.event_store.len()
230    }
231
232    // ===== Detector Methods =====
233
234    /// Register a detector with the analysis engine
235    ///
236    /// This method automatically wraps the detector in an adapter
237    /// and registers it as an analyzer.
238    ///
239    /// # Arguments
240    /// * `detector` - The detector to register
241    pub fn register_detector<D>(&self, detector: D)
242    where
243        D: crate::analysis::detectors::Detector + Send + Sync + 'static,
244    {
245        use crate::analysis_engine::DetectorToAnalyzer;
246        let adapter = Box::new(DetectorToAnalyzer::new(detector));
247        self.register_analyzer(adapter);
248    }
249
250    /// Run all registered detectors and return the results
251    ///
252    /// # Returns
253    /// A vector of analysis results from all detectors
254    pub fn run_detectors(&self) -> Vec<crate::analysis_engine::analyzer::AnalysisResult> {
255        if let Ok(analysis) = self.analysis.lock() {
256            analysis.analyze()
257        } else {
258            tracing::error!("Failed to acquire analysis engine lock");
259            vec![]
260        }
261    }
262
263    /// Run the leak detector on the current snapshot
264    ///
265    /// # Returns
266    /// Detection results from the leak detector
267    pub fn run_leak_detector(&self) -> crate::analysis::detectors::DetectionResult {
268        use crate::analysis::detectors::{LeakDetector, LeakDetectorConfig};
269        let detector = LeakDetector::new(LeakDetectorConfig::default());
270        let snapshot = self.snapshot.build_snapshot();
271        let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
272            .active_allocations
273            .values()
274            .map(Self::to_allocation_info)
275            .collect();
276        detector.detect(&allocations)
277    }
278
279    /// Run the UAF (Use-After-Free) detector on the current snapshot
280    ///
281    /// # Returns
282    /// Detection results from the UAF detector
283    pub fn run_uaf_detector(&self) -> crate::analysis::detectors::DetectionResult {
284        use crate::analysis::detectors::{UafDetector, UafDetectorConfig};
285        let detector = UafDetector::new(UafDetectorConfig::default());
286        let snapshot = self.snapshot.build_snapshot();
287        let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
288            .active_allocations
289            .values()
290            .map(Self::to_allocation_info)
291            .collect();
292        detector.detect(&allocations)
293    }
294
295    /// Run the overflow detector on the current snapshot
296    ///
297    /// # Returns
298    /// Detection results from the overflow detector
299    pub fn run_overflow_detector(&self) -> crate::analysis::detectors::DetectionResult {
300        use crate::analysis::detectors::{OverflowDetector, OverflowDetectorConfig};
301        let detector = OverflowDetector::new(OverflowDetectorConfig::default());
302        let snapshot = self.snapshot.build_snapshot();
303        let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
304            .active_allocations
305            .values()
306            .map(Self::to_allocation_info)
307            .collect();
308        detector.detect(&allocations)
309    }
310
311    /// Run the safety detector on the current snapshot
312    ///
313    /// # Returns
314    /// Detection results from the safety detector
315    pub fn run_safety_detector(&self) -> crate::analysis::detectors::DetectionResult {
316        use crate::analysis::detectors::{SafetyDetector, SafetyDetectorConfig};
317        let detector = SafetyDetector::new(SafetyDetectorConfig::default());
318        let snapshot = self.snapshot.build_snapshot();
319        let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
320            .active_allocations
321            .values()
322            .map(Self::to_allocation_info)
323            .collect();
324        detector.detect(&allocations)
325    }
326
327    /// Run the lifecycle detector on the current snapshot
328    ///
329    /// # Returns
330    /// Detection results from the lifecycle detector
331    pub fn run_lifecycle_detector(&self) -> crate::analysis::detectors::DetectionResult {
332        use crate::analysis::detectors::{LifecycleDetector, LifecycleDetectorConfig};
333        let detector = LifecycleDetector::new(LifecycleDetectorConfig::default());
334        let snapshot = self.snapshot.build_snapshot();
335        let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
336            .active_allocations
337            .values()
338            .map(Self::to_allocation_info)
339            .collect();
340        detector.detect(&allocations)
341    }
342
343    // ===== Export Methods =====
344
345    /// Export the current memory snapshot as an HTML dashboard with a specific template
346    ///
347    /// # Arguments
348    /// * `path` - Directory path where the HTML file will be saved
349    /// * `template` - The dashboard template to use
350    ///
351    /// # Returns
352    /// Result indicating success or failure
353    pub fn export_html_with_template<P: AsRef<std::path::Path>>(
354        &self,
355        path: P,
356        template: crate::render_engine::export::DashboardTemplate,
357    ) -> Result<(), String> {
358        use crate::render_engine::export::export_dashboard_html_with_template;
359        use crate::tracker::Tracker;
360        use std::sync::Arc;
361
362        // Create a new tracker instance for export
363        let tracker = Tracker::new();
364        let passport_tracker =
365            Arc::new(crate::analysis::memory_passport_tracker::get_global_passport_tracker());
366
367        export_dashboard_html_with_template(path, &tracker, &passport_tracker, template, None)
368            .map_err(|e| format!("Failed to export HTML: {}", e))
369    }
370
371    /// Export the current memory snapshot as an HTML dashboard
372    ///
373    /// This method automatically detects program characteristics and selects
374    /// the most appropriate template:
375    /// - Multithread: If the program uses multiple threads
376    /// - Binary: Default template for single-threaded programs
377    ///
378    /// # Arguments
379    /// * `path` - Directory path where the HTML file will be saved
380    ///
381    /// # Returns
382    /// Result indicating success or failure
383    pub fn export_html<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
384        use crate::render_engine::export::DashboardTemplate;
385
386        let _snapshot = self.snapshot.build_snapshot();
387        self.export_html_with_template(path, DashboardTemplate::Unified)
388    }
389
390    /// Export all JSON files
391    ///
392    /// This method exports 9 JSON files containing comprehensive memory analysis:
393    /// - memory_analysis.json: Complete memory allocation analysis
394    /// - lifetime.json: Ownership and lifetime tracking
395    /// - ownership_graph.json: Ownership graph analysis with cycle detection
396    /// - system_resources.json: System resource monitoring
397    /// - thread_analysis.json: Thread-specific memory stats
398    /// - unsafe_ffi.json: Unsafe FFI boundary tracking
399    /// - memory_passports.json: Memory lifecycle passports
400    /// - leak_detection.json: Potential memory leaks
401    /// - async_analysis.json: Async task analysis
402    ///
403    /// # Arguments
404    /// * `path` - Directory path where JSON files will be saved
405    ///
406    /// # Returns
407    /// Result indicating success or failure
408    pub fn export_json<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
409        use crate::render_engine::export::export_all_json;
410        use crate::tracker::Tracker;
411        use std::sync::Arc;
412
413        let tracker = Tracker::new();
414        let passport_tracker =
415            Arc::new(crate::analysis::memory_passport_tracker::get_global_passport_tracker());
416        let async_tracker = Arc::new(crate::capture::backends::async_tracker::AsyncTracker::new());
417
418        export_all_json(path, &tracker, &passport_tracker, &async_tracker)
419            .map_err(|e| format!("Failed to export JSON files: {}", e))
420    }
421}
422
423impl Default for MemScope {
424    fn default() -> Self {
425        Self::new()
426    }
427}
428
429#[cfg(test)]
430mod tests {
431    use super::*;
432
433    #[test]
434    fn test_memscope_creation() {
435        let memscope = MemScope::new();
436        assert_eq!(memscope.event_count(), 0);
437    }
438
439    #[test]
440    fn test_memscope_default() {
441        let memscope = MemScope::default();
442        assert_eq!(memscope.event_count(), 0);
443    }
444
445    #[test]
446    fn test_memscope_with_backend() {
447        let memscope = MemScope::with_backend(CaptureBackendType::Core);
448        assert_eq!(memscope.event_count(), 0);
449    }
450
451    #[test]
452    fn test_summary() {
453        let memscope = MemScope::new();
454        let result = memscope.summary();
455        match result {
456            crate::query::QueryResult::Summary(_) => (),
457            _ => panic!("Expected summary result"),
458        }
459    }
460
461    #[test]
462    fn test_top_allocations() {
463        let memscope = MemScope::new();
464        let result = memscope.top_allocations(10);
465        match result {
466            crate::query::QueryResult::Allocations(_) => (),
467            _ => panic!("Expected allocations result"),
468        }
469    }
470
471    #[test]
472    fn test_render_json() {
473        let memscope = MemScope::new();
474        let result = memscope.render_json(false);
475        assert!(result.is_ok());
476    }
477
478    #[test]
479    fn test_clear() {
480        let memscope = MemScope::new();
481        // Simulate some events
482        memscope.capture.capture_alloc(0x1000, 1024, 1);
483        assert_eq!(memscope.event_count(), 1);
484
485        memscope.clear();
486        assert_eq!(memscope.event_count(), 0);
487    }
488}