use crate::analysis::detectors::Detector;
use crate::analysis_engine::{AnalysisEngine, Analyzer};
use crate::capture::{CaptureBackendType, CaptureEngine};
use crate::event_store::EventStore;
use crate::metadata::MetadataEngine;
use crate::query::QueryEngine;
use crate::render_engine::RenderEngine;
use crate::snapshot::SnapshotEngine;
use crate::timeline::TimelineEngine;
use std::sync::{Arc, Mutex};
pub struct MemScope {
pub event_store: Arc<EventStore>,
pub capture: Arc<CaptureEngine>,
pub metadata: Arc<MetadataEngine>,
pub snapshot: Arc<SnapshotEngine>,
pub query: Arc<QueryEngine>,
pub analysis: Arc<Mutex<AnalysisEngine>>,
pub timeline: Arc<TimelineEngine>,
pub render: Arc<RenderEngine>,
}
impl MemScope {
fn to_allocation_info(
active: &crate::snapshot::ActiveAllocation,
) -> crate::capture::types::AllocationInfo {
let thread_id_u64 = active.thread_id;
let thread_id = std::thread::current().id();
let ptr = match active.kind {
crate::core::types::TrackKind::HeapOwner { ptr, .. } => ptr,
crate::core::types::TrackKind::StackOwner { heap_ptr, .. } => heap_ptr,
crate::core::types::TrackKind::Container | crate::core::types::TrackKind::Value => 0,
};
crate::capture::types::AllocationInfo {
ptr,
size: active.size,
var_name: active.var_name.clone(),
type_name: active.type_name.clone(),
scope_name: None,
timestamp_alloc: active.allocated_at,
timestamp_dealloc: None,
thread_id,
thread_id_u64,
borrow_count: 0,
stack_trace: None,
is_leaked: false,
lifetime_ms: None,
module_path: None,
borrow_info: None,
clone_info: None,
ownership_history_available: false,
smart_pointer_info: None,
memory_layout: None,
generic_info: None,
dynamic_type_info: None,
runtime_state: None,
stack_allocation: None,
temporary_object: None,
fragmentation_analysis: None,
generic_instantiation: None,
type_relationships: None,
type_usage: None,
function_call_tracking: None,
lifecycle_tracking: None,
access_tracking: None,
drop_chain_analysis: None,
stack_ptr: active.stack_ptr,
task_id: None,
}
}
pub fn new() -> Self {
let event_store = Arc::new(EventStore::new());
let capture = Arc::new(CaptureEngine::new(
CaptureBackendType::Unified,
event_store.clone(),
));
let metadata = Arc::new(MetadataEngine::new());
let snapshot = Arc::new(SnapshotEngine::new(event_store.clone()));
let query = Arc::new(QueryEngine::new(snapshot.clone()));
let analysis = Arc::new(Mutex::new(AnalysisEngine::new(snapshot.clone())));
let timeline = Arc::new(TimelineEngine::new(event_store.clone()));
let render = Arc::new(RenderEngine::new(snapshot.clone()));
Self {
event_store,
capture,
metadata,
snapshot,
query,
analysis,
timeline,
render,
}
}
pub fn with_backend(backend_type: CaptureBackendType) -> Self {
let event_store = Arc::new(EventStore::new());
let capture = Arc::new(CaptureEngine::new(backend_type, event_store.clone()));
let metadata = Arc::new(MetadataEngine::new());
let snapshot = Arc::new(SnapshotEngine::new(event_store.clone()));
let query = Arc::new(QueryEngine::new(snapshot.clone()));
let analysis = Arc::new(Mutex::new(AnalysisEngine::new(snapshot.clone())));
let timeline = Arc::new(TimelineEngine::new(event_store.clone()));
let render = Arc::new(RenderEngine::new(snapshot.clone()));
Self {
event_store,
capture,
metadata,
snapshot,
query,
analysis,
timeline,
render,
}
}
pub fn register_analyzer(&self, analyzer: Box<dyn Analyzer>) {
if let Ok(mut analysis) = self.analysis.lock() {
analysis.register_analyzer(analyzer);
tracing::info!("Analyzer registered successfully");
} else {
tracing::error!("Failed to acquire analysis engine lock for registration");
}
}
pub fn summary(&self) -> crate::query::QueryResult {
self.query.summary()
}
pub fn top_allocations(&self, limit: usize) -> crate::query::QueryResult {
self.query.top_allocations(limit)
}
pub fn render_json(&self, verbose: bool) -> Result<crate::render_engine::RenderResult, String> {
let snapshot = self.snapshot.build_snapshot();
self.render.render_json(&snapshot, verbose)
}
pub fn clear(&self) {
self.event_store.clear();
}
pub fn event_count(&self) -> usize {
self.event_store.len()
}
pub fn register_detector<D>(&self, detector: D)
where
D: crate::analysis::detectors::Detector + Send + Sync + 'static,
{
use crate::analysis_engine::DetectorToAnalyzer;
let adapter = Box::new(DetectorToAnalyzer::new(detector));
self.register_analyzer(adapter);
}
pub fn run_detectors(&self) -> Vec<crate::analysis_engine::analyzer::AnalysisResult> {
if let Ok(analysis) = self.analysis.lock() {
analysis.analyze()
} else {
tracing::error!("Failed to acquire analysis engine lock");
vec![]
}
}
pub fn run_leak_detector(&self) -> crate::analysis::detectors::DetectionResult {
use crate::analysis::detectors::{LeakDetector, LeakDetectorConfig};
let detector = LeakDetector::new(LeakDetectorConfig::default());
let snapshot = self.snapshot.build_snapshot();
let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
.active_allocations
.values()
.map(Self::to_allocation_info)
.collect();
detector.detect(&allocations)
}
pub fn run_uaf_detector(&self) -> crate::analysis::detectors::DetectionResult {
use crate::analysis::detectors::{UafDetector, UafDetectorConfig};
let detector = UafDetector::new(UafDetectorConfig::default());
let snapshot = self.snapshot.build_snapshot();
let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
.active_allocations
.values()
.map(Self::to_allocation_info)
.collect();
detector.detect(&allocations)
}
pub fn run_overflow_detector(&self) -> crate::analysis::detectors::DetectionResult {
use crate::analysis::detectors::{OverflowDetector, OverflowDetectorConfig};
let detector = OverflowDetector::new(OverflowDetectorConfig::default());
let snapshot = self.snapshot.build_snapshot();
let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
.active_allocations
.values()
.map(Self::to_allocation_info)
.collect();
detector.detect(&allocations)
}
pub fn run_safety_detector(&self) -> crate::analysis::detectors::DetectionResult {
use crate::analysis::detectors::{SafetyDetector, SafetyDetectorConfig};
let detector = SafetyDetector::new(SafetyDetectorConfig::default());
let snapshot = self.snapshot.build_snapshot();
let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
.active_allocations
.values()
.map(Self::to_allocation_info)
.collect();
detector.detect(&allocations)
}
pub fn run_lifecycle_detector(&self) -> crate::analysis::detectors::DetectionResult {
use crate::analysis::detectors::{LifecycleDetector, LifecycleDetectorConfig};
let detector = LifecycleDetector::new(LifecycleDetectorConfig::default());
let snapshot = self.snapshot.build_snapshot();
let allocations: Vec<crate::capture::types::AllocationInfo> = snapshot
.active_allocations
.values()
.map(Self::to_allocation_info)
.collect();
detector.detect(&allocations)
}
pub fn export_html_with_template<P: AsRef<std::path::Path>>(
&self,
path: P,
template: crate::render_engine::export::DashboardTemplate,
) -> Result<(), String> {
use crate::render_engine::export::export_dashboard_html_with_template;
use crate::tracker::Tracker;
use std::sync::Arc;
let tracker = Tracker::new();
let passport_tracker =
Arc::new(crate::analysis::memory_passport_tracker::get_global_passport_tracker());
export_dashboard_html_with_template(path, &tracker, &passport_tracker, template, None)
.map_err(|e| format!("Failed to export HTML: {}", e))
}
pub fn export_html<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
use crate::render_engine::export::DashboardTemplate;
let _snapshot = self.snapshot.build_snapshot();
self.export_html_with_template(path, DashboardTemplate::Unified)
}
pub fn export_json<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), String> {
use crate::render_engine::export::export_all_json;
use crate::tracker::Tracker;
use std::sync::Arc;
let tracker = Tracker::new();
let passport_tracker =
Arc::new(crate::analysis::memory_passport_tracker::get_global_passport_tracker());
let async_tracker = Arc::new(crate::capture::backends::async_tracker::AsyncTracker::new());
export_all_json(path, &tracker, &passport_tracker, &async_tracker)
.map_err(|e| format!("Failed to export JSON files: {}", e))
}
}
impl Default for MemScope {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memscope_creation() {
let memscope = MemScope::new();
assert_eq!(memscope.event_count(), 0);
}
#[test]
fn test_memscope_default() {
let memscope = MemScope::default();
assert_eq!(memscope.event_count(), 0);
}
#[test]
fn test_memscope_with_backend() {
let memscope = MemScope::with_backend(CaptureBackendType::Core);
assert_eq!(memscope.event_count(), 0);
}
#[test]
fn test_summary() {
let memscope = MemScope::new();
let result = memscope.summary();
match result {
crate::query::QueryResult::Summary(_) => (),
_ => panic!("Expected summary result"),
}
}
#[test]
fn test_top_allocations() {
let memscope = MemScope::new();
let result = memscope.top_allocations(10);
match result {
crate::query::QueryResult::Allocations(_) => (),
_ => panic!("Expected allocations result"),
}
}
#[test]
fn test_render_json() {
let memscope = MemScope::new();
let result = memscope.render_json(false);
assert!(result.is_ok());
}
#[test]
fn test_clear() {
let memscope = MemScope::new();
memscope.capture.capture_alloc(0x1000, 1024, 1);
assert_eq!(memscope.event_count(), 1);
memscope.clear();
assert_eq!(memscope.event_count(), 0);
}
}