1#![warn(missing_docs)]
9
10pub mod advanced_trackable_macro;
12pub mod advanced_types;
14pub mod analysis;
16pub mod cli;
18pub mod core;
20pub mod export;
22pub mod utils;
24pub mod variable_registry;
26
27pub mod enhanced_types;
30pub use advanced_types::*;
31pub use analysis::*;
32pub use export::*;
33pub use analysis::enhanced_memory_analysis::EnhancedMemoryAnalyzer;
37pub use analysis::unsafe_ffi_tracker::{get_global_unsafe_ffi_tracker, UnsafeFFITracker};
38pub use core::allocator::TrackingAllocator;
39pub use core::tracker::{get_global_tracker, ExportOptions, MemoryTracker};
40pub use core::types::{AllocationInfo, TrackingError, TrackingResult};
41pub use export::visualization::{export_lifecycle_timeline, export_memory_analysis};
42pub use utils::{format_bytes, get_simple_type, simplify_type_name};
43
44#[cfg(feature = "derive")]
46pub use memscope_derive::Trackable;
47
48#[cfg(feature = "tracking-allocator")]
50#[global_allocator]
51pub static GLOBAL: TrackingAllocator = TrackingAllocator::new();
53
54pub trait Trackable {
56 fn get_heap_ptr(&self) -> Option<usize>;
58
59 fn get_type_name(&self) -> &'static str;
61
62 fn get_size_estimate(&self) -> usize;
64
65 fn get_ref_count(&self) -> usize {
67 1
68 }
69
70 fn get_data_ptr(&self) -> usize {
72 self.get_heap_ptr().unwrap_or(0)
73 }
74
75 fn get_internal_allocations(&self, _var_name: &str) -> Vec<(usize, String)> {
77 Vec::new()
78 }
79
80 fn track_clone_relationship(&self, _clone_ptr: usize, _source_ptr: usize) {
82 }
84
85 fn update_ref_count_tracking(&self, _ptr: usize) {
87 }
89
90 fn get_advanced_type_info(&self) -> Option<crate::advanced_types::AdvancedTypeInfo> {
92 let type_name = self.get_type_name();
94 if crate::advanced_types::is_advanced_type(type_name) {
95 let allocation = crate::core::types::AllocationInfo {
97 ptr: self.get_heap_ptr().unwrap_or(0),
98 size: self.get_size_estimate(),
99 var_name: None,
100 type_name: Some(type_name.to_string()),
101 scope_name: None,
102 timestamp_alloc: std::time::SystemTime::now()
103 .duration_since(std::time::UNIX_EPOCH)
104 .unwrap_or_default()
105 .as_nanos() as u64,
106 timestamp_dealloc: None,
107 thread_id: format!("{:?}", std::thread::current().id()),
108 borrow_count: 0,
109 stack_trace: None,
110 is_leaked: false,
111 lifetime_ms: None,
112 smart_pointer_info: None,
113 memory_layout: None,
114 generic_info: None,
115 dynamic_type_info: None,
116 runtime_state: None,
117 stack_allocation: None,
118 temporary_object: None,
119 fragmentation_analysis: None,
120 generic_instantiation: None,
121 type_relationships: None,
122 type_usage: None,
123 function_call_tracking: None,
124 lifecycle_tracking: None,
125 access_tracking: None,
126 };
127
128 Some(
129 crate::advanced_types::GenericAdvancedTypeAnalyzer::analyze_by_type_name(
130 type_name,
131 &allocation,
132 ),
133 )
134 } else {
135 None
136 }
137 }
138}
139
140impl<T> Trackable for Vec<T> {
142 fn get_heap_ptr(&self) -> Option<usize> {
143 if self.capacity() > 0 {
144 Some(self.as_ptr() as usize)
145 } else {
146 None
147 }
148 }
149
150 fn get_type_name(&self) -> &'static str {
151 std::any::type_name::<Vec<T>>()
152 }
153
154 fn get_size_estimate(&self) -> usize {
155 self.capacity() * std::mem::size_of::<T>()
156 }
157}
158
159impl Trackable for String {
160 fn get_heap_ptr(&self) -> Option<usize> {
161 if self.capacity() > 0 {
162 Some(self.as_ptr() as usize)
163 } else {
164 None
165 }
166 }
167
168 fn get_type_name(&self) -> &'static str {
169 "String"
170 }
171
172 fn get_size_estimate(&self) -> usize {
173 self.capacity()
174 }
175}
176
177impl<T> Trackable for Box<T> {
178 fn get_heap_ptr(&self) -> Option<usize> {
179 Some(self.as_ref() as *const T as usize)
180 }
181
182 fn get_type_name(&self) -> &'static str {
183 std::any::type_name::<Box<T>>()
184 }
185
186 fn get_size_estimate(&self) -> usize {
187 std::mem::size_of::<T>()
188 }
189}
190
191impl<T> Trackable for std::rc::Rc<T> {
192 fn get_heap_ptr(&self) -> Option<usize> {
193 let instance_ptr = self as *const _ as usize;
196
197 Some(0x5000_0000 + (instance_ptr % 0x0FFF_FFFF))
200 }
201
202 fn get_type_name(&self) -> &'static str {
203 std::any::type_name::<std::rc::Rc<T>>()
204 }
205
206 fn get_size_estimate(&self) -> usize {
207 std::mem::size_of::<T>() + std::mem::size_of::<usize>() * 2 }
209
210 fn get_ref_count(&self) -> usize {
212 std::rc::Rc::strong_count(self)
213 }
214
215 fn get_data_ptr(&self) -> usize {
217 std::rc::Rc::as_ptr(self) as usize
218 }
219
220 fn track_clone_relationship(&self, clone_ptr: usize, source_ptr: usize) {
221 let tracker = crate::core::tracker::get_global_tracker();
222 let data_ptr = self.get_data_ptr();
223 let strong_count = std::rc::Rc::strong_count(self);
224 let weak_count = std::rc::Rc::weak_count(self);
225
226 if let Err(e) = tracker.track_smart_pointer_clone(
227 clone_ptr,
228 source_ptr,
229 data_ptr,
230 strong_count,
231 weak_count,
232 ) {
233 tracing::warn!("Failed to track Rc clone relationship: {}", e);
234 }
235 }
236
237 fn update_ref_count_tracking(&self, ptr: usize) {
238 let tracker = crate::core::tracker::get_global_tracker();
239 let strong_count = std::rc::Rc::strong_count(self);
240 let weak_count = std::rc::Rc::weak_count(self);
241
242 if let Err(e) = tracker.update_smart_pointer_ref_count(ptr, strong_count, weak_count) {
243 tracing::warn!("Failed to update Rc ref count: {}", e);
244 }
245 }
246}
247
248impl<T> Trackable for std::sync::Arc<T> {
249 fn get_heap_ptr(&self) -> Option<usize> {
250 let instance_ptr = self as *const _ as usize;
253
254 Some(0x6000_0000 + (instance_ptr % 0x0FFF_FFFF))
257 }
258
259 fn get_type_name(&self) -> &'static str {
260 std::any::type_name::<std::sync::Arc<T>>()
261 }
262
263 fn get_size_estimate(&self) -> usize {
264 std::mem::size_of::<T>() + std::mem::size_of::<std::sync::atomic::AtomicUsize>() * 2
265 }
267
268 fn get_ref_count(&self) -> usize {
270 std::sync::Arc::strong_count(self)
271 }
272
273 fn get_data_ptr(&self) -> usize {
275 std::sync::Arc::as_ptr(self) as usize
276 }
277
278 fn track_clone_relationship(&self, clone_ptr: usize, source_ptr: usize) {
279 let tracker = crate::core::tracker::get_global_tracker();
280 let data_ptr = self.get_data_ptr();
281 let strong_count = std::sync::Arc::strong_count(self);
282 let weak_count = std::sync::Arc::weak_count(self);
283
284 if let Err(e) = tracker.track_smart_pointer_clone(
285 clone_ptr,
286 source_ptr,
287 data_ptr,
288 strong_count,
289 weak_count,
290 ) {
291 tracing::warn!("Failed to track Arc clone relationship: {}", e);
292 }
293 }
294
295 fn update_ref_count_tracking(&self, ptr: usize) {
296 let tracker = crate::core::tracker::get_global_tracker();
297 let strong_count = std::sync::Arc::strong_count(self);
298 let weak_count = std::sync::Arc::weak_count(self);
299
300 if let Err(e) = tracker.update_smart_pointer_ref_count(ptr, strong_count, weak_count) {
301 tracing::warn!("Failed to update Arc ref count: {}", e);
302 }
303 }
304}
305
306impl<K, V> Trackable for std::collections::HashMap<K, V> {
307 fn get_heap_ptr(&self) -> Option<usize> {
308 Some(self as *const _ as usize)
311 }
312
313 fn get_type_name(&self) -> &'static str {
314 std::any::type_name::<std::collections::HashMap<K, V>>()
315 }
316
317 fn get_size_estimate(&self) -> usize {
318 self.capacity() * (std::mem::size_of::<K>() + std::mem::size_of::<V>() + 16)
320 }
321}
322
323impl<K, V> Trackable for std::collections::BTreeMap<K, V> {
324 fn get_heap_ptr(&self) -> Option<usize> {
325 if self.is_empty() {
326 None
327 } else {
328 Some(self as *const _ as usize)
329 }
330 }
331
332 fn get_type_name(&self) -> &'static str {
333 std::any::type_name::<std::collections::BTreeMap<K, V>>()
334 }
335
336 fn get_size_estimate(&self) -> usize {
337 self.len() * (std::mem::size_of::<K>() + std::mem::size_of::<V>() + 32)
339 }
340}
341
342impl<T> Trackable for std::collections::HashSet<T> {
343 fn get_heap_ptr(&self) -> Option<usize> {
344 if self.is_empty() {
345 None
346 } else {
347 Some(self as *const _ as usize)
348 }
349 }
350
351 fn get_type_name(&self) -> &'static str {
352 std::any::type_name::<std::collections::HashSet<T>>()
353 }
354
355 fn get_size_estimate(&self) -> usize {
356 self.capacity() * (std::mem::size_of::<T>() + 8) }
358}
359
360impl<T> Trackable for std::collections::BTreeSet<T> {
361 fn get_heap_ptr(&self) -> Option<usize> {
362 if self.is_empty() {
363 None
364 } else {
365 Some(self as *const _ as usize)
366 }
367 }
368
369 fn get_type_name(&self) -> &'static str {
370 std::any::type_name::<std::collections::BTreeSet<T>>()
371 }
372
373 fn get_size_estimate(&self) -> usize {
374 self.len() * (std::mem::size_of::<T>() + 24) }
376}
377
378impl<T> Trackable for std::collections::VecDeque<T> {
379 fn get_heap_ptr(&self) -> Option<usize> {
380 if self.capacity() > 0 {
381 Some(self.as_slices().0.as_ptr() as usize)
382 } else {
383 None
384 }
385 }
386
387 fn get_type_name(&self) -> &'static str {
388 std::any::type_name::<std::collections::VecDeque<T>>()
389 }
390
391 fn get_size_estimate(&self) -> usize {
392 self.capacity() * std::mem::size_of::<T>()
393 }
394}
395
396impl<T> Trackable for std::collections::LinkedList<T> {
397 fn get_heap_ptr(&self) -> Option<usize> {
398 if self.is_empty() {
399 None
400 } else {
401 Some(self as *const _ as usize)
402 }
403 }
404
405 fn get_type_name(&self) -> &'static str {
406 std::any::type_name::<std::collections::LinkedList<T>>()
407 }
408
409 fn get_size_estimate(&self) -> usize {
410 self.len() * (std::mem::size_of::<T>() + std::mem::size_of::<usize>() * 2)
411 }
413}
414
415impl<T> Trackable for std::collections::BinaryHeap<T> {
416 fn get_heap_ptr(&self) -> Option<usize> {
417 if self.capacity() > 0 {
418 Some(self as *const _ as usize)
419 } else {
420 None
421 }
422 }
423
424 fn get_type_name(&self) -> &'static str {
425 std::any::type_name::<std::collections::BinaryHeap<T>>()
426 }
427
428 fn get_size_estimate(&self) -> usize {
429 self.capacity() * std::mem::size_of::<T>()
430 }
431}
432
433impl<T> Trackable for std::rc::Weak<T> {
434 fn get_heap_ptr(&self) -> Option<usize> {
435 let instance_ptr = self as *const _ as usize;
437 Some(0x7000_0000 + (instance_ptr % 0x0FFF_FFFF))
438 }
439
440 fn get_type_name(&self) -> &'static str {
441 std::any::type_name::<std::rc::Weak<T>>()
442 }
443
444 fn get_size_estimate(&self) -> usize {
445 std::mem::size_of::<std::rc::Weak<T>>()
446 }
447
448 fn get_ref_count(&self) -> usize {
449 self.weak_count()
450 }
451
452 fn get_data_ptr(&self) -> usize {
453 if let Some(upgraded) = self.upgrade() {
455 std::rc::Rc::as_ptr(&upgraded) as usize
456 } else {
457 0 }
459 }
460}
461
462impl<T> Trackable for std::sync::Weak<T> {
463 fn get_heap_ptr(&self) -> Option<usize> {
464 let instance_ptr = self as *const _ as usize;
466 Some(0x8000_0000 + (instance_ptr % 0x0FFF_FFFF))
467 }
468
469 fn get_type_name(&self) -> &'static str {
470 std::any::type_name::<std::sync::Weak<T>>()
471 }
472
473 fn get_size_estimate(&self) -> usize {
474 std::mem::size_of::<std::sync::Weak<T>>()
475 }
476
477 fn get_ref_count(&self) -> usize {
478 self.weak_count()
479 }
480
481 fn get_data_ptr(&self) -> usize {
482 if let Some(upgraded) = self.upgrade() {
484 std::sync::Arc::as_ptr(&upgraded) as usize
485 } else {
486 0 }
488 }
489}
490
491impl_advanced_trackable!(std::cell::RefCell<T>, 0xA000_0000);
493impl_advanced_trackable!(std::sync::Mutex<T>, 0xB000_0000);
494impl_advanced_trackable!(std::sync::RwLock<T>, 0xC000_0000);
495
496impl_advanced_trackable!(std::cell::Cell<T>, 0xA100_0000);
498impl_advanced_trackable!(std::sync::mpsc::Sender<T>, 0xD000_0000);
499impl_advanced_trackable!(std::sync::mpsc::Receiver<T>, 0xD100_0000);
500impl_advanced_trackable!(std::sync::atomic::AtomicBool, 0xE000_0000, no_generics);
501impl_advanced_trackable!(std::sync::atomic::AtomicUsize, 0xE100_0000, no_generics);
502impl_advanced_trackable!(std::sync::atomic::AtomicIsize, 0xE200_0000, no_generics);
503impl_advanced_trackable!(std::sync::atomic::AtomicU8, 0xE300_0000, no_generics);
504impl_advanced_trackable!(std::sync::atomic::AtomicU16, 0xE400_0000, no_generics);
505impl_advanced_trackable!(std::sync::atomic::AtomicU32, 0xE500_0000, no_generics);
506impl_advanced_trackable!(std::sync::atomic::AtomicU64, 0xE600_0000, no_generics);
507impl_advanced_trackable!(std::sync::atomic::AtomicI8, 0xE700_0000, no_generics);
508impl_advanced_trackable!(std::sync::atomic::AtomicI16, 0xE800_0000, no_generics);
509impl_advanced_trackable!(std::sync::atomic::AtomicI32, 0xE900_0000, no_generics);
510impl_advanced_trackable!(std::sync::atomic::AtomicI64, 0xEA00_0000, no_generics);
511impl_advanced_trackable!(std::mem::ManuallyDrop<T>, 0xF000_0000);
512impl_advanced_trackable!(std::mem::MaybeUninit<T>, 0xF100_0000);
513impl_advanced_trackable!(std::pin::Pin<T>, 0xF200_0000);
514
515impl<T: Trackable> Trackable for Option<T> {
517 fn get_heap_ptr(&self) -> Option<usize> {
518 match self {
519 Some(value) => value.get_heap_ptr(),
520 None => None,
521 }
522 }
523
524 fn get_type_name(&self) -> &'static str {
525 std::any::type_name::<Option<T>>()
526 }
527
528 fn get_size_estimate(&self) -> usize {
529 match self {
530 Some(value) => std::mem::size_of::<Option<T>>() + value.get_size_estimate(),
531 None => std::mem::size_of::<Option<T>>(),
532 }
533 }
534
535 fn get_internal_allocations(&self, var_name: &str) -> Vec<(usize, String)> {
536 match self {
537 Some(value) => value.get_internal_allocations(&format!("{}::Some", var_name)),
538 None => Vec::new(),
539 }
540 }
541}
542
543impl<T: Trackable, E: Trackable> Trackable for Result<T, E> {
545 fn get_heap_ptr(&self) -> Option<usize> {
546 match self {
547 Ok(value) => value.get_heap_ptr(),
548 Err(error) => error.get_heap_ptr(),
549 }
550 }
551
552 fn get_type_name(&self) -> &'static str {
553 std::any::type_name::<Result<T, E>>()
554 }
555
556 fn get_size_estimate(&self) -> usize {
557 match self {
558 Ok(value) => std::mem::size_of::<Result<T, E>>() + value.get_size_estimate(),
559 Err(error) => std::mem::size_of::<Result<T, E>>() + error.get_size_estimate(),
560 }
561 }
562
563 fn get_internal_allocations(&self, var_name: &str) -> Vec<(usize, String)> {
564 match self {
565 Ok(value) => value.get_internal_allocations(&format!("{}::Ok", var_name)),
566 Err(error) => error.get_internal_allocations(&format!("{}::Err", var_name)),
567 }
568 }
569}
570
571#[macro_export]
598macro_rules! track_var {
599 ($var:expr) => {{
600 let var_name = stringify!($var);
601 let _ = $crate::_track_var_impl(&$var, var_name);
602 }};
604}
605
606#[macro_export]
655macro_rules! track_var_owned {
656 ($var:expr) => {{
657 let var_name = stringify!($var);
658 $crate::TrackedVariable::new($var, var_name.to_string())
659 }};
660}
661
662#[macro_export]
696macro_rules! track_var_smart {
697 ($var:expr) => {{
698 let var_name = stringify!($var);
699 $crate::_smart_track_var_impl($var, var_name)
700 }};
701}
702
703static TRACKED_VARIABLE_COUNTER: std::sync::atomic::AtomicUsize =
705 std::sync::atomic::AtomicUsize::new(1);
706
707pub mod smart_pointer_utils {
713 #[derive(Debug, Clone, PartialEq)]
715 pub enum SmartPointerType {
716 Rc,
718 Arc,
720 Box,
722 None,
724 }
725
726 pub fn detect_smart_pointer_type(type_name: &str) -> SmartPointerType {
728 if type_name.contains("::Rc<") || type_name.contains("std::rc::Rc<") {
729 SmartPointerType::Rc
730 } else if type_name.contains("::Arc<") || type_name.contains("std::sync::Arc<") {
731 SmartPointerType::Arc
732 } else if type_name.contains("::Box<") || type_name.contains("std::boxed::Box<") {
733 SmartPointerType::Box
734 } else {
735 SmartPointerType::None
736 }
737 }
738
739 pub fn is_smart_pointer(type_name: &str) -> bool {
741 detect_smart_pointer_type(type_name) != SmartPointerType::None
742 }
743
744 pub fn generate_synthetic_pointer(
746 smart_pointer_type: SmartPointerType,
747 unique_id: usize,
748 ) -> usize {
749 match smart_pointer_type {
750 SmartPointerType::Rc => 0x5000_0000 + unique_id,
751 SmartPointerType::Arc => 0x6000_0000 + unique_id,
752 SmartPointerType::Box => 0x7000_0000 + unique_id,
753 SmartPointerType::None => unique_id, }
755 }
756}
757
758pub struct TrackedVariable<T: Trackable> {
775 inner: T,
776 var_name: String,
777 ptr: Option<usize>,
778 creation_time: u64,
779 unique_id: usize, destruction_tracked: std::sync::atomic::AtomicBool, }
782
783impl<T: Trackable> TrackedVariable<T> {
784 pub fn new(value: T, var_name: String) -> Self {
786 let creation_time = std::time::SystemTime::now()
787 .duration_since(std::time::UNIX_EPOCH)
788 .unwrap_or_default()
789 .as_nanos() as u64;
790
791 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
792 let type_name = value.get_type_name().to_string();
793 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(&type_name);
794 let is_smart_pointer = smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
795
796 let ptr = if is_smart_pointer {
799 Some(smart_pointer_utils::generate_synthetic_pointer(
800 smart_pointer_type,
801 unique_id,
802 ))
803 } else {
804 value.get_heap_ptr().or_else(|| {
806 Some(0x8000_0000 + unique_id)
808 })
809 };
810
811 if let Some(ptr_val) = ptr {
813 let tracker = get_global_tracker();
814
815 let _ = crate::variable_registry::VariableRegistry::register_variable(
817 ptr_val,
818 var_name.clone(),
819 type_name.clone(),
820 value.get_size_estimate(),
821 );
822
823 let scope_tracker = crate::core::scope_tracker::get_global_scope_tracker();
825 let _ = scope_tracker.associate_variable(var_name.clone(), value.get_size_estimate());
826
827 if is_smart_pointer {
829 let ref_count = value.get_ref_count();
831 let data_ptr = value.get_data_ptr();
832
833 let _ = tracker.create_smart_pointer_allocation(
834 ptr_val,
835 value.get_size_estimate(),
836 var_name.clone(),
837 type_name.clone(),
838 creation_time,
839 ref_count,
840 data_ptr,
841 );
842
843 tracing::debug!(
844 "🎯 Created smart pointer tracking for '{}' at ptr 0x{:x}, ref_count={}",
845 var_name,
846 ptr_val,
847 ref_count
848 );
849 } else if ptr_val >= 0x8000_0000 {
850 let _ = tracker.create_synthetic_allocation(
852 ptr_val,
853 value.get_size_estimate(),
854 var_name.clone(),
855 type_name.clone(),
856 creation_time,
857 );
858
859 tracing::debug!(
860 "🎯 Created synthetic tracking for '{}' at ptr 0x{:x}",
861 var_name,
862 ptr_val
863 );
864 } else {
865 let _ = tracker.associate_var(ptr_val, var_name.clone(), type_name.clone());
867
868 tracing::debug!(
869 "🎯 Associated variable '{}' of type '{}' at ptr 0x{:x}",
870 var_name,
871 type_name,
872 ptr_val
873 );
874 }
875 }
876
877 Self {
878 inner: value,
879 var_name,
880 ptr,
881 creation_time,
882 unique_id,
883 destruction_tracked: std::sync::atomic::AtomicBool::new(false),
884 }
885 }
886
887 pub fn get(&self) -> &T {
889 &self.inner
890 }
891
892 pub fn get_mut(&mut self) -> &mut T {
894 &mut self.inner
895 }
896
897 pub fn into_inner(self) -> T {
910 let mut manual_drop_self = std::mem::ManuallyDrop::new(self);
912
913 if let Some(ptr_val) = manual_drop_self.ptr.take() {
915 if !manual_drop_self
917 .destruction_tracked
918 .swap(true, std::sync::atomic::Ordering::Relaxed)
919 {
920 let type_name = manual_drop_self.inner.get_type_name();
921 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(type_name);
922 let is_smart_pointer =
923 smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
924
925 if is_smart_pointer {
926 let final_ref_count = manual_drop_self.inner.get_ref_count();
927 if let Err(e) = Self::track_smart_pointer_destruction(
928 &manual_drop_self.var_name,
929 ptr_val,
930 manual_drop_self.creation_time,
931 final_ref_count,
932 ) {
933 tracing::warn!(
934 "Failed to track smart pointer destruction in into_inner(): {}",
935 e
936 );
937 }
938 } else {
939 if let Err(e) = Self::track_destruction(
940 &manual_drop_self.var_name,
941 ptr_val,
942 manual_drop_self.creation_time,
943 ) {
944 tracing::warn!("Failed to track destruction in into_inner(): {}", e);
945 }
946 }
947 }
948 }
949
950 unsafe { std::ptr::read(&manual_drop_self.inner) }
953 }
954
955 fn track_destruction(var_name: &str, ptr: usize, creation_time: u64) -> TrackingResult<()> {
957 let destruction_time = std::time::SystemTime::now()
958 .duration_since(std::time::UNIX_EPOCH)
959 .unwrap_or_default()
960 .as_nanos() as u64;
961
962 let lifetime_ms = (destruction_time.saturating_sub(creation_time)) / 1_000_000;
963
964 if let Err(e) = crate::variable_registry::VariableRegistry::mark_variable_destroyed(
966 ptr,
967 destruction_time,
968 ) {
969 tracing::warn!("Failed to mark variable destroyed in registry: {}", e);
970 }
971
972 let tracker = get_global_tracker();
974 tracker.track_deallocation_with_lifetime(ptr, lifetime_ms)?;
975
976 tracing::debug!(
977 "Destroyed tracked variable '{}' at ptr 0x{:x}, lifetime: {}ms",
978 var_name,
979 ptr,
980 lifetime_ms
981 );
982
983 Ok(())
984 }
985
986 fn track_smart_pointer_destruction(
988 var_name: &str,
989 ptr: usize,
990 creation_time: u64,
991 final_ref_count: usize,
992 ) -> TrackingResult<()> {
993 let destruction_time = std::time::SystemTime::now()
994 .duration_since(std::time::UNIX_EPOCH)
995 .unwrap_or_default()
996 .as_nanos() as u64;
997
998 let lifetime_ms = (destruction_time.saturating_sub(creation_time)) / 1_000_000;
999
1000 if let Err(e) = crate::variable_registry::VariableRegistry::mark_variable_destroyed(
1002 ptr,
1003 destruction_time,
1004 ) {
1005 tracing::warn!("Failed to mark smart pointer destroyed in registry: {}", e);
1006 }
1007
1008 let tracker = get_global_tracker();
1010 tracker.track_smart_pointer_deallocation(ptr, lifetime_ms, final_ref_count)?;
1011
1012 tracing::debug!(
1013 "Destroyed smart pointer '{}' at ptr 0x{:x}, lifetime: {}ms, final_ref_count: {}",
1014 var_name,
1015 ptr,
1016 lifetime_ms,
1017 final_ref_count
1018 );
1019
1020 Ok(())
1021 }
1022}
1023
1024impl<T: Trackable> Drop for TrackedVariable<T> {
1025 fn drop(&mut self) {
1026 if let Some(ptr_val) = self.ptr.take() {
1028 if !self
1030 .destruction_tracked
1031 .swap(true, std::sync::atomic::Ordering::Relaxed)
1032 {
1033 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1035 let tracker = get_global_tracker();
1037 if tracker.is_fast_mode() {
1038 return;
1039 }
1040
1041 let type_name = self.inner.get_type_name();
1042 let smart_pointer_type =
1043 smart_pointer_utils::detect_smart_pointer_type(type_name);
1044 let is_smart_pointer =
1045 smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
1046
1047 if is_smart_pointer {
1048 let final_ref_count = self.inner.get_ref_count();
1050 if let Err(e) = Self::track_smart_pointer_destruction(
1051 &self.var_name,
1052 ptr_val,
1053 self.creation_time,
1054 final_ref_count,
1055 ) {
1056 tracing::error!(
1057 "Failed to track smart pointer destruction in drop: {}",
1058 e
1059 );
1060 }
1061 } else {
1062 if let Err(e) =
1064 Self::track_destruction(&self.var_name, ptr_val, self.creation_time)
1065 {
1066 tracing::error!("Failed to track destruction in drop: {}", e);
1067 }
1068 }
1069 }));
1070 }
1071 }
1072 }
1073}
1074
1075impl<T: Trackable> std::ops::Deref for TrackedVariable<T> {
1077 type Target = T;
1078
1079 fn deref(&self) -> &Self::Target {
1080 &self.inner
1081 }
1082}
1083
1084impl<T: Trackable> std::ops::DerefMut for TrackedVariable<T> {
1085 fn deref_mut(&mut self) -> &mut Self::Target {
1086 &mut self.inner
1087 }
1088}
1089
1090impl<T: Trackable + std::fmt::Debug> std::fmt::Debug for TrackedVariable<T> {
1092 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1093 write!(f, "TrackedVariable({:?})", self.inner)
1094 }
1095}
1096
1097impl<T: Trackable + std::fmt::Display> std::fmt::Display for TrackedVariable<T> {
1098 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1099 write!(f, "{}", self.inner)
1100 }
1101}
1102
1103impl<T: Trackable + Clone> Clone for TrackedVariable<T> {
1104 fn clone(&self) -> Self {
1105 let clone_name = format!("{}_clone_{}", self.var_name, self.unique_id);
1107 Self::new(self.inner.clone(), clone_name)
1108 }
1109}
1110
1111#[doc(hidden)]
1116pub fn _track_var_impl<T: Trackable>(var: &T, var_name: &str) -> TrackingResult<()> {
1117 let tracker = get_global_tracker();
1118
1119 if tracker.is_fast_mode() {
1121 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1122 let synthetic_ptr = 0x8000_0000 + unique_id;
1123 return tracker.fast_track_allocation(
1124 synthetic_ptr,
1125 var.get_size_estimate(),
1126 var_name.to_string(),
1127 );
1128 }
1129
1130 let type_name = var.get_type_name().to_string();
1131 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(&type_name);
1132 let is_smart_pointer = smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
1133
1134 let ptr = if is_smart_pointer {
1136 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1138 Some(smart_pointer_utils::generate_synthetic_pointer(
1139 smart_pointer_type,
1140 unique_id,
1141 ))
1142 } else {
1143 var.get_heap_ptr().or_else(|| {
1145 let unique_id =
1147 TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1148 Some(0x8000_0000 + unique_id)
1149 })
1150 };
1151
1152 if let Some(ptr_val) = ptr {
1153 let creation_time = std::time::SystemTime::now()
1154 .duration_since(std::time::UNIX_EPOCH)
1155 .unwrap_or_default()
1156 .as_nanos() as u64;
1157
1158 let _ = crate::variable_registry::VariableRegistry::register_variable(
1160 ptr_val,
1161 var_name.to_string(),
1162 type_name.clone(),
1163 var.get_size_estimate(),
1164 );
1165
1166 let scope_tracker = crate::core::scope_tracker::get_global_scope_tracker();
1168 let _ = scope_tracker.associate_variable(var_name.to_string(), var.get_size_estimate());
1169
1170 if is_smart_pointer {
1172 let ref_count = var.get_ref_count();
1174 let data_ptr = var.get_data_ptr();
1175
1176 let _ = tracker.create_smart_pointer_allocation(
1177 ptr_val,
1178 var.get_size_estimate(),
1179 var_name.to_string(),
1180 type_name.clone(),
1181 creation_time,
1182 ref_count,
1183 data_ptr,
1184 );
1185
1186 tracing::debug!(
1187 "🎯 Created smart pointer tracking for '{}' at ptr 0x{:x}, ref_count={}",
1188 var_name,
1189 ptr_val,
1190 ref_count
1191 );
1192 } else if ptr_val >= 0x8000_0000 {
1193 let _ = tracker.create_synthetic_allocation(
1195 ptr_val,
1196 var.get_size_estimate(),
1197 var_name.to_string(),
1198 type_name.clone(),
1199 creation_time,
1200 );
1201
1202 tracing::debug!(
1203 "🎯 Created synthetic tracking for '{}' at ptr 0x{:x}",
1204 var_name,
1205 ptr_val
1206 );
1207 } else {
1208 tracker.associate_var(ptr_val, var_name.to_string(), type_name.clone())?;
1210
1211 tracing::debug!(
1212 "🎯 Associated variable '{}' of type '{}' at ptr 0x{:x}",
1213 var_name,
1214 type_name,
1215 ptr_val
1216 );
1217 }
1218 } else {
1219 tracing::debug!(
1221 "Variable '{}' could not be tracked (no pointer generated)",
1222 var_name
1223 );
1224 }
1225 Ok(())
1226}
1227
1228impl MemoryTracker {
1229 pub fn export_to_json_optimized<P: AsRef<std::path::Path>>(
1231 &self,
1232 path: P,
1233 ) -> TrackingResult<crate::export::complex_type_export::ComplexTypeExportResult> {
1234 use crate::export::complex_type_export::{
1235 export_comprehensive_analysis_optimized, ComplexTypeExportConfig,
1236 };
1237
1238 let path = path.as_ref();
1239 println!("🚀 Using optimized complex type export for maximum performance...");
1240
1241 let start_time = std::time::Instant::now();
1242
1243 let allocations = self.get_active_allocations()?;
1245 let stats = self.get_stats()?;
1246
1247 let analysis_manager = crate::analysis::AnalysisManager::new();
1249 let comprehensive_report =
1250 analysis_manager.perform_comprehensive_analysis(&allocations, &stats);
1251
1252 let config = ComplexTypeExportConfig {
1254 separate_complex_types: true,
1255 compress_data: false,
1256 chunk_size: 1000,
1257 pretty_format: false, };
1259
1260 let export_result = export_comprehensive_analysis_optimized(
1262 &comprehensive_report,
1263 &allocations,
1264 path,
1265 &config,
1266 )?;
1267
1268 let export_time = start_time.elapsed();
1269
1270 println!(
1272 "✅ Optimized export completed in {:.2}ms",
1273 export_time.as_millis()
1274 );
1275 println!(
1276 "📊 Performance improvement: {:.1}%",
1277 export_result.export_stats.performance_improvement
1278 );
1279 println!(
1280 "📁 Main file: {} ({} bytes)",
1281 export_result.main_file, export_result.export_stats.main_file_size
1282 );
1283
1284 if export_result.export_stats.complex_files_size > 0 {
1285 println!(
1286 "📁 Complex type files: {} bytes total",
1287 export_result.export_stats.complex_files_size
1288 );
1289
1290 if let Some(ref file) = export_result.complex_types_file {
1291 println!(" - Complex types: {}", file);
1292 }
1293 if let Some(ref file) = export_result.borrow_analysis_file {
1294 println!(" - Borrow analysis: {}", file);
1295 }
1296 if let Some(ref file) = export_result.async_analysis_file {
1297 println!(" - Async analysis: {}", file);
1298 }
1299 if let Some(ref file) = export_result.closure_analysis_file {
1300 println!(" - Closure analysis: {}", file);
1301 }
1302 if let Some(ref file) = export_result.lifecycle_analysis_file {
1303 println!(" - Lifecycle analysis: {}", file);
1304 }
1305 }
1306
1307 Ok(export_result)
1308 }
1309}
1310
1311#[doc(hidden)]
1314pub fn _smart_track_var_impl<T: Trackable + 'static>(var: T, var_name: &str) -> TrackingResult<T> {
1315 use std::any::TypeId;
1316
1317 let type_id = TypeId::of::<T>();
1318 let type_name = std::any::type_name::<T>();
1319
1320 let is_copy_type = type_id == TypeId::of::<i8>()
1322 || type_id == TypeId::of::<i16>()
1323 || type_id == TypeId::of::<i32>()
1324 || type_id == TypeId::of::<i64>()
1325 || type_id == TypeId::of::<i128>()
1326 || type_id == TypeId::of::<isize>()
1327 || type_id == TypeId::of::<u8>()
1328 || type_id == TypeId::of::<u16>()
1329 || type_id == TypeId::of::<u32>()
1330 || type_id == TypeId::of::<u64>()
1331 || type_id == TypeId::of::<u128>()
1332 || type_id == TypeId::of::<usize>()
1333 || type_id == TypeId::of::<f32>()
1334 || type_id == TypeId::of::<f64>()
1335 || type_id == TypeId::of::<bool>()
1336 || type_id == TypeId::of::<char>();
1337
1338 let is_smart_pointer = type_name.contains("::Rc<")
1339 || type_name.contains("::Arc<")
1340 || type_name.contains("::Weak<");
1341
1342 if is_copy_type {
1343 let _ = _track_var_impl(&var, var_name);
1345 tracing::debug!(
1346 "🧠 Smart tracking: Copy type '{}' tracked by reference",
1347 var_name
1348 );
1349 Ok(var)
1350 } else if is_smart_pointer {
1351 let _ = _track_var_impl(&var, var_name);
1353 tracing::debug!(
1354 "🧠 Smart tracking: Smart pointer '{}' tracked by reference",
1355 var_name
1356 );
1357 Ok(var)
1358 } else {
1359 let _ = _track_var_impl(&var, var_name);
1361 tracing::debug!(
1362 "🧠 Smart tracking: Non-Copy type '{}' tracked by reference",
1363 var_name
1364 );
1365 Ok(var)
1366 }
1367}
1368
1369pub fn init() {
1380 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1381
1382 let default_level = if cfg!(test) || std::env::var("MEMSCOPE_TEST_MODE").is_ok() {
1384 "memscope_rs=error" } else {
1386 "memscope_rs=info" };
1388
1389 tracing_subscriber::registry()
1390 .with(
1391 tracing_subscriber::EnvFilter::try_from_default_env()
1392 .unwrap_or_else(|_| default_level.into()),
1393 )
1394 .with(tracing_subscriber::fmt::layer())
1395 .init();
1396
1397 tracing::info!("memscope-rs initialized");
1398}
1399
1400pub fn init_for_testing() {
1403 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1404
1405 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1407
1408 tracing_subscriber::registry()
1410 .with(
1411 tracing_subscriber::EnvFilter::try_from_default_env()
1412 .unwrap_or_else(|_| "memscope_rs=error".into()),
1413 )
1414 .with(tracing_subscriber::fmt::layer())
1415 .init();
1416
1417 tracing::debug!("memscope-rs initialized for testing");
1418}
1419
1420pub mod test_utils {
1422 pub fn init_test() {
1424 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1425 std::env::set_var("RUST_LOG", "error");
1426
1427 static INIT: std::sync::Once = std::sync::Once::new();
1429 INIT.call_once(|| {
1430 super::init_for_testing();
1431 });
1432
1433 let tracker = super::get_global_tracker();
1435 tracker.enable_fast_mode();
1436 }
1437
1438 pub fn reset_tracker() {
1440 }
1443}
1444
1445#[macro_export]
1447macro_rules! init_test {
1448 () => {
1449 $crate::test_utils::init_test();
1450 };
1451}
1452
1453pub fn enable_auto_export(export_path: Option<&str>) {
1456 std::env::set_var("MEMSCOPE_AUTO_EXPORT", "1");
1457 if let Some(path) = export_path {
1458 std::env::set_var("MEMSCOPE_EXPORT_PATH", path);
1459 }
1460
1461 install_exit_hook();
1463
1464 println!(
1465 "📋 Auto-export enabled - JSON will be exported to: {}",
1466 export_path.unwrap_or("memscope_final_snapshot.json")
1467 );
1468}
1469
1470fn install_exit_hook() {
1472 use std::sync::Once;
1473 static HOOK_INSTALLED: Once = Once::new();
1474
1475 HOOK_INSTALLED.call_once(|| {
1476 let original_hook = std::panic::take_hook();
1478 std::panic::set_hook(Box::new(move |panic_info| {
1479 eprintln!("🚨 Program panicked, attempting to export memory data...");
1480 let _ = export_final_snapshot("memscope_panic_snapshot");
1481 original_hook(panic_info);
1482 }));
1483
1484 extern "C" fn exit_handler() {
1486 if std::env::var("MEMSCOPE_AUTO_EXPORT").is_ok() {
1487 println!("🔄 Program ending, exporting final memory snapshot...");
1488 let export_path = std::env::var("MEMSCOPE_EXPORT_PATH")
1489 .unwrap_or_else(|_| "memscope_final_snapshot".to_string());
1490
1491 if let Err(e) = export_final_snapshot(&export_path) {
1492 eprintln!("❌ Failed to export final snapshot: {}", e);
1493 } else {
1494 println!("✅ Final memory snapshot exported successfully");
1495 }
1496 }
1497 }
1498
1499 unsafe {
1500 libc::atexit(exit_handler);
1501 }
1502
1503 tracing::debug!("📌 Exit hooks installed for automatic memory export");
1504 });
1505}
1506
1507fn export_final_snapshot(base_path: &str) -> TrackingResult<()> {
1509 let tracker = get_global_tracker();
1510
1511 std::thread::sleep(std::time::Duration::from_millis(10));
1513
1514 let json_path = format!("{}.json", base_path);
1515 tracker.export_to_json(&json_path)?;
1516
1517 let export_format =
1519 std::env::var("MEMSCOPE_EXPORT_FORMAT").unwrap_or_else(|_| "json".to_string());
1520 if export_format == "html" || export_format == "both" {
1521 let html_path = format!("{}.html", base_path);
1522 let _ = tracker.export_interactive_dashboard(&html_path);
1523 }
1524
1525 Ok(())
1526}