1#![allow(missing_docs)]
9#![allow(clippy::manual_is_multiple_of)] pub mod advanced_trackable_macro;
13pub mod advanced_types;
15pub mod analysis;
17pub mod async_memory;
18pub mod cli;
20pub mod core;
22pub mod export;
24pub mod lockfree;
26pub mod unified;
28pub mod utils;
30pub mod variable_registry;
32
33pub mod enhanced_types;
36pub use advanced_types::*;
37pub use analysis::*;
38
39pub use export::{
44 export_user_variables_binary, export_user_variables_json, };
47
48pub use export::{
50 export_lifecycle_data, LifecycleExportConfig, LifecycleExporter, };
54
55pub use export::{
57 binary::detect_binary_type, binary::BinaryParser, };
60
61pub use analysis::enhanced_memory_analysis::EnhancedMemoryAnalyzer;
63pub use analysis::unsafe_ffi_tracker::{get_global_unsafe_ffi_tracker, UnsafeFFITracker};
64pub use core::allocator::TrackingAllocator;
65pub use core::tracker::memory_tracker::BinaryExportMode;
66pub use core::tracker::{get_tracker, ExportOptions, MemoryTracker};
67pub use core::types::{AllocationInfo, TrackingError, TrackingResult};
68pub use utils::{format_bytes, get_simple_type, simplify_type_name};
69
70#[cfg(feature = "derive")]
72pub use memscope_derive::Trackable;
73
74#[cfg(feature = "tracking-allocator")]
78#[global_allocator]
79pub static GLOBAL: TrackingAllocator = TrackingAllocator::new();
80
81pub trait Trackable {
83 fn get_heap_ptr(&self) -> Option<usize>;
85
86 fn get_type_name(&self) -> &'static str;
88
89 fn get_size_estimate(&self) -> usize;
91
92 fn get_ref_count(&self) -> usize {
94 1
95 }
96
97 fn get_data_ptr(&self) -> usize {
99 self.get_heap_ptr().unwrap_or(0)
100 }
101
102 fn get_internal_allocations(&self, _var_name: &str) -> Vec<(usize, String)> {
104 Vec::new()
105 }
106
107 fn track_clone_relationship(&self, _clone_ptr: usize, _source_ptr: usize) {
109 }
111
112 fn update_ref_count_tracking(&self, _ptr: usize) {
114 }
116
117 fn get_advanced_type_info(&self) -> Option<crate::advanced_types::AdvancedTypeInfo> {
119 let type_name = self.get_type_name();
121 if crate::advanced_types::is_advanced_type(type_name) {
122 let allocation = crate::core::types::AllocationInfo {
124 ptr: self.get_heap_ptr().unwrap_or(0),
125 size: self.get_size_estimate(),
126 var_name: None,
127 type_name: Some(type_name.to_string()),
128 scope_name: None,
129 timestamp_alloc: std::time::SystemTime::now()
130 .duration_since(std::time::UNIX_EPOCH)
131 .unwrap_or_default()
132 .as_nanos() as u64,
133 timestamp_dealloc: None,
134 thread_id: format!("{:?}", std::thread::current().id()),
135 borrow_count: 0,
136 stack_trace: None,
137 is_leaked: false,
138 lifetime_ms: None,
139 borrow_info: None,
140 clone_info: None,
141 ownership_history_available: false,
142 smart_pointer_info: None,
143 memory_layout: None,
144 generic_info: None,
145 dynamic_type_info: None,
146 runtime_state: None,
147 stack_allocation: None,
148 temporary_object: None,
149 fragmentation_analysis: None,
150 generic_instantiation: None,
151 type_relationships: None,
152 type_usage: None,
153 function_call_tracking: None,
154 lifecycle_tracking: None,
155 access_tracking: None,
156 drop_chain_analysis: None,
157 };
158
159 Some(
160 crate::advanced_types::GenericAdvancedTypeAnalyzer::analyze_by_type_name(
161 type_name,
162 &allocation,
163 ),
164 )
165 } else {
166 None
167 }
168 }
169}
170
171impl<T> Trackable for Vec<T> {
173 fn get_heap_ptr(&self) -> Option<usize> {
174 if self.capacity() > 0 {
175 Some(self.as_ptr() as usize)
176 } else {
177 None
178 }
179 }
180
181 fn get_type_name(&self) -> &'static str {
182 std::any::type_name::<Vec<T>>()
183 }
184
185 fn get_size_estimate(&self) -> usize {
186 self.capacity() * std::mem::size_of::<T>()
187 }
188}
189
190impl Trackable for String {
191 fn get_heap_ptr(&self) -> Option<usize> {
192 if self.capacity() > 0 {
193 Some(self.as_ptr() as usize)
194 } else {
195 None
196 }
197 }
198
199 fn get_type_name(&self) -> &'static str {
200 "String"
201 }
202
203 fn get_size_estimate(&self) -> usize {
204 self.capacity()
205 }
206}
207
208impl<T> Trackable for Box<T> {
209 fn get_heap_ptr(&self) -> Option<usize> {
210 Some(self.as_ref() as *const T as usize)
211 }
212
213 fn get_type_name(&self) -> &'static str {
214 std::any::type_name::<Box<T>>()
215 }
216
217 fn get_size_estimate(&self) -> usize {
218 std::mem::size_of::<T>()
219 }
220}
221
222impl<T> Trackable for std::rc::Rc<T> {
223 fn get_heap_ptr(&self) -> Option<usize> {
224 let instance_ptr = self as *const _ as usize;
227
228 Some(0x5000_0000 + (instance_ptr % 0x0FFF_FFFF))
231 }
232
233 fn get_type_name(&self) -> &'static str {
234 std::any::type_name::<std::rc::Rc<T>>()
235 }
236
237 fn get_size_estimate(&self) -> usize {
238 std::mem::size_of::<T>() + std::mem::size_of::<usize>() * 2 }
240
241 fn get_ref_count(&self) -> usize {
243 std::rc::Rc::strong_count(self)
244 }
245
246 fn get_data_ptr(&self) -> usize {
248 std::rc::Rc::as_ptr(self) as usize
249 }
250
251 fn track_clone_relationship(&self, clone_ptr: usize, source_ptr: usize) {
252 let tracker = crate::core::tracker::get_tracker();
253 let _data_ptr = self.get_data_ptr();
254 let _strong_count = std::rc::Rc::strong_count(self);
255 let weak_count = std::rc::Rc::weak_count(self);
256
257 if let Err(e) = tracker.track_smart_pointer_clone(
258 clone_ptr, source_ptr, clone_ptr, 1, weak_count,
261 ) {
262 tracing::warn!("Failed to track Rc clone relationship: {}", e);
263 }
264 }
265
266 fn update_ref_count_tracking(&self, ptr: usize) {
267 let tracker = crate::core::tracker::get_tracker();
268 let strong_count = std::rc::Rc::strong_count(self);
269 let weak_count = std::rc::Rc::weak_count(self);
270
271 if let Err(e) = tracker.update_smart_pointer_ref_count(ptr, strong_count, weak_count) {
272 tracing::warn!("Failed to update Rc ref count: {}", e);
273 }
274 }
275}
276
277impl<T> Trackable for std::sync::Arc<T> {
278 fn get_heap_ptr(&self) -> Option<usize> {
279 let instance_ptr = self as *const _ as usize;
282
283 Some(0x6000_0000 + (instance_ptr % 0x0FFF_FFFF))
286 }
287
288 fn get_type_name(&self) -> &'static str {
289 std::any::type_name::<std::sync::Arc<T>>()
290 }
291
292 fn get_size_estimate(&self) -> usize {
293 std::mem::size_of::<T>() + std::mem::size_of::<std::sync::atomic::AtomicUsize>() * 2
294 }
296
297 fn get_ref_count(&self) -> usize {
299 std::sync::Arc::strong_count(self)
300 }
301
302 fn get_data_ptr(&self) -> usize {
304 std::sync::Arc::as_ptr(self) as usize
305 }
306
307 fn track_clone_relationship(&self, clone_ptr: usize, source_ptr: usize) {
308 let tracker = crate::core::tracker::get_tracker();
309 let data_ptr = self.get_data_ptr();
310 let strong_count = std::sync::Arc::strong_count(self);
311 let weak_count = std::sync::Arc::weak_count(self);
312
313 if let Err(e) = tracker.track_smart_pointer_clone(
314 clone_ptr,
315 source_ptr,
316 data_ptr,
317 strong_count,
318 weak_count,
319 ) {
320 tracing::warn!("Failed to track Arc clone relationship: {}", e);
321 }
322 }
323
324 fn update_ref_count_tracking(&self, ptr: usize) {
325 let tracker = crate::core::tracker::get_tracker();
326 let strong_count = std::sync::Arc::strong_count(self);
327 let weak_count = std::sync::Arc::weak_count(self);
328
329 if let Err(e) = tracker.update_smart_pointer_ref_count(ptr, strong_count, weak_count) {
330 tracing::warn!("Failed to update Arc ref count: {}", e);
331 }
332 }
333}
334
335impl<K, V, S> Trackable for std::collections::HashMap<K, V, S> {
336 fn get_heap_ptr(&self) -> Option<usize> {
337 Some(self as *const _ as usize)
340 }
341
342 fn get_type_name(&self) -> &'static str {
343 std::any::type_name::<std::collections::HashMap<K, V, S>>()
344 }
345
346 fn get_size_estimate(&self) -> usize {
347 self.capacity() * (std::mem::size_of::<K>() + std::mem::size_of::<V>() + 16)
349 }
350}
351
352impl<K, V> Trackable for std::collections::BTreeMap<K, V> {
353 fn get_heap_ptr(&self) -> Option<usize> {
354 if self.is_empty() {
355 None
356 } else {
357 Some(self as *const _ as usize)
358 }
359 }
360
361 fn get_type_name(&self) -> &'static str {
362 std::any::type_name::<std::collections::BTreeMap<K, V>>()
363 }
364
365 fn get_size_estimate(&self) -> usize {
366 self.len() * (std::mem::size_of::<K>() + std::mem::size_of::<V>() + 32)
368 }
369}
370
371impl<T> Trackable for std::collections::HashSet<T> {
372 fn get_heap_ptr(&self) -> Option<usize> {
373 if self.is_empty() {
374 None
375 } else {
376 Some(self as *const _ as usize)
377 }
378 }
379
380 fn get_type_name(&self) -> &'static str {
381 std::any::type_name::<std::collections::HashSet<T>>()
382 }
383
384 fn get_size_estimate(&self) -> usize {
385 self.capacity() * (std::mem::size_of::<T>() + 8) }
387}
388
389impl<T> Trackable for std::collections::BTreeSet<T> {
390 fn get_heap_ptr(&self) -> Option<usize> {
391 if self.is_empty() {
392 None
393 } else {
394 Some(self as *const _ as usize)
395 }
396 }
397
398 fn get_type_name(&self) -> &'static str {
399 std::any::type_name::<std::collections::BTreeSet<T>>()
400 }
401
402 fn get_size_estimate(&self) -> usize {
403 self.len() * (std::mem::size_of::<T>() + 24) }
405}
406
407impl<T> Trackable for std::collections::VecDeque<T> {
408 fn get_heap_ptr(&self) -> Option<usize> {
409 if self.capacity() > 0 {
410 Some(self.as_slices().0.as_ptr() as usize)
411 } else {
412 None
413 }
414 }
415
416 fn get_type_name(&self) -> &'static str {
417 std::any::type_name::<std::collections::VecDeque<T>>()
418 }
419
420 fn get_size_estimate(&self) -> usize {
421 self.capacity() * std::mem::size_of::<T>()
422 }
423}
424
425impl<T> Trackable for std::collections::LinkedList<T> {
426 fn get_heap_ptr(&self) -> Option<usize> {
427 if self.is_empty() {
428 None
429 } else {
430 Some(self as *const _ as usize)
431 }
432 }
433
434 fn get_type_name(&self) -> &'static str {
435 std::any::type_name::<std::collections::LinkedList<T>>()
436 }
437
438 fn get_size_estimate(&self) -> usize {
439 self.len() * (std::mem::size_of::<T>() + std::mem::size_of::<usize>() * 2)
440 }
442}
443
444impl<T> Trackable for std::collections::BinaryHeap<T> {
445 fn get_heap_ptr(&self) -> Option<usize> {
446 if self.capacity() > 0 {
447 Some(self as *const _ as usize)
448 } else {
449 None
450 }
451 }
452
453 fn get_type_name(&self) -> &'static str {
454 std::any::type_name::<std::collections::BinaryHeap<T>>()
455 }
456
457 fn get_size_estimate(&self) -> usize {
458 self.capacity() * std::mem::size_of::<T>()
459 }
460}
461
462impl<T> Trackable for std::rc::Weak<T> {
463 fn get_heap_ptr(&self) -> Option<usize> {
464 let instance_ptr = self as *const _ as usize;
466 Some(0x7000_0000 + (instance_ptr % 0x0FFF_FFFF))
467 }
468
469 fn get_type_name(&self) -> &'static str {
470 std::any::type_name::<std::rc::Weak<T>>()
471 }
472
473 fn get_size_estimate(&self) -> usize {
474 std::mem::size_of::<std::rc::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::rc::Rc::as_ptr(&upgraded) as usize
485 } else {
486 0 }
488 }
489}
490
491impl<T> Trackable for std::sync::Weak<T> {
492 fn get_heap_ptr(&self) -> Option<usize> {
493 let instance_ptr = self as *const _ as usize;
495 Some(0x8000_0000 + (instance_ptr % 0x0FFF_FFFF))
496 }
497
498 fn get_type_name(&self) -> &'static str {
499 std::any::type_name::<std::sync::Weak<T>>()
500 }
501
502 fn get_size_estimate(&self) -> usize {
503 std::mem::size_of::<std::sync::Weak<T>>()
504 }
505
506 fn get_ref_count(&self) -> usize {
507 self.weak_count()
508 }
509
510 fn get_data_ptr(&self) -> usize {
511 if let Some(upgraded) = self.upgrade() {
513 std::sync::Arc::as_ptr(&upgraded) as usize
514 } else {
515 0 }
517 }
518}
519
520impl_advanced_trackable!(std::cell::RefCell<T>, 0xA000_0000);
522impl_advanced_trackable!(std::sync::Mutex<T>, 0xB000_0000);
523impl_advanced_trackable!(std::sync::RwLock<T>, 0xC000_0000);
524
525impl_advanced_trackable!(std::cell::Cell<T>, 0xA100_0000);
527impl_advanced_trackable!(std::sync::mpsc::Sender<T>, 0xD000_0000);
528impl_advanced_trackable!(std::sync::mpsc::Receiver<T>, 0xD100_0000);
529impl_advanced_trackable!(std::sync::atomic::AtomicBool, 0xE000_0000, no_generics);
530impl_advanced_trackable!(std::sync::atomic::AtomicUsize, 0xE100_0000, no_generics);
531impl_advanced_trackable!(std::sync::atomic::AtomicIsize, 0xE200_0000, no_generics);
532impl_advanced_trackable!(std::sync::atomic::AtomicU8, 0xE300_0000, no_generics);
533impl_advanced_trackable!(std::sync::atomic::AtomicU16, 0xE400_0000, no_generics);
534impl_advanced_trackable!(std::sync::atomic::AtomicU32, 0xE500_0000, no_generics);
535impl_advanced_trackable!(std::sync::atomic::AtomicU64, 0xE600_0000, no_generics);
536impl_advanced_trackable!(std::sync::atomic::AtomicI8, 0xE700_0000, no_generics);
537impl_advanced_trackable!(std::sync::atomic::AtomicI16, 0xE800_0000, no_generics);
538impl_advanced_trackable!(std::sync::atomic::AtomicI32, 0xE900_0000, no_generics);
539impl_advanced_trackable!(std::sync::atomic::AtomicI64, 0xEA00_0000, no_generics);
540impl_advanced_trackable!(std::mem::ManuallyDrop<T>, 0xF000_0000);
541impl_advanced_trackable!(std::mem::MaybeUninit<T>, 0xF100_0000);
542impl_advanced_trackable!(std::pin::Pin<T>, 0xF200_0000);
543
544impl_advanced_trackable!(std::ffi::CString, 0xF300_0000, no_generics);
546impl_advanced_trackable!(std::hash::RandomState, 0xF400_0000, no_generics);
547
548macro_rules! impl_primitive_trackable {
550 ($type:ty, $base_ptr:expr) => {
551 impl Trackable for $type {
552 fn get_heap_ptr(&self) -> Option<usize> {
553 Some($base_ptr + (self as *const _ as usize % 0x0FFF_FFFF))
555 }
556
557 fn get_type_name(&self) -> &'static str {
558 std::any::type_name::<$type>()
559 }
560
561 fn get_size_estimate(&self) -> usize {
562 std::mem::size_of::<$type>()
563 }
564 }
565 };
566}
567
568impl_primitive_trackable!(i8, 0x1000_0000);
570impl_primitive_trackable!(i16, 0x1100_0000);
571impl_primitive_trackable!(i32, 0x1200_0000);
572impl_primitive_trackable!(i64, 0x1300_0000);
573impl_primitive_trackable!(i128, 0x1400_0000);
574impl_primitive_trackable!(isize, 0x1500_0000);
575impl_primitive_trackable!(u8, 0x1600_0000);
576impl_primitive_trackable!(u16, 0x1700_0000);
577impl_primitive_trackable!(u32, 0x1800_0000);
578impl_primitive_trackable!(u64, 0x1900_0000);
579impl_primitive_trackable!(u128, 0x1A00_0000);
580impl_primitive_trackable!(usize, 0x1B00_0000);
581impl_primitive_trackable!(f32, 0x1C00_0000);
582impl_primitive_trackable!(f64, 0x1D00_0000);
583impl_primitive_trackable!(bool, 0x1E00_0000);
584impl_primitive_trackable!(char, 0x1F00_0000);
585
586impl<T1: Trackable, T2: Trackable, T3: Trackable> Trackable for (T1, T2, T3) {
588 fn get_heap_ptr(&self) -> Option<usize> {
589 if let Some(ptr1) = self.0.get_heap_ptr() {
591 Some(0xF500_0000 + (ptr1 % 0x0FFF_FFFF))
592 } else if let Some(ptr2) = self.1.get_heap_ptr() {
593 Some(0xF500_0000 + (ptr2 % 0x0FFF_FFFF))
594 } else if let Some(ptr3) = self.2.get_heap_ptr() {
595 Some(0xF500_0000 + (ptr3 % 0x0FFF_FFFF))
596 } else {
597 Some(0xF500_0000 + (self as *const _ as usize % 0x0FFF_FFFF))
599 }
600 }
601
602 fn get_type_name(&self) -> &'static str {
603 std::any::type_name::<(T1, T2, T3)>()
604 }
605
606 fn get_size_estimate(&self) -> usize {
607 self.0.get_size_estimate() + self.1.get_size_estimate() + self.2.get_size_estimate()
608 }
609}
610
611impl<T: Trackable> Trackable for Option<T> {
613 fn get_heap_ptr(&self) -> Option<usize> {
614 match self {
615 Some(value) => value.get_heap_ptr(),
616 None => None,
617 }
618 }
619
620 fn get_type_name(&self) -> &'static str {
621 std::any::type_name::<Option<T>>()
622 }
623
624 fn get_size_estimate(&self) -> usize {
625 match self {
626 Some(value) => std::mem::size_of::<Option<T>>() + value.get_size_estimate(),
627 None => std::mem::size_of::<Option<T>>(),
628 }
629 }
630
631 fn get_internal_allocations(&self, var_name: &str) -> Vec<(usize, String)> {
632 match self {
633 Some(value) => value.get_internal_allocations(&format!("{var_name}::Some")),
634 None => Vec::new(),
635 }
636 }
637}
638
639impl<T: Trackable, E: Trackable> Trackable for Result<T, E> {
641 fn get_heap_ptr(&self) -> Option<usize> {
642 match self {
643 Ok(value) => value.get_heap_ptr(),
644 Err(error) => error.get_heap_ptr(),
645 }
646 }
647
648 fn get_type_name(&self) -> &'static str {
649 std::any::type_name::<Result<T, E>>()
650 }
651
652 fn get_size_estimate(&self) -> usize {
653 match self {
654 Ok(value) => std::mem::size_of::<Result<T, E>>() + value.get_size_estimate(),
655 Err(error) => std::mem::size_of::<Result<T, E>>() + error.get_size_estimate(),
656 }
657 }
658
659 fn get_internal_allocations(&self, var_name: &str) -> Vec<(usize, String)> {
660 match self {
661 Ok(value) => value.get_internal_allocations(&format!("{var_name}::Ok")),
662 Err(error) => error.get_internal_allocations(&format!("{var_name}::Err")),
663 }
664 }
665}
666
667#[macro_export]
691macro_rules! track_var {
692 ($var:expr) => {{
693 let var_name = stringify!($var);
694 let _ = $crate::_track_var_impl(&$var, var_name);
695 }};
697}
698
699#[macro_export]
740macro_rules! track_var_owned {
741 ($var:expr) => {{
742 let var_name = stringify!($var);
743 $crate::TrackedVariable::new($var, var_name.to_string())
744 }};
745}
746
747#[macro_export]
781macro_rules! track_var_smart {
782 ($var:expr) => {{
783 let var_name = stringify!($var);
784 $crate::_smart_track_var_impl($var, var_name)
785 }};
786}
787
788static TRACKED_VARIABLE_COUNTER: std::sync::atomic::AtomicUsize =
790 std::sync::atomic::AtomicUsize::new(1);
791
792pub mod smart_pointer_utils {
798 #[derive(Debug, Clone, PartialEq)]
800 pub enum SmartPointerType {
801 Rc,
803 Arc,
805 Box,
807 None,
809 }
810
811 pub fn detect_smart_pointer_type(type_name: &str) -> SmartPointerType {
813 if type_name.contains("::Rc<") || type_name.contains("std::rc::Rc<") {
814 SmartPointerType::Rc
815 } else if type_name.contains("::Arc<") || type_name.contains("std::sync::Arc<") {
816 SmartPointerType::Arc
817 } else if type_name.contains("::Box<") || type_name.contains("std::boxed::Box<") {
818 SmartPointerType::Box
819 } else {
820 SmartPointerType::None
821 }
822 }
823
824 pub fn is_smart_pointer(type_name: &str) -> bool {
826 detect_smart_pointer_type(type_name) != SmartPointerType::None
827 }
828
829 pub fn generate_synthetic_pointer(
831 smart_pointer_type: SmartPointerType,
832 unique_id: usize,
833 ) -> usize {
834 match smart_pointer_type {
835 SmartPointerType::Rc => 0x5000_0000 + unique_id,
836 SmartPointerType::Arc => 0x6000_0000 + unique_id,
837 SmartPointerType::Box => 0x7000_0000 + unique_id,
838 SmartPointerType::None => unique_id, }
840 }
841}
842
843pub struct TrackedVariable<T: Trackable> {
860 inner: T,
861 var_name: String,
862 ptr: Option<usize>,
863 creation_time: u64,
864 unique_id: usize, destruction_tracked: std::sync::atomic::AtomicBool, }
867
868impl<T: Trackable> TrackedVariable<T> {
869 pub fn new(value: T, var_name: String) -> Self {
871 let creation_time = std::time::SystemTime::now()
872 .duration_since(std::time::UNIX_EPOCH)
873 .unwrap_or_default()
874 .as_nanos() as u64;
875
876 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
877 let type_name = value.get_type_name().to_string();
878 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(&type_name);
879 let is_smart_pointer = smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
880
881 let ptr = if is_smart_pointer {
884 Some(smart_pointer_utils::generate_synthetic_pointer(
885 smart_pointer_type,
886 unique_id,
887 ))
888 } else {
889 value.get_heap_ptr().or_else(|| {
891 Some(0x8000_0000 + unique_id)
893 })
894 };
895
896 if let Some(ptr_val) = ptr {
898 let tracker = crate::core::tracker::get_tracker();
899
900 let _ = crate::variable_registry::VariableRegistry::register_variable(
902 ptr_val,
903 var_name.clone(),
904 type_name.clone(),
905 value.get_size_estimate(),
906 );
907
908 let scope_tracker = crate::core::scope_tracker::get_global_scope_tracker();
910 let _ = scope_tracker.associate_variable(var_name.clone(), value.get_size_estimate());
911
912 if is_smart_pointer {
914 let ref_count = value.get_ref_count();
916 let data_ptr = value.get_data_ptr();
917
918 let _ = tracker.create_smart_pointer_allocation(
919 ptr_val,
920 value.get_size_estimate(),
921 var_name.clone(),
922 type_name.clone(),
923 creation_time,
924 ref_count,
925 data_ptr,
926 );
927
928 tracing::debug!(
929 "🎯 Created smart pointer tracking for '{}' at ptr 0x{:x}, ref_count={}",
930 var_name,
931 ptr_val,
932 ref_count
933 );
934 } else if ptr_val >= 0x8000_0000 {
935 let _ = tracker.create_synthetic_allocation(
937 ptr_val,
938 value.get_size_estimate(),
939 var_name.clone(),
940 type_name.clone(),
941 creation_time,
942 );
943
944 tracing::debug!(
945 "🎯 Created synthetic tracking for '{}' at ptr 0x{:x}",
946 var_name,
947 ptr_val
948 );
949 } else {
950 let _ = tracker.associate_var(ptr_val, var_name.clone(), type_name.clone());
952
953 tracing::debug!(
954 "🎯 Associated variable '{}' of type '{}' at ptr 0x{:x}",
955 var_name,
956 type_name,
957 ptr_val
958 );
959 }
960 }
961
962 Self {
963 inner: value,
964 var_name,
965 ptr,
966 creation_time,
967 unique_id,
968 destruction_tracked: std::sync::atomic::AtomicBool::new(false),
969 }
970 }
971
972 pub fn get(&self) -> &T {
974 &self.inner
975 }
976
977 pub fn get_mut(&mut self) -> &mut T {
979 &mut self.inner
980 }
981
982 pub fn into_inner(self) -> T {
995 let mut manual_drop_self = std::mem::ManuallyDrop::new(self);
997
998 if let Some(ptr_val) = manual_drop_self.ptr.take() {
1000 if !manual_drop_self
1002 .destruction_tracked
1003 .swap(true, std::sync::atomic::Ordering::Relaxed)
1004 {
1005 let type_name = manual_drop_self.inner.get_type_name();
1006 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(type_name);
1007 let is_smart_pointer =
1008 smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
1009
1010 if is_smart_pointer {
1011 let final_ref_count = manual_drop_self.inner.get_ref_count();
1012 if let Err(e) = Self::track_smart_pointer_destruction(
1013 &manual_drop_self.var_name,
1014 ptr_val,
1015 manual_drop_self.creation_time,
1016 final_ref_count,
1017 ) {
1018 tracing::warn!(
1019 "Failed to track smart pointer destruction in into_inner(): {}",
1020 e
1021 );
1022 }
1023 } else if let Err(e) = Self::track_destruction(
1024 &manual_drop_self.var_name,
1025 ptr_val,
1026 manual_drop_self.creation_time,
1027 ) {
1028 tracing::warn!("Failed to track destruction in into_inner(): {}", e);
1029 }
1030 }
1031 }
1032
1033 unsafe { std::ptr::read(&manual_drop_self.inner) }
1036 }
1037
1038 fn track_destruction(var_name: &str, ptr: usize, creation_time: u64) -> TrackingResult<()> {
1040 let destruction_time = std::time::SystemTime::now()
1041 .duration_since(std::time::UNIX_EPOCH)
1042 .unwrap_or_default()
1043 .as_nanos() as u64;
1044
1045 let lifetime_ms = (destruction_time.saturating_sub(creation_time)) / 1_000_000;
1046
1047 if let Err(e) = crate::variable_registry::VariableRegistry::mark_variable_destroyed(
1049 ptr,
1050 destruction_time,
1051 ) {
1052 tracing::warn!("Failed to mark variable destroyed in registry: {}", e);
1053 }
1054
1055 let tracker = crate::core::tracker::get_tracker();
1057 tracker.track_deallocation_with_lifetime(ptr, lifetime_ms)?;
1058
1059 tracing::debug!(
1060 "Destroyed tracked variable '{}' at ptr 0x{:x}, lifetime: {}ms",
1061 var_name,
1062 ptr,
1063 lifetime_ms
1064 );
1065
1066 Ok(())
1067 }
1068
1069 fn track_smart_pointer_destruction(
1071 var_name: &str,
1072 ptr: usize,
1073 creation_time: u64,
1074 final_ref_count: usize,
1075 ) -> TrackingResult<()> {
1076 let destruction_time = std::time::SystemTime::now()
1077 .duration_since(std::time::UNIX_EPOCH)
1078 .unwrap_or_default()
1079 .as_nanos() as u64;
1080
1081 let lifetime_ms = (destruction_time.saturating_sub(creation_time)) / 1_000_000;
1082
1083 if let Err(e) = crate::variable_registry::VariableRegistry::mark_variable_destroyed(
1085 ptr,
1086 destruction_time,
1087 ) {
1088 tracing::warn!("Failed to mark smart pointer destroyed in registry: {}", e);
1089 }
1090
1091 let tracker = crate::core::tracker::get_tracker();
1093 tracker.track_smart_pointer_deallocation(ptr, lifetime_ms, final_ref_count)?;
1094
1095 tracing::debug!(
1096 "Destroyed smart pointer '{}' at ptr 0x{:x}, lifetime: {}ms, final_ref_count: {}",
1097 var_name,
1098 ptr,
1099 lifetime_ms,
1100 final_ref_count
1101 );
1102
1103 Ok(())
1104 }
1105}
1106
1107impl<T: Trackable> Drop for TrackedVariable<T> {
1108 fn drop(&mut self) {
1109 if let Some(ptr_val) = self.ptr.take() {
1111 if !self
1113 .destruction_tracked
1114 .swap(true, std::sync::atomic::Ordering::Relaxed)
1115 {
1116 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1118 let tracker = crate::core::tracker::get_tracker();
1120 if tracker.is_fast_mode() {
1121 return;
1122 }
1123
1124 let type_name = self.inner.get_type_name();
1125 let smart_pointer_type =
1126 smart_pointer_utils::detect_smart_pointer_type(type_name);
1127 let is_smart_pointer =
1128 smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
1129
1130 if is_smart_pointer {
1131 let final_ref_count = self.inner.get_ref_count();
1133 if let Err(e) = Self::track_smart_pointer_destruction(
1134 &self.var_name,
1135 ptr_val,
1136 self.creation_time,
1137 final_ref_count,
1138 ) {
1139 tracing::error!(
1140 "Failed to track smart pointer destruction in drop: {}",
1141 e
1142 );
1143 }
1144 } else {
1145 if let Err(e) =
1147 Self::track_destruction(&self.var_name, ptr_val, self.creation_time)
1148 {
1149 tracing::error!("Failed to track destruction in drop: {}", e);
1150 }
1151 }
1152 }));
1153 }
1154 }
1155 }
1156}
1157
1158impl<T: Trackable> std::ops::Deref for TrackedVariable<T> {
1160 type Target = T;
1161
1162 fn deref(&self) -> &Self::Target {
1163 &self.inner
1164 }
1165}
1166
1167impl<T: Trackable> std::ops::DerefMut for TrackedVariable<T> {
1168 fn deref_mut(&mut self) -> &mut Self::Target {
1169 &mut self.inner
1170 }
1171}
1172
1173impl<T: Trackable + std::fmt::Debug> std::fmt::Debug for TrackedVariable<T> {
1175 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1176 write!(f, "TrackedVariable({:?})", self.inner)
1177 }
1178}
1179
1180impl<T: Trackable + std::fmt::Display> std::fmt::Display for TrackedVariable<T> {
1181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1182 write!(f, "{}", self.inner)
1183 }
1184}
1185
1186impl<T: Trackable + Clone> Clone for TrackedVariable<T> {
1187 fn clone(&self) -> Self {
1188 let clone_name = format!("{}_clone_{}", self.var_name, self.unique_id);
1190 Self::new(self.inner.clone(), clone_name)
1191 }
1192}
1193
1194#[doc(hidden)]
1199pub fn _track_var_impl<T: Trackable>(var: &T, var_name: &str) -> TrackingResult<()> {
1200 let tracker = crate::core::tracker::get_tracker();
1201
1202 if tracker.is_fast_mode() {
1204 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1205 let synthetic_ptr = 0x8000_0000 + unique_id;
1206 return tracker.fast_track_allocation(
1207 synthetic_ptr,
1208 var.get_size_estimate(),
1209 var_name.to_string(),
1210 );
1211 }
1212
1213 let type_name = var.get_type_name().to_string();
1214 let smart_pointer_type = smart_pointer_utils::detect_smart_pointer_type(&type_name);
1215 let is_smart_pointer = smart_pointer_type != smart_pointer_utils::SmartPointerType::None;
1216
1217 let ptr = if is_smart_pointer {
1219 let unique_id = TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1221 Some(smart_pointer_utils::generate_synthetic_pointer(
1222 smart_pointer_type,
1223 unique_id,
1224 ))
1225 } else {
1226 var.get_heap_ptr().or_else(|| {
1228 let unique_id =
1230 TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1231 Some(0x8000_0000 + unique_id)
1232 })
1233 };
1234
1235 if let Some(ptr_val) = ptr {
1236 let creation_time = std::time::SystemTime::now()
1237 .duration_since(std::time::UNIX_EPOCH)
1238 .unwrap_or_default()
1239 .as_nanos() as u64;
1240
1241 let _ = crate::variable_registry::VariableRegistry::register_variable(
1243 ptr_val,
1244 var_name.to_string(),
1245 type_name.clone(),
1246 var.get_size_estimate(),
1247 );
1248
1249 let scope_tracker = crate::core::scope_tracker::get_global_scope_tracker();
1251 let _ = scope_tracker.associate_variable(var_name.to_string(), var.get_size_estimate());
1252
1253 if is_smart_pointer {
1255 let ref_count = var.get_ref_count();
1257 let data_ptr = var.get_data_ptr();
1258
1259 let _ = tracker.create_smart_pointer_allocation(
1260 ptr_val,
1261 var.get_size_estimate(),
1262 var_name.to_string(),
1263 type_name.clone(),
1264 creation_time,
1265 ref_count,
1266 data_ptr,
1267 );
1268 tracing::debug!(
1269 "🎯 Created smart pointer tracking for '{}' at ptr 0x{:x}, ref_count={}",
1270 var_name,
1271 ptr_val,
1272 ref_count
1273 );
1274 } else if ptr_val >= 0x8000_0000 {
1275 let _ = tracker.create_synthetic_allocation(
1278 ptr_val,
1279 var.get_size_estimate(),
1280 var_name.to_string(),
1281 type_name.clone(),
1282 creation_time,
1283 );
1284
1285 tracing::debug!(
1286 "🎯 Created synthetic tracking for '{}' at ptr 0x{:x}",
1287 var_name,
1288 ptr_val
1289 );
1290 } else {
1291 tracker.associate_var(ptr_val, var_name.to_string(), type_name.clone())?;
1293
1294 tracing::debug!(
1295 "🎯 Associated variable '{}' of type '{}' at ptr 0x{:x}",
1296 var_name,
1297 type_name,
1298 ptr_val
1299 );
1300 }
1301 } else {
1302 tracing::debug!(
1304 "Variable '{}' could not be tracked (no pointer generated)",
1305 var_name
1306 );
1307 }
1308 Ok(())
1309}
1310
1311impl MemoryTracker {
1312 pub fn export_to_json_optimized<P: AsRef<std::path::Path>>(
1314 &self,
1315 path: P,
1316 ) -> TrackingResult<crate::export::complex_type_export::ComplexTypeExportResult> {
1317 use crate::export::complex_type_export::{
1318 export_comprehensive_analysis_optimized, ComplexTypeExportConfig,
1319 };
1320
1321 let path = path.as_ref();
1322 tracing::info!("🚀 Using optimized complex type export for maximum performance...");
1323
1324 let start_time = std::time::Instant::now();
1325
1326 let allocations = self.get_active_allocations()?;
1328 let stats = self.get_stats()?;
1329
1330 let analysis_manager = crate::analysis::AnalysisManager::new();
1332 let comprehensive_report =
1333 analysis_manager.perform_comprehensive_analysis(&allocations, &stats);
1334
1335 let config = ComplexTypeExportConfig {
1337 separate_complex_types: true,
1338 compress_data: false,
1339 chunk_size: 1000,
1340 pretty_format: false, };
1342
1343 let export_result = export_comprehensive_analysis_optimized(
1345 &comprehensive_report,
1346 &allocations,
1347 path,
1348 &config,
1349 )?;
1350
1351 let export_time = start_time.elapsed();
1352
1353 tracing::info!(
1355 "✅ Optimized export completed in {:.2}ms",
1356 export_time.as_millis()
1357 );
1358 tracing::info!(
1359 "📊 Performance improvement: {:.1}%",
1360 export_result.export_stats.performance_improvement
1361 );
1362 tracing::info!(
1363 "📁 Main file: {} ({} bytes)",
1364 export_result.main_file,
1365 export_result.export_stats.main_file_size
1366 );
1367
1368 if export_result.export_stats.complex_files_size > 0 {
1369 tracing::info!(
1370 "📁 Complex type files: {} bytes total",
1371 export_result.export_stats.complex_files_size
1372 );
1373
1374 if let Some(ref file) = export_result.complex_types_file {
1375 tracing::info!(" - Complex types: {}", file);
1376 }
1377 if let Some(ref file) = export_result.borrow_analysis_file {
1378 tracing::info!(" - Borrow analysis: {}", file);
1379 }
1380 if let Some(ref file) = export_result.async_analysis_file {
1381 tracing::info!(" - Async analysis: {}", file);
1382 }
1383 if let Some(ref file) = export_result.closure_analysis_file {
1384 tracing::info!(" - Closure analysis: {}", file);
1385 }
1386 if let Some(ref file) = export_result.lifecycle_analysis_file {
1387 tracing::info!(" - Lifecycle analysis: {}", file);
1388 }
1389 }
1390
1391 Ok(export_result)
1392 }
1393}
1394
1395#[doc(hidden)]
1398pub fn _smart_track_var_impl<T: Trackable + 'static>(var: T, var_name: &str) -> TrackingResult<T> {
1399 use std::any::TypeId;
1400
1401 let type_id = TypeId::of::<T>();
1402 let type_name = std::any::type_name::<T>();
1403
1404 let is_copy_type = type_id == TypeId::of::<i8>()
1406 || type_id == TypeId::of::<i16>()
1407 || type_id == TypeId::of::<i32>()
1408 || type_id == TypeId::of::<i64>()
1409 || type_id == TypeId::of::<i128>()
1410 || type_id == TypeId::of::<isize>()
1411 || type_id == TypeId::of::<u8>()
1412 || type_id == TypeId::of::<u16>()
1413 || type_id == TypeId::of::<u32>()
1414 || type_id == TypeId::of::<u64>()
1415 || type_id == TypeId::of::<u128>()
1416 || type_id == TypeId::of::<usize>()
1417 || type_id == TypeId::of::<f32>()
1418 || type_id == TypeId::of::<f64>()
1419 || type_id == TypeId::of::<bool>()
1420 || type_id == TypeId::of::<char>();
1421
1422 let is_smart_pointer = type_name.contains("::Rc<")
1423 || type_name.contains("::Arc<")
1424 || type_name.contains("::Weak<");
1425
1426 if is_copy_type {
1427 let _ = _track_var_impl(&var, var_name);
1429 tracing::debug!(
1430 "🧠 Smart tracking: Copy type '{}' tracked by reference",
1431 var_name
1432 );
1433 Ok(var)
1434 } else if is_smart_pointer {
1435 let _ = _track_var_impl(&var, var_name);
1437 tracing::debug!(
1438 "🧠 Smart tracking: Smart pointer '{}' tracked by reference",
1439 var_name
1440 );
1441 Ok(var)
1442 } else {
1443 let _ = _track_var_impl(&var, var_name);
1445 tracing::debug!(
1446 "🧠 Smart tracking: Non-Copy type '{}' tracked by reference",
1447 var_name
1448 );
1449 Ok(var)
1450 }
1451}
1452
1453pub fn init() {
1464 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1465
1466 let default_level = if cfg!(test) || std::env::var("MEMSCOPE_TEST_MODE").is_ok() {
1468 "memscope_rs=error" } else {
1470 "memscope_rs=info" };
1472
1473 tracing_subscriber::registry()
1474 .with(
1475 tracing_subscriber::EnvFilter::try_from_default_env()
1476 .unwrap_or_else(|_| default_level.into()),
1477 )
1478 .with(tracing_subscriber::fmt::layer())
1479 .init();
1480
1481 tracing::info!("memscope-rs initialized");
1482}
1483
1484pub fn init_for_testing() {
1487 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
1488
1489 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1491
1492 tracing_subscriber::registry()
1494 .with(
1495 tracing_subscriber::EnvFilter::try_from_default_env()
1496 .unwrap_or_else(|_| "memscope_rs=error".into()),
1497 )
1498 .with(tracing_subscriber::fmt::layer())
1499 .init();
1500
1501 tracing::debug!("memscope-rs initialized for testing");
1502}
1503
1504pub mod test_utils {
1506 pub fn init_test() {
1508 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1509 std::env::set_var("RUST_LOG", "error");
1510
1511 static INIT: std::sync::Once = std::sync::Once::new();
1513 INIT.call_once(|| {
1514 super::init_for_testing();
1515 });
1516
1517 let tracker = crate::core::tracker::get_tracker();
1519 tracker.enable_fast_mode();
1520 }
1521
1522 pub fn reset_tracker() {
1524 }
1527}
1528
1529#[macro_export]
1531macro_rules! init_test {
1532 () => {
1533 $crate::test_utils::init_test();
1534 };
1535}
1536
1537pub fn enable_auto_export(export_path: Option<&str>) {
1540 std::env::set_var("MEMSCOPE_AUTO_EXPORT", "1");
1541 if let Some(path) = export_path {
1542 std::env::set_var("MEMSCOPE_EXPORT_PATH", path);
1543 }
1544
1545 install_exit_hook();
1547
1548 tracing::info!(
1549 "📋 Auto-export enabled - JSON will be exported to: {}",
1550 export_path.unwrap_or("memscope_final_snapshot.json")
1551 );
1552}
1553
1554fn install_exit_hook() {
1556 use std::sync::Once;
1557 static HOOK_INSTALLED: Once = Once::new();
1558
1559 HOOK_INSTALLED.call_once(|| {
1560 let original_hook = std::panic::take_hook();
1562 std::panic::set_hook(Box::new(move |panic_info| {
1563 original_hook(panic_info);
1566 }));
1567
1568 extern "C" fn exit_handler() {
1570 }
1577
1578 unsafe {
1579 libc::atexit(exit_handler);
1580 }
1581
1582 tracing::debug!("📌 Exit hooks installed for automatic memory export");
1583 });
1584}
1585
1586#[cfg(test)]
1587mod test_unified_tracking;
1588
1589#[cfg(test)]
1590mod test_high_concurrency;
1591
1592#[cfg(test)]
1593mod tests {
1594 use super::*;
1595 use std::cell::{Cell, RefCell};
1596 use std::collections::{
1597 BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque,
1598 };
1599 use std::rc::{Rc, Weak as RcWeak};
1600 use std::sync::{Arc, Weak as ArcWeak};
1601 use std::sync::{Mutex, RwLock};
1602
1603 fn setup_test() {
1604 static INIT: std::sync::Once = std::sync::Once::new();
1606 INIT.call_once(|| {
1607 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1608 std::env::set_var("RUST_LOG", "error");
1609
1610 let _ = tracing_subscriber::fmt()
1612 .with_env_filter("error")
1613 .try_init();
1614 });
1615
1616 }
1618
1619 #[test]
1620 fn test_trackable_vec() {
1621 setup_test();
1622 let vec = vec![1, 2, 3, 4, 5];
1623
1624 assert!(vec.get_heap_ptr().is_some());
1626 assert_eq!(vec.get_type_name(), std::any::type_name::<Vec<i32>>());
1627 assert_eq!(
1628 vec.get_size_estimate(),
1629 vec.capacity() * std::mem::size_of::<i32>()
1630 );
1631 assert_eq!(vec.get_ref_count(), 1);
1632 assert_eq!(vec.get_data_ptr(), vec.get_heap_ptr().unwrap_or(0));
1633 assert!(vec.get_internal_allocations("test").is_empty());
1634 }
1635
1636 #[test]
1637 fn test_trackable_string() {
1638 setup_test();
1639 let s = String::from("Hello, World!");
1640
1641 assert!(s.get_heap_ptr().is_some());
1642 assert_eq!(s.get_type_name(), "String");
1643 assert_eq!(s.get_size_estimate(), s.capacity());
1644 assert_eq!(s.get_ref_count(), 1);
1645 }
1646
1647 #[test]
1648 fn test_trackable_box() {
1649 setup_test();
1650 let boxed = Box::new(42);
1651
1652 assert!(boxed.get_heap_ptr().is_some());
1653 assert_eq!(boxed.get_type_name(), std::any::type_name::<Box<i32>>());
1654 assert_eq!(boxed.get_size_estimate(), std::mem::size_of::<i32>());
1655 }
1656
1657 #[test]
1658 fn test_trackable_rc() {
1659 setup_test();
1660 let rc = Rc::new(vec![1, 2, 3]);
1661 let rc_clone = rc.clone();
1662
1663 assert!(rc.get_heap_ptr().is_some());
1664 assert_eq!(rc.get_type_name(), std::any::type_name::<Rc<Vec<i32>>>());
1665 assert_eq!(rc.get_ref_count(), 2); assert_eq!(rc.get_data_ptr(), Rc::as_ptr(&rc) as usize);
1667
1668 assert_eq!(rc.get_data_ptr(), rc_clone.get_data_ptr());
1670 }
1671
1672 #[test]
1673 fn test_trackable_arc() {
1674 setup_test();
1675 let arc = Arc::new(vec![1, 2, 3]);
1676 let arc_clone = arc.clone();
1677
1678 assert!(arc.get_heap_ptr().is_some());
1679 assert_eq!(arc.get_type_name(), std::any::type_name::<Arc<Vec<i32>>>());
1680 assert_eq!(arc.get_ref_count(), 2); assert_eq!(arc.get_data_ptr(), Arc::as_ptr(&arc) as usize);
1682
1683 assert_eq!(arc.get_data_ptr(), arc_clone.get_data_ptr());
1685 }
1686
1687 #[test]
1688 fn test_trackable_collections() {
1689 setup_test();
1690
1691 let mut map = HashMap::new();
1693 map.insert("key", "value");
1694 assert!(map.get_heap_ptr().is_some());
1695 assert_eq!(
1696 map.get_type_name(),
1697 std::any::type_name::<HashMap<&str, &str>>()
1698 );
1699
1700 let mut btree = BTreeMap::new();
1702 btree.insert(1, "one");
1703 assert!(btree.get_heap_ptr().is_some());
1704
1705 let mut set = HashSet::new();
1707 set.insert(42);
1708 assert!(set.get_heap_ptr().is_some());
1709
1710 let mut btree_set = BTreeSet::new();
1712 btree_set.insert(42);
1713 assert!(btree_set.get_heap_ptr().is_some());
1714
1715 let mut deque = VecDeque::new();
1717 deque.push_back(1);
1718 assert!(deque.get_heap_ptr().is_some());
1719
1720 let mut list = LinkedList::new();
1722 list.push_back(1);
1723 assert!(list.get_heap_ptr().is_some());
1724
1725 let mut heap = BinaryHeap::new();
1727 heap.push(1);
1728 assert!(heap.get_heap_ptr().is_some());
1729 }
1730
1731 #[test]
1732 fn test_trackable_weak_pointers() {
1733 setup_test();
1734
1735 let rc = Rc::new(42);
1737 let weak: RcWeak<i32> = Rc::downgrade(&rc);
1738 assert!(weak.get_heap_ptr().is_some());
1739 assert_eq!(weak.get_ref_count(), 1); assert_eq!(weak.get_data_ptr(), Rc::as_ptr(&rc) as usize);
1741
1742 let arc = Arc::new(42);
1744 let weak: ArcWeak<i32> = Arc::downgrade(&arc);
1745 assert!(weak.get_heap_ptr().is_some());
1746 assert_eq!(weak.get_ref_count(), 1); assert_eq!(weak.get_data_ptr(), Arc::as_ptr(&arc) as usize);
1748 }
1749
1750 #[test]
1751 fn test_trackable_option() {
1752 setup_test();
1753
1754 let some_vec = Some(vec![1, 2, 3]);
1755 let none_vec: Option<Vec<i32>> = None;
1756
1757 assert!(some_vec.get_heap_ptr().is_some());
1758 assert!(none_vec.get_heap_ptr().is_none());
1759
1760 assert_eq!(
1761 some_vec.get_type_name(),
1762 std::any::type_name::<Option<Vec<i32>>>()
1763 );
1764 assert_eq!(
1765 none_vec.get_type_name(),
1766 std::any::type_name::<Option<Vec<i32>>>()
1767 );
1768
1769 let allocations = some_vec.get_internal_allocations("test_var");
1771 assert_eq!(allocations.len(), 0); }
1774
1775 #[test]
1776 fn test_trackable_result() {
1777 setup_test();
1778
1779 let ok_result: Result<Vec<i32>, String> = Ok(vec![1, 2, 3]);
1780 let err_result: Result<Vec<i32>, String> = Err("error".to_string());
1781
1782 assert!(ok_result.get_heap_ptr().is_some());
1783 assert!(err_result.get_heap_ptr().is_some());
1784
1785 assert_eq!(
1786 ok_result.get_type_name(),
1787 std::any::type_name::<Result<Vec<i32>, String>>()
1788 );
1789 assert_eq!(
1790 err_result.get_type_name(),
1791 std::any::type_name::<Result<Vec<i32>, String>>()
1792 );
1793 }
1794
1795 #[test]
1796 fn test_trackable_tuple() {
1797 setup_test();
1798
1799 let tuple = (vec![1, 2, 3], String::from("hello"), Box::new(42));
1800
1801 assert!(tuple.get_heap_ptr().is_some());
1802 assert_eq!(
1803 tuple.get_type_name(),
1804 std::any::type_name::<(Vec<i32>, String, Box<i32>)>()
1805 );
1806
1807 let expected_size =
1809 tuple.0.get_size_estimate() + tuple.1.get_size_estimate() + tuple.2.get_size_estimate();
1810 assert_eq!(tuple.get_size_estimate(), expected_size);
1811 }
1812
1813 #[test]
1814 fn test_smart_pointer_utils() {
1815 use smart_pointer_utils::*;
1816
1817 assert_eq!(
1819 detect_smart_pointer_type("std::rc::Rc<i32>"),
1820 SmartPointerType::Rc
1821 );
1822 assert_eq!(
1823 detect_smart_pointer_type("std::sync::Arc<String>"),
1824 SmartPointerType::Arc
1825 );
1826 assert_eq!(
1827 detect_smart_pointer_type("std::boxed::Box<Vec<i32>>"),
1828 SmartPointerType::Box
1829 );
1830 assert_eq!(
1831 detect_smart_pointer_type("Vec<i32>"),
1832 SmartPointerType::None
1833 );
1834
1835 assert!(is_smart_pointer("std::rc::Rc<i32>"));
1837 assert!(is_smart_pointer("std::sync::Arc<String>"));
1838 assert!(is_smart_pointer("std::boxed::Box<Vec<i32>>"));
1839 assert!(!is_smart_pointer("Vec<i32>"));
1840
1841 assert_eq!(
1843 generate_synthetic_pointer(SmartPointerType::Rc, 123),
1844 0x5000_0000 + 123
1845 );
1846 assert_eq!(
1847 generate_synthetic_pointer(SmartPointerType::Arc, 456),
1848 0x6000_0000 + 456
1849 );
1850 assert_eq!(
1851 generate_synthetic_pointer(SmartPointerType::Box, 789),
1852 0x7000_0000 + 789
1853 );
1854 }
1855
1856 #[test]
1857 fn test_tracked_variable_basic() {
1858 setup_test();
1859
1860 let vec = vec![1, 2, 3, 4, 5];
1862
1863 assert_eq!(vec.len(), 5);
1866 assert_eq!(vec[0], 1);
1867 assert!(vec.get_heap_ptr().is_some());
1868 assert_eq!(vec.get_type_name(), std::any::type_name::<Vec<i32>>());
1869 }
1870
1871 #[test]
1872 fn test_tracked_variable_smart_pointer() {
1873 setup_test();
1874
1875 let rc = Rc::new(vec![1, 2, 3]);
1877
1878 assert_eq!(rc.len(), 3);
1879 assert_eq!(rc[0], 1);
1880 assert!(rc.get_heap_ptr().is_some());
1881 assert_eq!(rc.get_type_name(), std::any::type_name::<Rc<Vec<i32>>>());
1882 assert_eq!(rc.get_ref_count(), 1);
1883 }
1884
1885 #[test]
1886 fn test_tracked_variable_into_inner() {
1887 setup_test();
1888
1889 let vec = vec![1, 2, 3, 4, 5];
1891
1892 assert_eq!(vec.len(), 5);
1894 assert_eq!(vec[0], 1);
1895
1896 let moved_vec = vec;
1898 assert_eq!(moved_vec.len(), 5);
1899 assert_eq!(moved_vec[0], 1);
1900 }
1901
1902 #[test]
1903 fn test_tracked_variable_clone() {
1904 setup_test();
1905
1906 let vec = vec![1, 2, 3];
1908 let cloned_vec = vec.clone();
1909
1910 assert_eq!(vec.len(), cloned_vec.len());
1911 assert_eq!(vec[0], cloned_vec[0]);
1912
1913 assert!(vec.get_heap_ptr().is_some());
1915 assert!(cloned_vec.get_heap_ptr().is_some());
1916 }
1917
1918 #[test]
1919 fn test_track_var_macro() {
1920 setup_test();
1921
1922 let vec = vec![1, 2, 3, 4, 5];
1923
1924 assert_eq!(vec.len(), 5);
1927 assert_eq!(vec[0], 1);
1928
1929 assert!(vec.get_heap_ptr().is_some());
1931 assert_eq!(vec.get_type_name(), std::any::type_name::<Vec<i32>>());
1932 }
1933
1934 #[test]
1935 fn test_track_var_owned_macro() {
1936 setup_test();
1937
1938 let vec = vec![1, 2, 3, 4, 5];
1940
1941 assert_eq!(vec.len(), 5);
1943 assert_eq!(vec[0], 1);
1944 assert!(vec.get_heap_ptr().is_some());
1945 assert_eq!(vec.get_type_name(), std::any::type_name::<Vec<i32>>());
1946 }
1947
1948 #[test]
1949 fn test_track_var_smart_macro() {
1950 setup_test();
1951
1952 let number = 42i32;
1954 assert_eq!(number, 42);
1955 assert!(number.get_heap_ptr().is_some());
1956
1957 let vec = vec![1, 2, 3];
1959 assert_eq!(vec.len(), 3);
1960 assert!(vec.get_heap_ptr().is_some());
1961
1962 let rc = Rc::new(vec![1, 2, 3]);
1964 assert_eq!(rc.len(), 3);
1965 assert!(rc.get_heap_ptr().is_some());
1966 assert_eq!(rc.get_ref_count(), 1);
1967 }
1968
1969 #[test]
1970 fn test_init_functions() {
1971 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1973 std::env::set_var("RUST_LOG", "error");
1974
1975 assert_eq!(std::env::var("MEMSCOPE_TEST_MODE").unwrap(), "1");
1976 assert_eq!(std::env::var("RUST_LOG").unwrap(), "error");
1977
1978 let _ = std::panic::catch_unwind(|| {
1981 });
1983 }
1984
1985 #[test]
1986 fn test_enable_auto_export() {
1987 setup_test();
1988
1989 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
1991
1992 enable_auto_export(Some("test_export"));
1994 assert_eq!(std::env::var("MEMSCOPE_AUTO_EXPORT").unwrap(), "1");
1995 assert_eq!(
1996 std::env::var("MEMSCOPE_EXPORT_PATH").unwrap(),
1997 "test_export"
1998 );
1999
2000 std::env::remove_var("MEMSCOPE_EXPORT_PATH");
2002 enable_auto_export(None);
2003 assert_eq!(std::env::var("MEMSCOPE_AUTO_EXPORT").unwrap(), "1");
2004 assert!(std::env::var("MEMSCOPE_EXPORT_PATH").is_err());
2005
2006 std::env::remove_var("MEMSCOPE_TEST_MODE");
2008 std::env::remove_var("MEMSCOPE_AUTO_EXPORT");
2009 }
2010
2011 #[test]
2012 fn test_export_final_snapshot() {
2013 setup_test();
2014
2015 let temp_path = "tmp_rovodev_test_export";
2017
2018 assert!(!temp_path.is_empty());
2020 assert!(!temp_path.is_empty());
2021
2022 let json_path = format!("{temp_path}.json");
2024 let html_path = format!("{temp_path}.html");
2025
2026 assert!(json_path.ends_with(".json"));
2027 assert!(html_path.ends_with(".html"));
2028
2029 }
2031
2032 #[test]
2033 fn test_advanced_type_implementations() {
2034 setup_test();
2035
2036 let cell = RefCell::new(42);
2038 assert!(cell.get_heap_ptr().is_some());
2039 assert_eq!(cell.get_type_name(), std::any::type_name::<RefCell<i32>>());
2040
2041 let cell = Cell::new(42);
2043 assert!(cell.get_heap_ptr().is_some());
2044 assert_eq!(cell.get_type_name(), std::any::type_name::<Cell<i32>>());
2045
2046 let mutex = Mutex::new(42);
2048 assert!(mutex.get_heap_ptr().is_some());
2049 assert_eq!(mutex.get_type_name(), std::any::type_name::<Mutex<i32>>());
2050
2051 let rwlock = RwLock::new(42);
2053 assert!(rwlock.get_heap_ptr().is_some());
2054 assert_eq!(rwlock.get_type_name(), std::any::type_name::<RwLock<i32>>());
2055 }
2056
2057 #[test]
2058 fn test_memory_tracker_optimized_export() {
2059 setup_test();
2060
2061 let temp_path = std::path::Path::new("tmp_rovodev_optimized_export.json");
2066 assert!(temp_path.to_str().is_some());
2067
2068 }
2071
2072 #[test]
2073 fn test_smart_track_var_impl_copy_types() {
2074 setup_test();
2075
2076 let i32_val = 42i32;
2078 let u32_val = 42u32;
2079 let f64_val = std::f64::consts::PI;
2080 let bool_val = true;
2081 let char_val = 'a';
2082
2083 assert!(i32_val.get_heap_ptr().is_some());
2084 assert!(u32_val.get_heap_ptr().is_some());
2085 assert!(f64_val.get_heap_ptr().is_some());
2086 assert!(bool_val.get_heap_ptr().is_some());
2087 assert!(char_val.get_heap_ptr().is_some());
2088
2089 assert_eq!(i32_val.get_type_name(), "i32");
2090 assert_eq!(u32_val.get_type_name(), "u32");
2091 assert_eq!(f64_val.get_type_name(), "f64");
2092 assert_eq!(bool_val.get_type_name(), "bool");
2093 assert_eq!(char_val.get_type_name(), "char");
2094 }
2095
2096 #[test]
2097 fn test_trackable_advanced_type_info() {
2098 setup_test();
2099
2100 let vec = vec![1, 2, 3];
2101 let type_info = vec.get_advanced_type_info();
2102
2103 assert!(type_info.is_none());
2106 }
2107
2108 #[test]
2109 fn test_trackable_default_methods() {
2110 setup_test();
2111
2112 let vec = vec![1, 2, 3];
2113
2114 vec.track_clone_relationship(0x1000, 0x2000); vec.update_ref_count_tracking(0x1000); }
2120
2121 #[test]
2122 fn test_init_test_macro() {
2123 std::env::set_var("MEMSCOPE_TEST_MODE", "1");
2128 std::env::set_var("RUST_LOG", "error");
2129
2130 assert_eq!(std::env::var("MEMSCOPE_TEST_MODE").unwrap(), "1");
2131 assert_eq!(std::env::var("RUST_LOG").unwrap(), "error");
2132 }
2133
2134 #[test]
2135 fn test_tracked_variable_counter() {
2136 setup_test();
2137
2138 let initial_count = TRACKED_VARIABLE_COUNTER.load(std::sync::atomic::Ordering::Relaxed);
2140
2141 TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2143 TRACKED_VARIABLE_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
2144
2145 let final_count = TRACKED_VARIABLE_COUNTER.load(std::sync::atomic::Ordering::Relaxed);
2146
2147 assert!(final_count >= initial_count + 2);
2149 }
2150
2151 #[test]
2152 fn test_empty_collections() {
2153 setup_test();
2154
2155 let empty_vec: Vec<i32> = Vec::new();
2157 assert!(empty_vec.get_heap_ptr().is_none());
2158
2159 let empty_map: HashMap<i32, String> = HashMap::new();
2160 assert!(empty_map.get_heap_ptr().is_some()); let empty_btree: BTreeMap<i32, String> = BTreeMap::new();
2163 assert!(empty_btree.get_heap_ptr().is_none());
2164
2165 let empty_set: HashSet<i32> = HashSet::new();
2166 assert!(empty_set.get_heap_ptr().is_none());
2167
2168 let empty_btree_set: BTreeSet<i32> = BTreeSet::new();
2169 assert!(empty_btree_set.get_heap_ptr().is_none());
2170
2171 let empty_list: LinkedList<i32> = LinkedList::new();
2172 assert!(empty_list.get_heap_ptr().is_none());
2173
2174 let empty_heap: BinaryHeap<i32> = BinaryHeap::new();
2175 assert!(empty_heap.get_heap_ptr().is_none());
2176 }
2177}