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#![warn(missing_docs)]
9
10pub mod allocator;
11pub mod export_enhanced;
12pub mod tracker;
13pub mod types;
14pub mod utils;
15pub mod visualization;
16
17// Re-export main types for easier use
18pub use allocator::TrackingAllocator;
19pub use tracker::{get_global_tracker, MemoryTracker};
20pub use types::{AllocationInfo, TrackingError, TrackingResult};
21pub use utils::{format_bytes, get_simple_type, simplify_type_name};
22pub use visualization::{export_lifecycle_timeline, export_memory_analysis};
23
24// Set up the global allocator when the tracking-allocator feature is enabled
25#[cfg(feature = "tracking-allocator")]
26#[global_allocator]
27/// Global tracking allocator instance used when the tracking-allocator feature is enabled.
28pub static GLOBAL: TrackingAllocator = TrackingAllocator::new();
29
30/// Trait for types that can be tracked by the memory tracker.
31pub trait Trackable {
32    /// Get the pointer to the heap allocation for this value.
33    fn get_heap_ptr(&self) -> Option<usize>;
34
35    /// Get the type name for this value.
36    fn get_type_name(&self) -> &'static str;
37}
38
39// Implement Trackable for common heap-allocated types
40impl<T> Trackable for Vec<T> {
41    fn get_heap_ptr(&self) -> Option<usize> {
42        if self.capacity() > 0 {
43            Some(self.as_ptr() as usize)
44        } else {
45            None
46        }
47    }
48
49    fn get_type_name(&self) -> &'static str {
50        std::any::type_name::<Vec<T>>()
51    }
52}
53
54impl Trackable for String {
55    fn get_heap_ptr(&self) -> Option<usize> {
56        if self.capacity() > 0 {
57            Some(self.as_ptr() as usize)
58        } else {
59            None
60        }
61    }
62
63    fn get_type_name(&self) -> &'static str {
64        "String"
65    }
66}
67
68impl<T> Trackable for Box<T> {
69    fn get_heap_ptr(&self) -> Option<usize> {
70        Some(self.as_ref() as *const T as usize)
71    }
72
73    fn get_type_name(&self) -> &'static str {
74        std::any::type_name::<Box<T>>()
75    }
76}
77
78impl<T> Trackable for std::rc::Rc<T> {
79    fn get_heap_ptr(&self) -> Option<usize> {
80        // For Rc, the allocation tracking is complex because Rc uses a control block
81        // We'll track the Rc itself rather than the inner data to avoid pointer issues
82        Some(std::rc::Rc::as_ptr(self) as usize)
83    }
84
85    fn get_type_name(&self) -> &'static str {
86        std::any::type_name::<std::rc::Rc<T>>()
87    }
88}
89
90impl<T> Trackable for std::sync::Arc<T> {
91    fn get_heap_ptr(&self) -> Option<usize> {
92        // For Arc, the allocation tracking is complex because Arc uses a control block
93        // We'll track the Arc itself rather than the inner data to avoid pointer issues
94        Some(std::sync::Arc::as_ptr(self) as usize)
95    }
96
97    fn get_type_name(&self) -> &'static str {
98        std::any::type_name::<std::sync::Arc<T>>()
99    }
100}
101
102/// Macro to track a variable's memory allocation.
103///
104/// This macro associates a variable name with its heap allocation,
105/// allowing the memory tracker to provide meaningful names in reports.
106///
107/// # Example
108/// ```rust
109/// use memscope_rs::track_var;
110///
111/// let my_vec = vec![1, 2, 3, 4, 5];
112/// track_var!(my_vec);
113/// ```
114#[macro_export]
115macro_rules! track_var {
116    ($var:ident) => {
117        $crate::_track_var_impl(&$var, stringify!($var))
118    };
119}
120
121/// Internal implementation function for the track_var! macro.
122/// This function should not be called directly.
123#[doc(hidden)]
124pub fn _track_var_impl<T: Trackable>(var: &T, var_name: &str) -> TrackingResult<()> {
125    if let Some(ptr) = var.get_heap_ptr() {
126        let tracker = get_global_tracker();
127        let type_name = var.get_type_name().to_string();
128
129        // Debug: Print tracking attempt
130        // tracing::debug!(
131        //     "Tracking variable '{}' of type '{}' at ptr {:x}",
132        //     var_name,
133        //     type_name,
134        //     ptr
135        // );
136
137        tracker.associate_var(ptr, var_name.to_string(), type_name)
138    } else {
139        // Variable doesn't have a heap allocation (e.g., empty Vec)
140        // tracing::debug!("Variable '{}' has no heap allocation to track", var_name);
141        Ok(())
142    }
143}
144
145/// Initialize the memory tracking system.
146///
147/// This function sets up the tracing subscriber and prepares the global tracker.
148/// Call this early in your application, typically in main().
149///
150/// # Example
151/// ```rust
152/// memscope_rs::init();
153/// // Your application code here
154/// ```
155pub fn init() {
156    use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
157
158    tracing_subscriber::registry()
159        .with(
160            tracing_subscriber::EnvFilter::try_from_default_env()
161                .unwrap_or_else(|_| "memscope_rs=info".into()),
162        )
163        .with(tracing_subscriber::fmt::layer())
164        .init();
165
166    tracing::info!("memscope-rs initialized");
167}