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