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