memscope_rs/core/
optimized_tracker.rs1use crate::core::clone_monitor;
8use crate::core::optimized_types::OptimizedAllocationInfo;
9use crate::core::shared_types::{SharedAllocationCollection, SharedAllocationInfo};
10use crate::core::types::{MemoryStats, TrackingResult};
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex};
13
14pub struct OptimizedMemoryTracker {
16 active_allocations: Mutex<HashMap<usize, Arc<OptimizedAllocationInfo>>>,
18 stats: Mutex<MemoryStats>,
20 allocation_history: Mutex<Vec<Arc<OptimizedAllocationInfo>>>,
22}
23
24impl OptimizedMemoryTracker {
25 pub fn new() -> Self {
27 Self {
28 active_allocations: Mutex::new(HashMap::new()),
29 stats: Mutex::new(MemoryStats::default()),
30 allocation_history: Mutex::new(Vec::new()),
31 }
32 }
33
34 pub fn track_allocation_shared(&self, ptr: usize, size: usize) -> TrackingResult<()> {
36 let allocation = OptimizedAllocationInfo::new(ptr, size);
37 let shared_allocation = Arc::new(allocation);
38
39 clone_monitor::record_avoided_clone("AllocationInfo", size);
41
42 match (self.active_allocations.try_lock(), self.stats.try_lock()) {
43 (Ok(mut active), Ok(mut stats)) => {
44 active.insert(ptr, Arc::clone(&shared_allocation));
46
47 stats.total_allocations = stats.total_allocations.saturating_add(1);
49 stats.active_allocations = stats.active_allocations.saturating_add(1);
50 stats.active_memory = stats.active_memory.saturating_add(size);
51
52 if let Ok(mut history) = self.allocation_history.try_lock() {
54 history.push(shared_allocation);
55 }
56
57 Ok(())
58 }
59 _ => Err(crate::core::types::TrackingError::ThreadSafetyError(
60 "Lock contention".to_string(),
61 )),
62 }
63 }
64
65 pub fn get_active_allocations_shared(&self) -> TrackingResult<SharedAllocationCollection> {
67 match self.active_allocations.lock() {
68 Ok(active) => {
69 let shared_infos: Vec<SharedAllocationInfo> = active
70 .values()
71 .map(|arc_info| SharedAllocationInfo::new((**arc_info).clone()))
72 .collect();
73
74 Ok(SharedAllocationCollection::new(shared_infos))
75 }
76 Err(_) => Err(crate::core::types::TrackingError::ThreadSafetyError(
77 "Lock contention".to_string(),
78 )),
79 }
80 }
81
82 pub fn get_allocation_history_shared(&self) -> TrackingResult<SharedAllocationCollection> {
84 match self.allocation_history.lock() {
85 Ok(history) => {
86 let shared_infos: Vec<SharedAllocationInfo> = history
87 .iter()
88 .map(|arc_info| SharedAllocationInfo::new((**arc_info).clone()))
89 .collect();
90
91 Ok(SharedAllocationCollection::new(shared_infos))
92 }
93 Err(_) => Err(crate::core::types::TrackingError::ThreadSafetyError(
94 "Lock contention".to_string(),
95 )),
96 }
97 }
98
99 pub fn associate_var_shared(
101 &self,
102 ptr: usize,
103 var_name: String,
104 type_name: String,
105 ) -> TrackingResult<()> {
106 match self.active_allocations.lock() {
107 Ok(mut active) => {
108 if let Some(arc_allocation) = active.get_mut(&ptr) {
109 let mut updated_allocation = (**arc_allocation).clone();
111 updated_allocation.var_name =
112 Some(crate::core::string_pool::intern_string(&var_name));
113 updated_allocation.type_name =
114 Some(crate::core::string_pool::intern_string(&type_name));
115
116 *arc_allocation = Arc::new(updated_allocation);
118
119 Ok(())
120 } else {
121 Err(crate::core::types::TrackingError::InvalidPointer(
122 "Allocation not found".to_string(),
123 ))
124 }
125 }
126 Err(_) => Err(crate::core::types::TrackingError::ThreadSafetyError(
127 "Lock contention".to_string(),
128 )),
129 }
130 }
131
132 pub fn get_stats(&self) -> TrackingResult<MemoryStats> {
134 match self.stats.lock() {
135 Ok(stats) => Ok(stats.clone()),
136 Err(_) => Err(crate::core::types::TrackingError::ThreadSafetyError(
137 "Lock contention".to_string(),
138 )),
139 }
140 }
141
142 pub fn get_clone_stats(&self) -> clone_monitor::CloneMonitorStats {
144 clone_monitor::get_clone_stats()
145 }
146}
147
148impl Default for OptimizedMemoryTracker {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_new_optimized_tracker() {
160 let tracker = OptimizedMemoryTracker::new();
162 let stats = tracker.get_stats().expect("Failed to get stats");
163
164 assert_eq!(stats.active_allocations, 0);
165 assert_eq!(stats.active_memory, 0);
166 assert_eq!(stats.total_allocations, 0);
167 }
168
169 #[test]
170 fn test_track_allocation_shared() {
171 let tracker = OptimizedMemoryTracker::new();
173 let ptr = 0x1000;
174 let size = 1024;
175
176 tracker
177 .track_allocation_shared(ptr, size)
178 .expect("Failed to track allocation");
179
180 let stats = tracker.get_stats().expect("Failed to get stats");
181 assert_eq!(stats.active_allocations, 1);
182 assert_eq!(stats.active_memory, size);
183 assert_eq!(stats.total_allocations, 1);
184 }
185
186 #[test]
187 fn test_track_multiple_allocations() {
188 let tracker = OptimizedMemoryTracker::new();
190
191 for i in 0..5 {
192 let ptr = 0x1000 + (i * 0x100);
193 let size = 256 * (i + 1);
194
195 tracker
196 .track_allocation_shared(ptr, size)
197 .expect("Failed to track allocation");
198 }
199
200 let stats = tracker.get_stats().expect("Failed to get stats");
201 assert_eq!(stats.active_allocations, 5);
202 assert_eq!(stats.total_allocations, 5);
203
204 assert_eq!(stats.active_memory, 3840);
206 }
207
208 #[test]
209 fn test_get_active_allocations_shared() {
210 let tracker = OptimizedMemoryTracker::new();
212
213 tracker
215 .track_allocation_shared(0x2000, 512)
216 .expect("Failed to track allocation");
217 tracker
218 .track_allocation_shared(0x3000, 1024)
219 .expect("Failed to track allocation");
220
221 let shared_collection = tracker
222 .get_active_allocations_shared()
223 .expect("Failed to get active allocations");
224
225 assert_eq!(shared_collection.len(), 2);
226
227 let ptrs: Vec<usize> = shared_collection.iter().map(|info| info.ptr()).collect();
229 assert!(ptrs.contains(&0x2000));
230 assert!(ptrs.contains(&0x3000));
231 }
232
233 #[test]
234 fn test_get_allocation_history_shared() {
235 let tracker = OptimizedMemoryTracker::new();
237
238 for i in 0..3 {
240 tracker
241 .track_allocation_shared(0x4000 + i * 0x100, 256)
242 .expect("Failed to track allocation");
243 }
244
245 let history = tracker
246 .get_allocation_history_shared()
247 .expect("Failed to get allocation history");
248
249 assert_eq!(history.len(), 3);
250 }
251
252 #[test]
253 fn test_associate_var_shared() {
254 let tracker = OptimizedMemoryTracker::new();
256 let ptr = 0x5000;
257
258 tracker
260 .track_allocation_shared(ptr, 1024)
261 .expect("Failed to track allocation");
262
263 tracker
265 .associate_var_shared(ptr, "test_variable".to_string(), "TestType".to_string())
266 .expect("Failed to associate variable");
267
268 let allocations = tracker
270 .get_active_allocations_shared()
271 .expect("Failed to get allocations");
272
273 let found = allocations.iter().find(|info| info.ptr() == ptr);
274
275 assert!(found.is_some());
276 let allocation = found.unwrap();
277 assert!(allocation.var_name_str().is_some());
278 assert!(allocation.type_name_str().is_some());
279 }
280
281 #[test]
282 fn test_associate_var_non_existing_allocation() {
283 let tracker = OptimizedMemoryTracker::new();
285
286 let result = tracker.associate_var_shared(
287 0x9999,
288 "non_existing".to_string(),
289 "TestType".to_string(),
290 );
291
292 assert!(result.is_err());
293 match result {
294 Err(crate::core::types::TrackingError::InvalidPointer(_)) => {}
295 _ => panic!("Expected InvalidPointer error"),
296 }
297 }
298
299 #[test]
300 fn test_default_implementation() {
301 let tracker = OptimizedMemoryTracker::default();
303 let stats = tracker.get_stats().expect("Failed to get stats");
304
305 assert_eq!(stats.active_allocations, 0);
306 assert_eq!(stats.active_memory, 0);
307 }
308
309 #[test]
310 fn test_clone_stats_retrieval() {
311 let tracker = OptimizedMemoryTracker::new();
313
314 tracker
316 .track_allocation_shared(0x6000, 2048)
317 .expect("Failed to track allocation");
318
319 let clone_stats = tracker.get_clone_stats();
320
321 assert!(clone_stats.avoided_clones > 0);
323 }
324
325 #[test]
326 fn test_arc_sharing_efficiency() {
327 let tracker = OptimizedMemoryTracker::new();
329
330 tracker
332 .track_allocation_shared(0x7000, 1024)
333 .expect("Failed to track allocation");
334
335 let collection1 = tracker
337 .get_active_allocations_shared()
338 .expect("Failed to get allocations");
339 let collection2 = tracker
340 .get_active_allocations_shared()
341 .expect("Failed to get allocations");
342
343 assert_eq!(collection1.len(), 1);
345 assert_eq!(collection2.len(), 1);
346 }
347
348 #[test]
349 fn test_large_allocation_tracking() {
350 let tracker = OptimizedMemoryTracker::new();
352 let large_size = 1024 * 1024 * 10; tracker
355 .track_allocation_shared(0x8000, large_size)
356 .expect("Failed to track large allocation");
357
358 let stats = tracker.get_stats().expect("Failed to get stats");
359 assert_eq!(stats.active_memory, large_size);
360 }
361
362 #[test]
363 fn test_allocation_history_persistence() {
364 let tracker = OptimizedMemoryTracker::new();
366
367 for i in 0..5 {
369 tracker
370 .track_allocation_shared(0x9000 + i * 0x100, 512)
371 .expect("Failed to track allocation");
372 }
373
374 let history1 = tracker
376 .get_allocation_history_shared()
377 .expect("Failed to get history");
378 let history2 = tracker
379 .get_allocation_history_shared()
380 .expect("Failed to get history");
381
382 assert_eq!(history1.len(), history2.len());
383 assert_eq!(history1.len(), 5);
384 }
385}