memscope_rs/
advanced_trackable_macro.rs

1//! Macro for implementing Trackable for advanced types
2//!
3//! This module provides a macro that automatically implements Trackable
4//! for advanced Rust types with minimal boilerplate code.
5
6/// Macro to implement Trackable for advanced types with automatic analysis
7#[macro_export]
8macro_rules! impl_advanced_trackable {
9    ($type:ty, $offset:expr) => {
10        impl<T> $crate::Trackable for $type {
11            fn get_heap_ptr(&self) -> Option<usize> {
12                // Use unique offset for this type category
13                let instance_ptr = self as *const _ as usize;
14                Some($offset + (instance_ptr % 0x0FFF_FFFF))
15            }
16
17            fn get_type_name(&self) -> &'static str {
18                std::any::type_name::<$type>()
19            }
20
21            fn get_size_estimate(&self) -> usize {
22                std::mem::size_of::<$type>()
23            }
24
25            fn get_advanced_type_info(&self) -> Option<$crate::advanced_types::AdvancedTypeInfo> {
26                let type_name = self.get_type_name();
27                let allocation = $crate::core::types::AllocationInfo {
28                    ptr: self.get_heap_ptr().unwrap_or(0),
29                    size: self.get_size_estimate(),
30                    var_name: None,
31                    type_name: Some(type_name.to_string()),
32                    scope_name: None,
33                    timestamp_alloc: std::time::SystemTime::now()
34                        .duration_since(std::time::UNIX_EPOCH)
35                        .unwrap_or_default()
36                        .as_nanos() as u64,
37                    timestamp_dealloc: None,
38                    thread_id: format!("{:?}", std::thread::current().id()),
39                    borrow_count: 0,
40                    stack_trace: None,
41                    is_leaked: false,
42                    lifetime_ms: None,
43                    borrow_info: None,
44                    clone_info: None,
45                    ownership_history_available: false,
46                    smart_pointer_info: None,
47                    memory_layout: None,
48                    generic_info: None,
49                    dynamic_type_info: None,
50                    runtime_state: None,
51                    stack_allocation: None,
52                    temporary_object: None,
53                    fragmentation_analysis: None,
54                    generic_instantiation: None,
55                    type_relationships: None,
56                    type_usage: None,
57                    function_call_tracking: None,
58                    lifecycle_tracking: None,
59                    access_tracking: None,
60                    drop_chain_analysis: None,
61                };
62
63                Some(
64                    $crate::advanced_types::GenericAdvancedTypeAnalyzer::analyze_by_type_name(
65                        type_name,
66                        &allocation,
67                    ),
68                )
69            }
70        }
71    };
72
73    // Variant for types without generics
74    ($type:ty, $offset:expr, no_generics) => {
75        impl $crate::Trackable for $type {
76            fn get_heap_ptr(&self) -> Option<usize> {
77                let instance_ptr = self as *const _ as usize;
78                Some($offset + (instance_ptr % 0x0FFF_FFFF))
79            }
80
81            fn get_type_name(&self) -> &'static str {
82                std::any::type_name::<$type>()
83            }
84
85            fn get_size_estimate(&self) -> usize {
86                std::mem::size_of::<$type>()
87            }
88
89            fn get_advanced_type_info(&self) -> Option<$crate::advanced_types::AdvancedTypeInfo> {
90                let type_name = self.get_type_name();
91                let allocation = $crate::core::types::AllocationInfo {
92                    ptr: self.get_heap_ptr().unwrap_or(0),
93                    size: self.get_size_estimate(),
94                    var_name: None,
95                    type_name: Some(type_name.to_string()),
96                    scope_name: None,
97                    timestamp_alloc: std::time::SystemTime::now()
98                        .duration_since(std::time::UNIX_EPOCH)
99                        .unwrap_or_default()
100                        .as_nanos() as u64,
101                    timestamp_dealloc: None,
102                    thread_id: format!("{:?}", std::thread::current().id()),
103                    borrow_count: 0,
104                    stack_trace: None,
105                    is_leaked: false,
106                    lifetime_ms: None,
107                    borrow_info: None,
108                    clone_info: None,
109                    ownership_history_available: false,
110                    smart_pointer_info: None,
111                    memory_layout: None,
112                    generic_info: None,
113                    dynamic_type_info: None,
114                    runtime_state: None,
115                    stack_allocation: None,
116                    temporary_object: None,
117                    fragmentation_analysis: None,
118                    generic_instantiation: None,
119                    type_relationships: None,
120                    type_usage: None,
121                    function_call_tracking: None,
122                    lifecycle_tracking: None,
123                    access_tracking: None,
124                    drop_chain_analysis: None,
125                };
126
127                Some(
128                    $crate::advanced_types::GenericAdvancedTypeAnalyzer::analyze_by_type_name(
129                        type_name,
130                        &allocation,
131                    ),
132                )
133            }
134        }
135    };
136}
137
138#[cfg(test)]
139mod tests {
140    use crate::Trackable;
141    use std::cell::RefCell;
142    use std::sync::Mutex;
143
144    // Test struct for generic macro variant
145    struct TestGenericStruct<T> {
146        #[allow(dead_code)]
147        data: T,
148    }
149
150    // Test struct for non-generic macro variant
151    struct TestSimpleStruct {
152        #[allow(dead_code)]
153        value: i32,
154    }
155
156    // Apply the macro to test structs
157    impl_advanced_trackable!(TestGenericStruct<T>, 0x1000_0000);
158    impl_advanced_trackable!(TestSimpleStruct, 0x2000_0000, no_generics);
159
160    #[test]
161    fn test_generic_macro_implementation() {
162        let test_struct = TestGenericStruct { data: 42i32 };
163
164        // Test get_heap_ptr
165        let heap_ptr = test_struct.get_heap_ptr();
166        assert!(heap_ptr.is_some());
167        let ptr = heap_ptr.unwrap();
168        assert!(ptr >= 0x1000_0000);
169        assert!(ptr < 0x1000_0000 + 0x0FFF_FFFF);
170
171        // Test get_type_name
172        let type_name = test_struct.get_type_name();
173        assert!(type_name.contains("TestGenericStruct"));
174        assert!(type_name.contains("i32"));
175
176        // Test get_size_estimate
177        let size = test_struct.get_size_estimate();
178        assert_eq!(size, std::mem::size_of::<TestGenericStruct<i32>>());
179    }
180
181    #[test]
182    fn test_non_generic_macro_implementation() {
183        let test_struct = TestSimpleStruct { value: 123 };
184
185        // Test get_heap_ptr
186        let heap_ptr = test_struct.get_heap_ptr();
187        assert!(heap_ptr.is_some());
188        let ptr = heap_ptr.unwrap();
189        assert!(ptr >= 0x2000_0000);
190        assert!(ptr < 0x2000_0000 + 0x0FFF_FFFF);
191
192        // Test get_type_name
193        let type_name = test_struct.get_type_name();
194        assert!(type_name.contains("TestSimpleStruct"));
195
196        // Test get_size_estimate
197        let size = test_struct.get_size_estimate();
198        assert_eq!(size, std::mem::size_of::<TestSimpleStruct>());
199    }
200
201    #[test]
202    fn test_advanced_type_info_generation() {
203        let test_struct = TestGenericStruct {
204            data: "test".to_string(),
205        };
206
207        // Test get_advanced_type_info
208        let advanced_info = test_struct.get_advanced_type_info();
209        assert!(advanced_info.is_some());
210
211        let info = advanced_info.unwrap();
212        // The analyzer should categorize this as a generic type
213        assert!(!format!("{:?}", info.category).is_empty());
214        // May or may not have issues - length is always >= 0 for Vec
215        assert!(info.performance_info.overhead_factor >= 1.0);
216    }
217
218    #[test]
219    fn test_macro_with_different_offsets() {
220        let generic_struct = TestGenericStruct { data: 100u64 };
221        let simple_struct = TestSimpleStruct { value: 200 };
222
223        let generic_ptr = generic_struct.get_heap_ptr().unwrap();
224        let simple_ptr = simple_struct.get_heap_ptr().unwrap();
225
226        // Ensure different offsets produce different pointer ranges
227        assert!((0x1000_0000..0x1000_0000 + 0x0FFF_FFFF).contains(&generic_ptr));
228        assert!((0x2000_0000..0x2000_0000 + 0x0FFF_FFFF).contains(&simple_ptr));
229
230        // The base ranges should be different
231        assert!((generic_ptr & 0xF000_0000) != (simple_ptr & 0xF000_0000));
232    }
233
234    #[test]
235    fn test_allocation_info_creation() {
236        let test_struct = TestGenericStruct {
237            data: RefCell::new(42),
238        };
239
240        let advanced_info = test_struct.get_advanced_type_info().unwrap();
241
242        // Verify that AllocationInfo is properly created
243        // Note: extract_state_info returns None for borrow_count by default
244        // This is expected behavior as mentioned in the implementation
245        assert!(advanced_info.state_info.is_borrowed.is_none());
246        assert!(advanced_info.state_info.is_locked.is_none());
247
248        // Check that timestamp is reasonable (not zero)
249        // We can't check exact value but ensure it's recent
250        let type_name = test_struct.get_type_name();
251        assert!(!type_name.is_empty());
252    }
253
254    #[test]
255    fn test_macro_with_interior_mutability_types() {
256        // Test with RefCell
257        struct RefCellStruct {
258            #[allow(dead_code)]
259            data: RefCell<i32>,
260        }
261        impl_advanced_trackable!(RefCellStruct, 0x3000_0000, no_generics);
262
263        let refcell_struct = RefCellStruct {
264            data: RefCell::new(42),
265        };
266
267        let type_name = refcell_struct.get_type_name();
268        assert!(type_name.contains("RefCellStruct"));
269
270        let advanced_info = refcell_struct.get_advanced_type_info();
271        assert!(advanced_info.is_some());
272    }
273
274    #[test]
275    fn test_macro_with_sync_types() {
276        // Test with Mutex
277        struct MutexStruct {
278            #[allow(dead_code)]
279            data: Mutex<String>,
280        }
281        impl_advanced_trackable!(MutexStruct, 0x4000_0000, no_generics);
282
283        let mutex_struct = MutexStruct {
284            data: Mutex::new("test".to_string()),
285        };
286
287        let type_name = mutex_struct.get_type_name();
288        assert!(type_name.contains("MutexStruct"));
289
290        let heap_ptr = mutex_struct.get_heap_ptr().unwrap();
291        assert!(heap_ptr >= 0x4000_0000);
292
293        let advanced_info = mutex_struct.get_advanced_type_info();
294        assert!(advanced_info.is_some());
295    }
296
297    #[test]
298    fn test_unique_pointer_generation() {
299        // Create multiple instances and ensure they get different pointers
300        let struct1 = TestSimpleStruct { value: 1 };
301        let struct2 = TestSimpleStruct { value: 2 };
302        let struct3 = TestSimpleStruct { value: 3 };
303
304        let ptr1 = struct1.get_heap_ptr().unwrap();
305        let ptr2 = struct2.get_heap_ptr().unwrap();
306        let ptr3 = struct3.get_heap_ptr().unwrap();
307
308        // All should be in the same range but different values
309        assert!(ptr1 >= 0x2000_0000);
310        assert!(ptr2 >= 0x2000_0000);
311        assert!(ptr3 >= 0x2000_0000);
312
313        // They should be different (very high probability)
314        assert!(ptr1 != ptr2 || ptr2 != ptr3 || ptr1 != ptr3);
315    }
316
317    #[test]
318    fn test_generic_type_with_different_parameters() {
319        let int_struct = TestGenericStruct { data: 42i32 };
320        let string_struct = TestGenericStruct {
321            data: "hello".to_string(),
322        };
323        let vec_struct = TestGenericStruct {
324            data: vec![1, 2, 3],
325        };
326
327        // All should have different type names
328        let int_type = int_struct.get_type_name();
329        let string_type = string_struct.get_type_name();
330        let vec_type = vec_struct.get_type_name();
331
332        assert!(int_type.contains("i32"));
333        assert!(string_type.contains("String"));
334        assert!(vec_type.contains("Vec"));
335
336        // All should be in the same pointer range
337        let int_ptr = int_struct.get_heap_ptr().unwrap();
338        let string_ptr = string_struct.get_heap_ptr().unwrap();
339        let vec_ptr = vec_struct.get_heap_ptr().unwrap();
340
341        assert!((0x1000_0000..0x1000_0000 + 0x0FFF_FFFF).contains(&int_ptr));
342        assert!((0x1000_0000..0x1000_0000 + 0x0FFF_FFFF).contains(&string_ptr));
343        assert!((0x1000_0000..0x1000_0000 + 0x0FFF_FFFF).contains(&vec_ptr));
344    }
345
346    #[test]
347    fn test_size_estimation_accuracy() {
348        let simple_struct = TestSimpleStruct { value: 42 };
349        let generic_struct = TestGenericStruct { data: 123u64 };
350
351        let simple_size = simple_struct.get_size_estimate();
352        let generic_size = generic_struct.get_size_estimate();
353
354        // Verify sizes match actual struct sizes
355        assert_eq!(simple_size, std::mem::size_of::<TestSimpleStruct>());
356        assert_eq!(generic_size, std::mem::size_of::<TestGenericStruct<u64>>());
357
358        // Simple struct should be smaller (i32 vs u64)
359        assert!(simple_size <= generic_size);
360    }
361
362    #[test]
363    fn test_macro_thread_safety() {
364        use std::sync::atomic::{AtomicUsize, Ordering};
365        use std::sync::Arc;
366        use std::thread;
367
368        let counter = Arc::new(AtomicUsize::new(0));
369        let mut handles = vec![];
370
371        // Create multiple threads that use the macro
372        for _ in 0..4 {
373            let counter_clone = counter.clone();
374            let handle = thread::spawn(move || {
375                let test_struct = TestSimpleStruct { value: 42 };
376                let _ptr = test_struct.get_heap_ptr();
377                let _type_name = test_struct.get_type_name();
378                let _size = test_struct.get_size_estimate();
379                counter_clone.fetch_add(1, Ordering::SeqCst);
380            });
381            handles.push(handle);
382        }
383
384        // Wait for all threads to complete
385        for handle in handles {
386            handle.join().unwrap();
387        }
388
389        // Verify all threads completed successfully
390        assert_eq!(counter.load(Ordering::SeqCst), 4);
391    }
392
393    #[test]
394    fn test_allocation_info_fields() {
395        let test_struct = TestGenericStruct {
396            data: vec![1, 2, 3],
397        };
398        let advanced_info = test_struct.get_advanced_type_info().unwrap();
399
400        // Verify AllocationInfo has expected structure
401        // Note: extract_state_info returns None for most fields by default
402        // This is expected behavior as runtime introspection is limited
403        assert!(advanced_info.state_info.is_borrowed.is_none());
404        assert!(advanced_info.state_info.borrow_count.is_none());
405        assert!(advanced_info.state_info.is_locked.is_none());
406
407        // Check performance info is reasonable
408        assert!(advanced_info.performance_info.overhead_factor >= 1.0);
409        // Memory overhead is always >= 0 for usize
410
411        // Verify category and behavior are set
412        assert!(!format!("{:?}", advanced_info.category).is_empty());
413        assert!(!format!("{:?}", advanced_info.behavior).is_empty());
414    }
415}