Skip to main content

memscope_rs/
lib.rs

1//! Memory tracking and visualization tools for Rust applications.
2//!
3//! This crate provides tools for tracking memory allocations and visualizing
4//! memory usage in Rust applications. It includes a custom global allocator
5//! that tracks all heap allocations and deallocations, and provides utilities
6//! for exporting memory usage data in various formats.
7
8// Import TrackKind for three-layer object model
9use crate::core::types::TrackKind;
10
11/// Advanced memory analysis functionality
12pub mod analysis;
13/// Analysis Engine - Memory analysis logic
14pub mod analysis_engine;
15/// Core memory tracking functionality
16pub mod core;
17
18pub mod capture;
19/// Event Store Engine - Centralized event storage
20pub mod event_store;
21/// Facade API - Unified user interface
22pub mod facade;
23/// Metadata Engine - Centralized metadata management
24pub mod metadata;
25/// Query Engine - Unified query interface
26pub mod query;
27/// Render Engine - Output rendering
28pub mod render_engine;
29/// Snapshot Engine - Snapshot construction and aggregation
30pub mod snapshot;
31/// Memory management utilities
32pub mod memory {
33    pub use crate::snapshot::memory::*;
34}
35/// Task Registry - Unified task tracking and relationship management
36pub mod task_registry;
37/// Timeline Engine - Time-based memory analysis
38pub mod timeline;
39/// Unified Tracker API - Simple, unified interface for memory tracking
40pub mod tracker;
41
42// Export simplified global tracking API
43pub use capture::backends::global_tracking::{
44    global_tracker, init_global_tracking, init_global_tracking_with_config, is_initialized,
45    GlobalTracker, GlobalTrackerConfig, GlobalTrackerStats, TrackerConfig,
46};
47
48/// Analyzer Module - Unified analysis entry point
49pub mod analyzer;
50/// Unified error handling and recovery system
51pub mod error;
52/// Memory allocation tracking statistics and monitoring
53pub mod tracking;
54/// Utility functions
55pub mod utils;
56/// Variable registry for lightweight HashMap-based variable tracking
57pub mod variable_registry;
58/// View Module - Unified read-only access to memory data
59pub mod view;
60
61/// Ergonomics wrapper — one-call init + simplified export
62pub mod mem_ctx;
63
64/// Auto-export configuration (output path, formats, signal policy, flush interval).
65pub mod auto_export;
66/// RAII guard returned by `start()` / `start_with()`; drops trigger exit export.
67pub mod guard;
68/// Lifecycle hooks: idempotent `export_once` + panic/ctrlc/atexit handlers.
69pub mod lifecycle;
70/// Periodic background flusher (worker thread + graceful shutdown + final flush).
71pub mod periodic_flusher;
72
73/// Re-exports for ergonomic usage
74pub mod prelude {
75    pub use crate::auto_export::{AutoExportConfig, ExportFormatSet, MemScopeConfig, SignalPolicy};
76    pub use crate::guard::{start, start_with, MemScopeGuard};
77    pub use crate::lifecycle::ExportReason;
78    pub use crate::mem_ctx::MemCtx;
79    pub use crate::{track, track_clone, MemScopeResult};
80}
81
82// Re-export the unified one-line start API + on-demand helpers at crate root.
83pub use crate::auto_export::{AutoExportConfig, ExportFormatSet, MemScopeConfig, SignalPolicy};
84pub use crate::guard::{start, start_with, MemScopeGuard};
85pub use crate::lifecycle::{export_for_reason, snapshot_json, trigger_export_now, ExportReason};
86
87/// Initialize logging system for memscope-rs.
88///
89/// This function sets up the tracing subscriber with appropriate filtering
90/// and formatting for memory tracking operations.
91///
92/// # Example
93///
94/// ```ignore
95/// use memscope_rs::init_logging;
96///
97/// init_logging();
98/// // Now logging is configured and ready to use
99/// ```
100///
101/// # Environment Variables
102///
103/// The logging level can be controlled via the `RUST_LOG` environment variable:
104///
105/// - `RUST_LOG=memscope_rs=error` - Only errors
106/// - `RUST_LOG=memscope_rs=warn` - Warnings and errors
107/// - `RUST_LOG=memscope_rs=info` - Info, warnings, and errors (default)
108/// - `RUST_LOG=memscope_rs=debug` - Debug, info, warnings, and errors
109///
110/// # Errors
111///
112/// Returns an error if the log filter directive cannot be parsed.
113///
114/// # Note
115///
116/// This function can be called multiple times safely; subsequent calls will be ignored.
117pub fn init_logging() -> MemScopeResult<()> {
118    use tracing_subscriber::{fmt, EnvFilter};
119
120    static INIT: std::sync::Once = std::sync::Once::new();
121
122    let result = Ok(());
123    INIT.call_once(|| {
124        let filter = EnvFilter::from_default_env()
125            .add_directive(tracing::Level::INFO.into())
126            .add_directive(
127                "memscope_rs=info"
128                    .parse()
129                    .unwrap_or(tracing::Level::INFO.into()),
130            );
131
132        fmt()
133            .with_env_filter(filter)
134            .with_target(true)
135            .with_thread_ids(true)
136            .with_file(true)
137            .with_line_number(true)
138            .with_thread_names(true)
139            .init();
140
141        tracing::info!("memscope-rs logging initialized");
142    });
143    result
144}
145
146pub use analysis::*;
147pub use capture::backends::bottleneck_analysis::{BottleneckKind, PerformanceIssue};
148pub use capture::backends::hotspot_analysis::{CallStackHotspot, MemoryUsagePeak};
149pub use capture::backends::{
150    configure_tracking_strategy, get_tracker as get_capture_tracker, AllocationCategory,
151    AnalysisSummary, AsyncAllocation, AsyncBackend, AsyncMemorySnapshot, AsyncSnapshot, AsyncStats,
152    AsyncTracker, CoreBackend, Event, EventType, FrequencyData, FrequencyPattern, InteractionType,
153    LockfreeAnalysis, LockfreeBackend, RuntimeEnvironment, SamplingConfig, SystemMetrics, TaskInfo,
154    TaskMemoryProfile, ThreadInteraction, ThreadLocalTracker, ThreadStats, TrackedFuture,
155    TrackingStrategy, UnifiedBackend,
156};
157pub use capture::backends::{
158    is_tracking, memory_snapshot, quick_trace, stop_tracing, trace_all, trace_thread,
159};
160pub use capture::types::{AllocationInfo, SmartPointerInfo, TrackingError, TrackingResult};
161pub use capture::{CaptureBackend, CaptureBackendType, CaptureEngine};
162pub use core::allocator::TrackingAllocator;
163pub use core::tracker::{get_tracker, MemoryTracker};
164pub use core::{ExportMode, ExportOptions};
165pub use core::{MemScopeError, MemScopeResult};
166
167// Re-export unified analyzer interface
168pub use analyzer::{
169    AnalysisReport, Analyzer, ClassificationAnalysis, ClassificationSummary, CycleReport,
170    DetectionAnalysis, ExportEngine, GraphAnalysis, LeakReport, MetricsAnalysis, MetricsReport,
171    SafetyAnalysis, SafetySummary, TimelineAnalysis, TypeCategory, TypeClassification,
172};
173pub use view::{FilterBuilder, MemoryView, ViewStats};
174
175/// Create analyzer from GlobalTracker (recommended entry point).
176///
177/// Returns an error if the tracker is not initialized or contains invalid data.
178///
179/// # Errors
180///
181/// Returns `MemScopeError` if:
182/// - The tracker has not been initialized
183/// - The tracker contains corrupted event data
184///
185/// # Example
186///
187/// ```ignore
188/// use memscope_rs::{init_global_tracking, global_tracker, analyzer};
189///
190/// init_global_tracking().unwrap();
191/// let tracker = global_tracker().unwrap();
192///
193/// let mut az = analyzer(&tracker).expect("Failed to create analyzer");
194/// let report = az.analyze();
195/// println!("{}", report.summary());
196/// ```
197pub fn analyzer(tracker: &GlobalTracker) -> MemScopeResult<Analyzer> {
198    // Validate tracker has valid data
199    let events = tracker.tracker().events();
200
201    // Basic validation: check for obviously corrupted data
202    for event in &events {
203        if event.size > isize::MAX as usize {
204            return Err(MemScopeError::new(
205                crate::core::error::ErrorKind::ValidationError,
206                format!(
207                    "Invalid allocation size: {} bytes (exceeds maximum)",
208                    event.size
209                ),
210            ));
211        }
212    }
213
214    Ok(Analyzer::from_tracker(tracker))
215}
216#[cfg(feature = "derive")]
217pub use memscope_derive::Trackable;
218pub use snapshot::engine::SnapshotEngine;
219pub use snapshot::memory::{
220    BoundedHistory, BoundedHistoryConfig, BoundedHistoryStats, MemoryConfig, TimestampedEntry,
221};
222pub use snapshot::types::{ActiveAllocation, MemorySnapshot, MemoryStats, ThreadMemoryStats};
223/// Global tracking allocator instance - only enabled with tracking-allocator feature
224/// for single-threaded or low-concurrency applications.
225#[cfg(feature = "tracking-allocator")]
226#[global_allocator]
227pub static GLOBAL: TrackingAllocator = TrackingAllocator::new();
228/// Trait for types that can be tracked by the memory tracker.
229///
230/// This trait defines the interface for tracking memory allocations and their
231/// semantic roles in the three-layer object model (HeapOwner, Container, Value).
232pub trait Trackable {
233    /// Get the memory role classification for this value.
234    ///
235    /// Returns `TrackKind::HeapOwner` for types that own heap memory,
236    /// `TrackKind::Container` for types that organize data internally,
237    /// and `TrackKind::Value` for types without heap allocation.
238    fn track_kind(&self) -> TrackKind;
239    /// Get the type name for this value.
240    fn get_type_name(&self) -> &'static str;
241    /// Get estimated size of the allocation.
242    fn get_size_estimate(&self) -> usize;
243    /// Get reference count for smart pointers (Arc/Rc).
244    fn get_ref_count(&self) -> Option<usize> {
245        None
246    }
247    /// Get data pointer for smart pointers.
248    fn get_data_ptr(&self) -> Option<usize>;
249    /// Get the size of the pointed-to data.
250    fn get_data_size(&self) -> Option<usize>;
251}
252
253impl<T> Trackable for Vec<T> {
254    fn track_kind(&self) -> TrackKind {
255        TrackKind::HeapOwner {
256            ptr: self.as_ptr() as usize,
257            size: self.capacity() * std::mem::size_of::<T>(),
258        }
259    }
260    fn get_type_name(&self) -> &'static str {
261        "Vec<T>"
262    }
263    fn get_size_estimate(&self) -> usize {
264        std::mem::size_of::<T>() * self.capacity()
265    }
266    fn get_data_ptr(&self) -> Option<usize> {
267        Some(self.as_ptr() as usize)
268    }
269    fn get_data_size(&self) -> Option<usize> {
270        Some(std::mem::size_of::<T>() * self.len())
271    }
272}
273
274impl Trackable for String {
275    fn track_kind(&self) -> TrackKind {
276        TrackKind::HeapOwner {
277            ptr: self.as_ptr() as usize,
278            size: self.capacity(),
279        }
280    }
281    fn get_type_name(&self) -> &'static str {
282        "String"
283    }
284    fn get_size_estimate(&self) -> usize {
285        self.capacity()
286    }
287    fn get_data_ptr(&self) -> Option<usize> {
288        Some(self.as_ptr() as usize)
289    }
290    fn get_data_size(&self) -> Option<usize> {
291        Some(self.len())
292    }
293}
294
295impl<K, V> Trackable for std::collections::HashMap<K, V> {
296    fn track_kind(&self) -> TrackKind {
297        TrackKind::Container
298    }
299    fn get_type_name(&self) -> &'static str {
300        "HashMap<K, V>"
301    }
302    fn get_size_estimate(&self) -> usize {
303        std::mem::size_of::<(K, V)>() * self.capacity()
304    }
305    fn get_data_ptr(&self) -> Option<usize> {
306        None
307    }
308    fn get_data_size(&self) -> Option<usize> {
309        Some(std::mem::size_of::<(K, V)>() * self.len())
310    }
311}
312
313impl<K, V> Trackable for std::collections::BTreeMap<K, V> {
314    fn track_kind(&self) -> TrackKind {
315        TrackKind::Container
316    }
317    fn get_type_name(&self) -> &'static str {
318        "BTreeMap<K, V>"
319    }
320    fn get_size_estimate(&self) -> usize {
321        std::mem::size_of::<(K, V)>() * self.len()
322    }
323    fn get_data_ptr(&self) -> Option<usize> {
324        None
325    }
326    fn get_data_size(&self) -> Option<usize> {
327        Some(std::mem::size_of::<(K, V)>() * self.len())
328    }
329}
330
331impl<T> Trackable for std::collections::VecDeque<T> {
332    fn track_kind(&self) -> TrackKind {
333        TrackKind::Container
334    }
335    fn get_type_name(&self) -> &'static str {
336        "VecDeque<T>"
337    }
338    fn get_size_estimate(&self) -> usize {
339        std::mem::size_of::<T>() * self.capacity()
340    }
341    fn get_data_ptr(&self) -> Option<usize> {
342        None
343    }
344    fn get_data_size(&self) -> Option<usize> {
345        Some(std::mem::size_of::<T>() * self.len())
346    }
347}
348
349impl<T> Trackable for Box<T> {
350    fn track_kind(&self) -> TrackKind {
351        TrackKind::HeapOwner {
352            ptr: &**self as *const T as usize,
353            size: std::mem::size_of_val(&**self),
354        }
355    }
356    fn get_type_name(&self) -> &'static str {
357        "Box<T>"
358    }
359    fn get_size_estimate(&self) -> usize {
360        std::mem::size_of_val(&**self)
361    }
362    fn get_data_ptr(&self) -> Option<usize> {
363        Some(&**self as *const T as usize)
364    }
365    fn get_data_size(&self) -> Option<usize> {
366        Some(std::mem::size_of::<T>())
367    }
368}
369
370impl<T> Trackable for std::rc::Rc<T> {
371    fn track_kind(&self) -> TrackKind {
372        // Rc is a StackOwner: it's allocated on stack (8 bytes pointer) but points to heap
373        let stack_ptr = self as *const _ as usize;
374        let heap_ptr = &**self as *const T as usize;
375        TrackKind::StackOwner {
376            ptr: stack_ptr,
377            heap_ptr,
378            size: std::mem::size_of::<T>(),
379        }
380    }
381    fn get_type_name(&self) -> &'static str {
382        "Rc<T>"
383    }
384    fn get_size_estimate(&self) -> usize {
385        std::mem::size_of::<T>()
386    }
387    fn get_ref_count(&self) -> Option<usize> {
388        Some(std::rc::Rc::strong_count(self))
389    }
390    fn get_data_ptr(&self) -> Option<usize> {
391        Some(&**self as *const T as usize)
392    }
393    fn get_data_size(&self) -> Option<usize> {
394        Some(std::mem::size_of::<T>())
395    }
396}
397
398impl<T> Trackable for std::sync::Arc<T> {
399    fn track_kind(&self) -> TrackKind {
400        // Arc is a StackOwner: it's allocated on stack (8 bytes pointer) but points to heap
401        let stack_ptr = self as *const _ as usize;
402        let heap_ptr = &**self as *const T as usize;
403        TrackKind::StackOwner {
404            ptr: stack_ptr,
405            heap_ptr,
406            size: std::mem::size_of::<T>(),
407        }
408    }
409    fn get_type_name(&self) -> &'static str {
410        "Arc<T>"
411    }
412    fn get_size_estimate(&self) -> usize {
413        std::mem::size_of::<T>()
414    }
415    fn get_ref_count(&self) -> Option<usize> {
416        Some(std::sync::Arc::strong_count(self))
417    }
418    fn get_data_ptr(&self) -> Option<usize> {
419        Some(&**self as *const T as usize)
420    }
421    fn get_data_size(&self) -> Option<usize> {
422        Some(std::mem::size_of::<T>())
423    }
424}
425
426impl<T: Trackable> Trackable for std::cell::RefCell<T> {
427    fn track_kind(&self) -> TrackKind {
428        TrackKind::Container
429    }
430    fn get_type_name(&self) -> &'static str {
431        "RefCell<T>"
432    }
433    fn get_size_estimate(&self) -> usize {
434        std::mem::size_of::<T>()
435    }
436    fn get_data_ptr(&self) -> Option<usize> {
437        None
438    }
439    fn get_data_size(&self) -> Option<usize> {
440        Some(std::mem::size_of::<T>())
441    }
442}
443
444impl<T: Trackable> Trackable for std::sync::RwLock<T> {
445    fn track_kind(&self) -> TrackKind {
446        TrackKind::Container
447    }
448    fn get_type_name(&self) -> &'static str {
449        "RwLock<T>"
450    }
451    fn get_size_estimate(&self) -> usize {
452        std::mem::size_of::<T>()
453    }
454    fn get_data_ptr(&self) -> Option<usize> {
455        None
456    }
457    fn get_data_size(&self) -> Option<usize> {
458        Some(std::mem::size_of::<T>())
459    }
460}