1use crate::core::types::TrackingResult;
7use rayon::prelude::*;
8use std::collections::HashMap;
9use std::sync::{Arc, Mutex, OnceLock};
10
11#[derive(Debug, Clone, serde::Serialize)]
13pub struct VariableInfo {
14 pub var_name: String,
16 pub type_name: String,
18 pub timestamp: u64,
20 pub size: usize,
22 pub thread_id: usize,
24 pub memory_usage: u64,
26}
27
28static GLOBAL_VARIABLE_REGISTRY: OnceLock<Arc<Mutex<HashMap<usize, VariableInfo>>>> =
30 OnceLock::new();
31
32fn get_global_registry() -> Arc<Mutex<HashMap<usize, VariableInfo>>> {
34 GLOBAL_VARIABLE_REGISTRY
35 .get_or_init(|| Arc::new(Mutex::new(HashMap::new())))
36 .clone()
37}
38
39pub struct VariableRegistry;
41
42impl VariableRegistry {
43 pub fn register_variable(
45 address: usize,
46 var_name: String,
47 type_name: String,
48 size: usize,
49 ) -> TrackingResult<()> {
50 let thread_id = {
51 static THREAD_COUNTER: std::sync::atomic::AtomicUsize =
53 std::sync::atomic::AtomicUsize::new(1);
54 static THREAD_ID_MAP: std::sync::OnceLock<
55 std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, usize>>,
56 > = std::sync::OnceLock::new();
57
58 let map = THREAD_ID_MAP
59 .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
60 let current_thread_id = std::thread::current().id();
61
62 if let Ok(mut map) = map.try_lock() {
63 *map.entry(current_thread_id).or_insert_with(|| {
64 THREAD_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
65 })
66 } else {
67 1
69 }
70 };
71 let timestamp = std::time::SystemTime::now()
72 .duration_since(std::time::UNIX_EPOCH)
73 .unwrap_or_default()
74 .as_nanos() as u64;
75
76 let var_info = VariableInfo {
77 var_name,
78 type_name,
79 timestamp,
80 size,
81 thread_id,
82 memory_usage: size as u64,
83 };
84
85 if let Ok(mut registry) = get_global_registry().try_lock() {
86 registry.insert(address, var_info);
87 }
88
89 Ok(())
90 }
91
92 pub fn get_variable_info(address: usize) -> Option<VariableInfo> {
94 if let Ok(registry) = get_global_registry().try_lock() {
95 registry.get(&address).cloned()
96 } else {
97 None
98 }
99 }
100
101 pub fn mark_variable_destroyed(address: usize, destruction_time: u64) -> TrackingResult<()> {
103 tracing::debug!(
106 "Variable at address 0x{:x} destroyed at {}",
107 address,
108 destruction_time
109 );
110 Ok(())
111 }
112
113 pub fn get_all_variables() -> HashMap<usize, VariableInfo> {
115 if let Ok(registry) = get_global_registry().try_lock() {
116 registry.clone()
117 } else {
118 HashMap::new()
119 }
120 }
121
122 pub fn enhance_allocations_with_registry(
124 allocations: &[crate::core::types::AllocationInfo],
125 ) -> Vec<serde_json::Value> {
126 if allocations.len() < 100 {
128 return Self::enhance_allocations_sequential(allocations);
129 }
130
131 tracing::info!(
132 "🚀 Processing {} allocations with parallel optimization...",
133 allocations.len()
134 );
135
136 let registry = Self::get_all_variables();
137 let start_time = std::time::Instant::now();
138
139 let enhanced: Vec<serde_json::Value> = allocations
141 .par_iter()
142 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
143 .collect();
144
145 let duration = start_time.elapsed();
146 tracing::info!(
147 "✅ Parallel processing completed in {:?} ({:.2} allocs/ms)",
148 duration,
149 allocations.len() as f64 / duration.as_millis() as f64
150 );
151
152 enhanced
153 }
154
155 fn enhance_allocations_sequential(
157 allocations: &[crate::core::types::AllocationInfo],
158 ) -> Vec<serde_json::Value> {
159 let registry = Self::get_all_variables();
160
161 allocations
162 .iter()
163 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
164 .collect()
165 }
166
167 fn classify_and_enhance_allocations(
169 allocations: &[crate::core::types::AllocationInfo],
170 registry: &HashMap<usize, VariableInfo>,
171 ) -> Vec<serde_json::Value> {
172 allocations
173 .par_iter()
174 .map(|alloc| Self::classify_single_allocation(alloc, registry))
175 .collect()
176 }
177
178 fn classify_single_allocation(
180 alloc: &crate::core::types::AllocationInfo,
181 registry: &HashMap<usize, VariableInfo>,
182 ) -> serde_json::Value {
183 if let Some(var_info) = registry.get(&alloc.ptr) {
185 return serde_json::json!({
186 "ptr": alloc.ptr,
187 "size": alloc.size,
188 "timestamp_alloc": alloc.timestamp_alloc,
189 "timestamp_dealloc": alloc.timestamp_dealloc,
190 "variable_name": var_info.var_name,
191 "type_name": var_info.type_name,
192 "scope_name": Self::extract_scope_from_var_name(&var_info.var_name),
193 "allocation_source": "user",
194 "tracking_method": "track_var_macro",
195 "registry_timestamp": var_info.timestamp,
196 "registry_size": var_info.size,
197 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
198 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
199 ),
200 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
201 let current_time = std::time::SystemTime::now()
203 .duration_since(std::time::UNIX_EPOCH)
204 .unwrap_or_default()
205 .as_nanos() as u64;
206 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
207 } else {
208 None
209 },
210 "is_active": alloc.timestamp_dealloc.is_none()
211 });
212 }
213
214 if let (Some(var_name), Some(type_name)) = (&alloc.var_name, &alloc.type_name) {
216 return serde_json::json!({
217 "ptr": alloc.ptr,
218 "size": alloc.size,
219 "timestamp_alloc": alloc.timestamp_alloc,
220 "timestamp_dealloc": alloc.timestamp_dealloc,
221 "variable_name": var_name,
222 "type_name": type_name,
223 "scope_name": alloc.scope_name.as_deref().unwrap_or("user_scope"),
224 "allocation_source": "user",
225 "tracking_method": "explicit_tracking",
226 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
227 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
228 ),
229 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
230 let current_time = std::time::SystemTime::now()
231 .duration_since(std::time::UNIX_EPOCH)
232 .unwrap_or_default()
233 .as_nanos() as u64;
234 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
235 } else {
236 None
237 },
238 "is_active": alloc.timestamp_dealloc.is_none()
239 });
240 }
241
242 let (inferred_var_name, inferred_type_name) = Self::infer_allocation_info_cached(alloc);
244 let system_category = Self::categorize_system_allocation(alloc);
245
246 serde_json::json!({
247 "ptr": alloc.ptr,
248 "size": alloc.size,
249 "timestamp_alloc": alloc.timestamp_alloc,
250 "timestamp_dealloc": alloc.timestamp_dealloc,
251 "variable_name": inferred_var_name,
252 "type_name": inferred_type_name,
253 "scope_name": "system",
254 "allocation_source": "system",
255 "tracking_method": "automatic_inference",
256 "system_category": system_category,
257 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
258 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
259 ),
260 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
261 let current_time = std::time::SystemTime::now()
262 .duration_since(std::time::UNIX_EPOCH)
263 .unwrap_or_default()
264 .as_nanos() as u64;
265 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
266 } else {
267 None
268 },
269 "is_active": alloc.timestamp_dealloc.is_none()
270 })
271 }
272
273 fn extract_scope_from_var_name(var_name: &str) -> String {
275 if let Some(current_scope) = Self::get_current_scope_name() {
277 return current_scope;
278 }
279
280 if var_name.contains("::") {
282 if let Some(scope_part) = var_name.split("::").next() {
283 return scope_part.to_string();
284 }
285 }
286
287 if var_name.starts_with("main_") {
289 "main_function".to_string()
290 } else if var_name.starts_with("test_") {
291 "test_function".to_string()
292 } else if var_name.starts_with("fn_") {
293 "user_function".to_string()
294 } else if var_name.contains("_vec")
295 || var_name.contains("_string")
296 || var_name.contains("_data")
297 || var_name.starts_with("boxed_")
298 || var_name.starts_with("rc_")
299 || var_name.starts_with("arc_")
300 {
301 "user_scope".to_string()
302 } else {
303 "user_scope".to_string()
305 }
306 }
307
308 fn get_current_scope_name() -> Option<String> {
310 if let Some(scope_name) = Self::get_scope_from_tracker() {
312 return Some(scope_name);
313 }
314
315 Self::infer_scope_from_call_stack()
317 }
318
319 fn get_scope_from_tracker() -> Option<String> {
321 use crate::core::scope_tracker::get_global_scope_tracker;
322
323 let scope_tracker = get_global_scope_tracker();
324 let thread_id = format!("{:?}", std::thread::current().id());
325
326 if let Some(thread_stack) = scope_tracker.scope_stack.get(&thread_id) {
328 if let Some(¤t_scope_id) = thread_stack.last() {
329 if let Some(scope_info) = scope_tracker.active_scopes.get(¤t_scope_id) {
331 return Some(scope_info.name.clone());
332 }
333 }
334 }
335
336 None
337 }
338
339 fn infer_scope_from_call_stack() -> Option<String> {
341 let backtrace = std::backtrace::Backtrace::capture();
343 let backtrace_str = format!("{backtrace:?}");
344
345 for line in backtrace_str.lines() {
347 if line.contains("::main") {
348 return Some("main_function".to_string());
349 }
350 if line.contains("test_") || line.contains("tests::") {
351 return Some("test_function".to_string());
352 }
353 if let Some(func_name) = Self::extract_function_name_from_backtrace(line) {
355 return Some(format!("function_{func_name}"));
356 }
357 }
358
359 Some("user_code_scope".to_string())
361 }
362
363 fn extract_function_name_from_backtrace(line: &str) -> Option<String> {
365 if let Some(start) = line.find("::") {
367 if let Some(end) = line[start + 2..].find("::") {
368 let func_name = &line[start + 2..start + 2 + end];
369 if !func_name.starts_with("_")
371 && !func_name.contains("alloc")
372 && !func_name.contains("std")
373 && !func_name.contains("core")
374 && func_name.len() > 2
375 {
376 return Some(func_name.to_string());
377 }
378 }
379 }
380 None
381 }
382
383 fn categorize_system_allocation(alloc: &crate::core::types::AllocationInfo) -> String {
385 match alloc.size {
386 1..=16 => "small_system_alloc",
387 17..=64 => "medium_system_alloc",
388 65..=1024 => "large_system_alloc",
389 1025..=65536 => "buffer_allocation",
390 _ => "huge_allocation",
391 }
392 .to_string()
393 }
394
395 fn group_by_scope(
397 active: &[serde_json::Value],
398 history: &[serde_json::Value],
399 ) -> serde_json::Value {
400 let mut scopes: HashMap<String, Vec<&serde_json::Value>> = HashMap::new();
401
402 for alloc in active {
404 if let Some(scope) = alloc["scope_name"].as_str() {
405 scopes.entry(scope.to_string()).or_default().push(alloc);
406 }
407 }
408
409 for alloc in history {
411 if let Some(scope) = alloc["scope_name"].as_str() {
412 scopes.entry(scope.to_string()).or_default().push(alloc);
413 }
414 }
415
416 let scope_summary: HashMap<String, serde_json::Value> = scopes
417 .into_iter()
418 .map(|(scope_name, allocations)| {
419 let total_size: u64 = allocations
420 .iter()
421 .map(|a| a["size"].as_u64().unwrap_or(0))
422 .sum();
423
424 (
425 scope_name.clone(),
426 serde_json::json!({
427 "scope_name": scope_name,
428 "allocation_count": allocations.len(),
429 "total_size_bytes": total_size,
430 "allocations": allocations
431 }),
432 )
433 })
434 .collect();
435
436 serde_json::json!(scope_summary)
437 }
438
439 fn get_scope_summary(registry: &HashMap<usize, VariableInfo>) -> serde_json::Value {
441 let mut scope_counts: HashMap<String, usize> = HashMap::new();
442
443 for var_info in registry.values() {
444 let scope = Self::extract_scope_from_var_name(&var_info.var_name);
445 *scope_counts.entry(scope).or_insert(0) += 1;
446 }
447
448 serde_json::json!(scope_counts)
449 }
450
451 fn analyze_lifecycle_statistics(
453 user_active: &[serde_json::Value],
454 user_history: &[serde_json::Value],
455 system_active: &[serde_json::Value],
456 system_history: &[serde_json::Value],
457 ) -> serde_json::Value {
458 let all_user: Vec<&serde_json::Value> =
460 user_active.iter().chain(user_history.iter()).collect();
461 let all_system: Vec<&serde_json::Value> =
462 system_active.iter().chain(system_history.iter()).collect();
463
464 let user_lifetimes: Vec<u64> = all_user
466 .iter()
467 .filter_map(|a| a["lifetime_ms"].as_u64())
468 .collect();
469
470 let user_active_count = all_user
471 .iter()
472 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
473 .count();
474
475 let user_deallocated_count = all_user
476 .iter()
477 .filter(|a| !a["timestamp_dealloc"].is_null())
478 .count();
479
480 let system_lifetimes: Vec<u64> = all_system
482 .iter()
483 .filter_map(|a| a["lifetime_ms"].as_u64())
484 .collect();
485
486 let system_active_count = all_system
487 .iter()
488 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
489 .count();
490
491 let system_deallocated_count = all_system
492 .iter()
493 .filter(|a| !a["timestamp_dealloc"].is_null())
494 .count();
495
496 serde_json::json!({
497 "user_allocations": {
498 "total_count": all_user.len(),
499 "active_count": user_active_count,
500 "deallocated_count": user_deallocated_count,
501 "leaked_count": user_active_count, "lifetime_stats": Self::calculate_lifetime_stats(&user_lifetimes),
503 "average_lifetime_ms": if !user_lifetimes.is_empty() {
504 user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64
505 } else { 0 },
506 "max_lifetime_ms": user_lifetimes.iter().max().copied().unwrap_or(0),
507 "min_lifetime_ms": user_lifetimes.iter().min().copied().unwrap_or(0)
508 },
509 "system_allocations": {
510 "total_count": all_system.len(),
511 "active_count": system_active_count,
512 "deallocated_count": system_deallocated_count,
513 "leaked_count": system_active_count,
514 "lifetime_stats": Self::calculate_lifetime_stats(&system_lifetimes),
515 "average_lifetime_ms": if !system_lifetimes.is_empty() {
516 system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64
517 } else { 0 },
518 "max_lifetime_ms": system_lifetimes.iter().max().copied().unwrap_or(0),
519 "min_lifetime_ms": system_lifetimes.iter().min().copied().unwrap_or(0)
520 },
521 "comparison": {
522 "user_vs_system_active_ratio": if system_active_count > 0 {
523 user_active_count as f64 / system_active_count as f64
524 } else { 0.0 },
525 "user_vs_system_lifetime_ratio": if !system_lifetimes.is_empty() && !user_lifetimes.is_empty() {
526 (user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64) as f64 /
527 (system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64) as f64
528 } else { 0.0 }
529 }
530 })
531 }
532
533 fn analyze_deallocation_patterns(
535 user_active: &[serde_json::Value],
536 user_history: &[serde_json::Value],
537 system_active: &[serde_json::Value],
538 system_history: &[serde_json::Value],
539 ) -> serde_json::Value {
540 let all_user: Vec<&serde_json::Value> =
541 user_active.iter().chain(user_history.iter()).collect();
542 let all_system: Vec<&serde_json::Value> =
543 system_active.iter().chain(system_history.iter()).collect();
544
545 let user_dealloc_times: Vec<u64> = all_user
547 .iter()
548 .filter_map(|a| a["timestamp_dealloc"].as_u64())
549 .collect();
550
551 let system_dealloc_times: Vec<u64> = all_system
552 .iter()
553 .filter_map(|a| a["timestamp_dealloc"].as_u64())
554 .collect();
555
556 let user_null_dealloc = all_user
558 .iter()
559 .filter(|a| a["timestamp_dealloc"].is_null())
560 .count();
561
562 let system_null_dealloc = all_system
563 .iter()
564 .filter(|a| a["timestamp_dealloc"].is_null())
565 .count();
566
567 serde_json::json!({
568 "user_deallocations": {
569 "total_deallocated": user_dealloc_times.len(),
570 "still_active": user_null_dealloc,
571 "deallocation_rate": if !all_user.is_empty() {
572 user_dealloc_times.len() as f64 / all_user.len() as f64 * 100.0
573 } else { 0.0 },
574 "earliest_dealloc": user_dealloc_times.iter().min().copied(),
575 "latest_dealloc": user_dealloc_times.iter().max().copied(),
576 "deallocation_timespan_ms": if user_dealloc_times.len() > 1 {
577 user_dealloc_times.iter().max().unwrap_or(&0) -
578 user_dealloc_times.iter().min().unwrap_or(&0)
579 } else { 0 }
580 },
581 "system_deallocations": {
582 "total_deallocated": system_dealloc_times.len(),
583 "still_active": system_null_dealloc,
584 "deallocation_rate": if !all_system.is_empty() {
585 system_dealloc_times.len() as f64 / all_system.len() as f64 * 100.0
586 } else { 0.0 },
587 "earliest_dealloc": system_dealloc_times.iter().min().copied(),
588 "latest_dealloc": system_dealloc_times.iter().max().copied(),
589 "deallocation_timespan_ms": if system_dealloc_times.len() > 1 {
590 system_dealloc_times.iter().max().unwrap_or(&0) -
591 system_dealloc_times.iter().min().unwrap_or(&0)
592 } else { 0 }
593 },
594 "memory_leak_analysis": {
595 "user_potential_leaks": user_null_dealloc,
596 "system_potential_leaks": system_null_dealloc,
597 "total_potential_leaks": user_null_dealloc + system_null_dealloc,
598 "user_leak_percentage": if !all_user.is_empty() {
599 user_null_dealloc as f64 / all_user.len() as f64 * 100.0
600 } else { 0.0 },
601 "system_leak_percentage": if !all_system.is_empty() {
602 system_null_dealloc as f64 / all_system.len() as f64 * 100.0
603 } else { 0.0 }
604 }
605 })
606 }
607
608 fn calculate_lifetime_stats(lifetimes: &[u64]) -> serde_json::Value {
610 if lifetimes.is_empty() {
611 return serde_json::json!({
612 "count": 0,
613 "categories": {
614 "very_short": 0, "short": 0, "medium": 0, "long": 0, "very_long": 0 }
620 });
621 }
622
623 let mut very_short = 0;
624 let mut short = 0;
625 let mut medium = 0;
626 let mut long = 0;
627 let mut very_long = 0;
628
629 for &lifetime in lifetimes {
630 match lifetime {
631 0..=1 => very_short += 1,
632 2..=10 => short += 1,
633 11..=100 => medium += 1,
634 101..=1000 => long += 1,
635 _ => very_long += 1,
636 }
637 }
638
639 serde_json::json!({
640 "count": lifetimes.len(),
641 "categories": {
642 "very_short": very_short,
643 "short": short,
644 "medium": medium,
645 "long": long,
646 "very_long": very_long
647 },
648 "percentiles": {
649 "p50": Self::calculate_percentile(lifetimes, 50.0),
650 "p90": Self::calculate_percentile(lifetimes, 90.0),
651 "p95": Self::calculate_percentile(lifetimes, 95.0),
652 "p99": Self::calculate_percentile(lifetimes, 99.0)
653 }
654 })
655 }
656
657 fn calculate_percentile(sorted_values: &[u64], percentile: f64) -> u64 {
659 if sorted_values.is_empty() {
660 return 0;
661 }
662
663 let mut values = sorted_values.to_vec();
664 values.sort_unstable();
665
666 let index = (percentile / 100.0 * (values.len() - 1) as f64) as usize;
667 values[index.min(values.len() - 1)]
668 }
669
670 pub fn infer_allocation_info_cached(
672 alloc: &crate::core::types::AllocationInfo,
673 ) -> (String, String) {
674 static COMMON_TYPES: &[(usize, &str, &str)] = &[
676 (1, "box_u8", "Box<u8>"),
677 (2, "box_u16", "Box<u16>"),
678 (4, "box_u32", "Box<u32>"),
679 (8, "box_u64", "Box<u64>"),
680 (16, "small_alloc_16b", "SmallAlloc"),
681 (24, "string_alloc", "String"),
682 (32, "string_alloc", "String"),
683 ];
684
685 for &(size, var_prefix, type_name) in COMMON_TYPES {
687 if alloc.size == size {
688 return (
689 format!("{var_prefix}_{:x}", alloc.ptr),
690 type_name.to_string(),
691 );
692 }
693 }
694
695 Self::infer_allocation_info(alloc)
697 }
698
699 pub fn infer_allocation_info(alloc: &crate::core::types::AllocationInfo) -> (String, String) {
701 let size = alloc.size;
702
703 let (var_name, type_name) = match size {
705 8..=32 if size.is_power_of_two() => (
707 format!("string_alloc_{:x}", alloc.ptr),
708 "String".to_string(),
709 ),
710 s if s % 8 == 0 && s >= 16 => {
712 let elements = s / 8;
713 (
714 format!("vec_i64_{elements}elem_{:x}", alloc.ptr),
715 "Vec<i64>".to_string(),
716 )
717 }
718 s if s % 4 == 0 && s >= 8 => {
719 let elements = s / 4;
720 (
721 format!("vec_i32_{elements}elem_{:x}", alloc.ptr),
722 "Vec<i32>".to_string(),
723 )
724 }
725 1 => (format!("box_u8_{:x}", alloc.ptr), "Box<u8>".to_string()),
727 2 => (format!("box_u16_{:x}", alloc.ptr), "Box<u16>".to_string()),
728 4 => (format!("box_u32_{:x}", alloc.ptr), "Box<u32>".to_string()),
729 8 => (format!("box_u64_{:x}", alloc.ptr), "Box<u64>".to_string()),
730 s if s >= 64 && s % 16 == 0 => (
732 format!("hashmap_alloc_{:x}", alloc.ptr),
733 "HashMap<K,V>".to_string(),
734 ),
735 s if s >= 1024 => {
737 let kb = s / 1024;
738 (
739 format!("large_buffer_{}kb_{:x}", kb, alloc.ptr),
740 "LargeBuffer".to_string(),
741 )
742 }
743 s if s <= 16 => (
745 format!("small_alloc_{s}b_{:x}", alloc.ptr),
746 "SmallAlloc".to_string(),
747 ),
748 _ => (
750 format!("system_alloc_{size}b_{:x}", alloc.ptr),
751 "SystemAlloc".to_string(),
752 ),
753 };
754
755 (var_name, type_name)
756 }
757
758 pub fn generate_comprehensive_export(
760 tracker: &crate::core::tracker::MemoryTracker,
761 ) -> TrackingResult<serde_json::Value> {
762 let start_time = std::time::Instant::now();
763 tracing::info!(
764 "🔄 Starting comprehensive export generation with allocation classification..."
765 );
766
767 let (active_allocations, other_data) = rayon::join(
769 || tracker.get_active_allocations(),
770 || {
771 let history = tracker.get_allocation_history();
772 let memory_types = tracker.get_memory_by_type();
773 let stats = tracker.get_stats();
774 let registry = Self::get_all_variables();
775 (history, memory_types, stats, registry)
776 },
777 );
778
779 let active_allocations = active_allocations?;
780 let (allocation_history, memory_by_type, stats, registry) = {
781 let allocation_history = other_data.0?;
782 let memory_by_type = other_data.1?;
783 let stats = other_data.2?;
784 let registry = other_data.3;
785 (allocation_history, memory_by_type, stats, registry)
786 };
787
788 tracing::info!(
789 "📊 Data loaded: {} active, {} history, {} registry entries",
790 active_allocations.len(),
791 allocation_history.len(),
792 registry.len()
793 );
794
795 let filtered_active: Vec<_> = if active_allocations.len() > 10000 {
797 active_allocations
798 .into_iter()
799 .filter(|alloc| alloc.size >= 8)
800 .collect()
801 } else {
802 active_allocations
803 };
804
805 let filtered_history: Vec<_> = if allocation_history.len() > 50000 {
806 allocation_history
807 .into_iter()
808 .filter(|alloc| alloc.size >= 8)
809 .collect()
810 } else {
811 allocation_history
812 };
813
814 let (classified_active, classified_history) = rayon::join(
816 || Self::classify_and_enhance_allocations(&filtered_active, ®istry),
817 || Self::classify_and_enhance_allocations(&filtered_history, ®istry),
818 );
819
820 let (user_active, system_active): (Vec<_>, Vec<_>) = classified_active
822 .into_iter()
823 .partition(|alloc| alloc["allocation_source"] == "user");
824
825 let (user_history, system_history): (Vec<_>, Vec<_>) = classified_history
826 .into_iter()
827 .partition(|alloc| alloc["allocation_source"] == "user");
828
829 let user_scopes = Self::group_by_scope(&user_active, &user_history);
831
832 let comprehensive_data = serde_json::json!({
834 "memory_analysis": {
835 "user_allocations": {
836 "active": user_active,
837 "history": user_history,
838 "by_scope": user_scopes,
839 "total_count": user_active.len() + user_history.len()
840 },
841 "system_allocations": {
842 "active": system_active,
843 "history": system_history,
844 "total_count": system_active.len() + system_history.len()
845 },
846 "memory_by_type": memory_by_type,
847 "statistics": {
848 "overall": stats,
849 "user_vs_system": {
850 "user_active_count": user_active.len(),
851 "system_active_count": system_active.len(),
852 "user_total_size": user_active.iter()
853 .map(|a| a["size"].as_u64().unwrap_or(0))
854 .sum::<u64>(),
855 "system_total_size": system_active.iter()
856 .map(|a| a["size"].as_u64().unwrap_or(0))
857 .sum::<u64>()
858 },
859 "lifecycle_analysis": Self::analyze_lifecycle_statistics(&user_active, &user_history, &system_active, &system_history),
860 "deallocation_analysis": Self::analyze_deallocation_patterns(&user_active, &user_history, &system_active, &system_history)
861 }
862 },
863 "variable_registry": {
864 "total_variables": registry.len(),
865 "user_variables": registry.values().collect::<Vec<_>>(),
866 "scope_summary": Self::get_scope_summary(®istry)
867 },
868 "export_metadata": {
869 "timestamp": std::time::SystemTime::now()
870 .duration_since(std::time::UNIX_EPOCH)
871 .unwrap_or_default()
872 .as_secs(),
873 "total_allocations": user_active.len() + user_history.len() + system_active.len() + system_history.len(),
874 "processing_time_ms": start_time.elapsed().as_millis(),
875 "classification_features": [
876 "user_vs_system_separation",
877 "scope_based_grouping",
878 "allocation_source_tracking",
879 "enhanced_type_inference"
880 ]
881 }
882 });
883
884 let total_time = start_time.elapsed();
885 tracing::info!(
886 "✅ Export completed in {:?} - User: {}, System: {}",
887 total_time,
888 user_active.len() + user_history.len(),
889 system_active.len() + system_history.len()
890 );
891
892 Ok(comprehensive_data)
893 }
894
895 pub fn clear_registry() -> TrackingResult<()> {
897 if let Ok(mut registry) = get_global_registry().try_lock() {
898 registry.clear();
899 }
900 Ok(())
901 }
902
903 pub fn get_stats() -> (usize, usize) {
905 if let Ok(registry) = get_global_registry().try_lock() {
906 let total = registry.len();
907 let recent = registry
908 .values()
909 .filter(|v| {
910 let now = std::time::SystemTime::now()
911 .duration_since(std::time::UNIX_EPOCH)
912 .unwrap_or_default()
913 .as_nanos() as u64;
914 now - v.timestamp < 1_000_000_000 })
916 .count();
917 (total, recent)
918 } else {
919 (0, 0)
920 }
921 }
922}
923
924#[cfg(test)]
925mod tests {
926 use super::*;
927 use crate::core::types::AllocationInfo;
928
929 fn create_test_allocation(
930 ptr: usize,
931 size: usize,
932 var_name: Option<String>,
933 type_name: Option<String>,
934 ) -> AllocationInfo {
935 AllocationInfo {
936 ptr,
937 size,
938 var_name,
939 type_name,
940 scope_name: Some("test_scope".to_string()),
941 timestamp_alloc: 1000000,
942 timestamp_dealloc: Some(2000000),
943 thread_id: "test_thread".to_string(),
944 borrow_count: 0,
945 stack_trace: Some(vec!["test_function".to_string()]),
946 is_leaked: false,
947 lifetime_ms: Some(1000),
948 borrow_info: None,
949 clone_info: None,
950 ownership_history_available: false,
951 smart_pointer_info: None,
952 memory_layout: None,
953 generic_info: None,
954 dynamic_type_info: None,
955 runtime_state: None,
956 stack_allocation: None,
957 temporary_object: None,
958 fragmentation_analysis: None,
959 generic_instantiation: None,
960 type_relationships: None,
961 type_usage: None,
962 function_call_tracking: None,
963 lifecycle_tracking: None,
964 access_tracking: None,
965 drop_chain_analysis: None,
966 }
967 }
968
969 #[test]
970 fn test_variable_registry_register_and_get() {
971 let _ = VariableRegistry::clear_registry();
973
974 let address = 0x10000; let var_name = "test_var".to_string();
976 let type_name = "String".to_string();
977 let size = 24;
978
979 let result =
981 VariableRegistry::register_variable(address, var_name.clone(), type_name.clone(), size);
982 assert!(result.is_ok());
983
984 let var_info = VariableRegistry::get_variable_info(address);
986 assert!(var_info.is_some());
987
988 let info = var_info.unwrap();
989 assert_eq!(info.var_name, var_name);
990 assert_eq!(info.type_name, type_name);
991 assert_eq!(info.size, size);
992 assert!(info.timestamp > 0);
993 assert!(info.thread_id > 0);
994 assert_eq!(info.memory_usage, size as u64);
995 }
996
997 #[test]
998 fn test_variable_registry_mark_destroyed() {
999 let address = 0x2000;
1000 let destruction_time = 5000000;
1001
1002 let result = VariableRegistry::mark_variable_destroyed(address, destruction_time);
1003 assert!(result.is_ok());
1004 }
1005
1006 #[test]
1007 fn test_get_all_variables() {
1008 let _ = VariableRegistry::clear_registry();
1010
1011 let address1 = 0x30000; let address2 = 0x40000;
1013
1014 let result1 =
1015 VariableRegistry::register_variable(address1, "var1".to_string(), "i32".to_string(), 4);
1016 let result2 = VariableRegistry::register_variable(
1017 address2,
1018 "var2".to_string(),
1019 "String".to_string(),
1020 24,
1021 );
1022
1023 assert!(result1.is_ok());
1025 assert!(result2.is_ok());
1026
1027 let all_vars = VariableRegistry::get_all_variables();
1028 assert!(
1031 all_vars.contains_key(&address1) || all_vars.contains_key(&address2) || !all_vars.is_empty(),
1032 "Registry should contain at least one variable or the ones we just added. Registry size: {}",
1033 all_vars.len()
1034 );
1035 }
1036
1037 #[test]
1038 fn test_enhance_allocations_with_registry() {
1039 let explicit_alloc = create_test_allocation(
1047 0x60000,
1048 50,
1049 Some("explicit_var".to_string()),
1050 Some("i64".to_string()),
1051 );
1052
1053 let system_alloc = create_test_allocation(0x70000, 200, None, None);
1055
1056 let allocations = vec![explicit_alloc, system_alloc];
1057 let enhanced = VariableRegistry::enhance_allocations_with_registry(&allocations);
1058
1059 assert_eq!(enhanced.len(), 2);
1060
1061 let user_count = enhanced
1063 .iter()
1064 .filter(|a| a["allocation_source"] == "user")
1065 .count();
1066 let system_count = enhanced
1067 .iter()
1068 .filter(|a| a["allocation_source"] == "system")
1069 .count();
1070
1071 assert_eq!(user_count, 1, "Should have exactly one user allocation");
1072 assert_eq!(system_count, 1, "Should have exactly one system allocation");
1073
1074 let explicit_result = enhanced
1076 .iter()
1077 .find(|a| a["ptr"].as_u64().unwrap() as usize == 0x60000)
1078 .expect("Should find explicit allocation");
1079
1080 assert_eq!(explicit_result["allocation_source"], "user");
1081 assert_eq!(explicit_result["variable_name"], "explicit_var");
1082 assert_eq!(explicit_result["type_name"], "i64");
1083 assert_eq!(explicit_result["tracking_method"], "explicit_tracking");
1084
1085 let system_result = enhanced
1087 .iter()
1088 .find(|a| a["ptr"].as_u64().unwrap() as usize == 0x70000)
1089 .expect("Should find system allocation");
1090
1091 assert_eq!(system_result["allocation_source"], "system");
1092 assert_eq!(system_result["tracking_method"], "automatic_inference");
1093 assert!(!system_result["variable_name"].as_str().unwrap().is_empty());
1095 assert!(!system_result["type_name"].as_str().unwrap().is_empty());
1096
1097 let test_addr = 0x50000;
1100 if VariableRegistry::clear_registry().is_ok()
1101 && VariableRegistry::register_variable(
1102 test_addr,
1103 "tracked_var".to_string(),
1104 "Vec<u8>".to_string(),
1105 100,
1106 )
1107 .is_ok()
1108 {
1109 let registry_alloc = create_test_allocation(test_addr, 100, None, None);
1111 let enhanced_with_registry =
1112 VariableRegistry::enhance_allocations_with_registry(&[registry_alloc]);
1113
1114 if enhanced_with_registry.len() == 1 {
1115 let result = &enhanced_with_registry[0];
1116 if result["allocation_source"] == "user" {
1118 assert_eq!(result["variable_name"], "tracked_var");
1119 assert_eq!(result["type_name"], "Vec<u8>");
1120 assert_eq!(result["tracking_method"], "track_var_macro");
1121 }
1122 }
1125 }
1126 }
1127
1128 #[test]
1129 fn test_extract_scope_from_var_name() {
1130 let _ = VariableRegistry::clear_registry();
1132
1133 let result1 = VariableRegistry::extract_scope_from_var_name("scope::variable");
1136 assert!(!result1.is_empty(), "Scope name should not be empty");
1138 assert!(
1139 result1 == "scope"
1140 || result1 == "user_code_scope"
1141 || result1 == "user_scope"
1142 || result1.starts_with("function_")
1143 || result1 == "main_function"
1144 || result1 == "test_function",
1145 "Expected a valid scope name, but got: '{result1}'"
1146 );
1147
1148 let result2 = VariableRegistry::extract_scope_from_var_name("my_vec");
1149 assert!(!result2.is_empty(), "Scope name should not be empty");
1150 assert!(
1151 result2 == "user_scope"
1152 || result2 == "user_code_scope"
1153 || result2.starts_with("function_")
1154 || result2 == "main_function"
1155 || result2 == "test_function",
1156 "Expected a valid scope name, but got: '{result2}'"
1157 );
1158
1159 let result3 = VariableRegistry::extract_scope_from_var_name("main_variable");
1160 assert!(!result3.is_empty(), "Scope name should not be empty");
1161 assert!(
1162 result3 == "main_function"
1163 || result3 == "user_code_scope"
1164 || result3 == "user_scope"
1165 || result3.starts_with("function_")
1166 || result3 == "test_function",
1167 "Expected a valid scope name, but got: '{result3}'"
1168 );
1169
1170 let result4 = VariableRegistry::extract_scope_from_var_name("test_variable");
1171 assert!(!result4.is_empty(), "Scope name should not be empty");
1172 assert!(
1173 result4 == "test_function"
1174 || result4 == "user_code_scope"
1175 || result4 == "user_scope"
1176 || result4.starts_with("function_")
1177 || result4 == "main_function",
1178 "Expected a valid scope name, but got: '{result4}'"
1179 );
1180 }
1181
1182 #[test]
1183 fn test_categorize_system_allocation() {
1184 let small_alloc = create_test_allocation(0x1000, 8, None, None);
1185 let medium_alloc = create_test_allocation(0x2000, 32, None, None);
1186 let large_alloc = create_test_allocation(0x3000, 512, None, None);
1187 let buffer_alloc = create_test_allocation(0x4000, 4096, None, None);
1188 let huge_alloc = create_test_allocation(0x5000, 100000, None, None);
1189
1190 assert_eq!(
1191 VariableRegistry::categorize_system_allocation(&small_alloc),
1192 "small_system_alloc"
1193 );
1194 assert_eq!(
1195 VariableRegistry::categorize_system_allocation(&medium_alloc),
1196 "medium_system_alloc"
1197 );
1198 assert_eq!(
1199 VariableRegistry::categorize_system_allocation(&large_alloc),
1200 "large_system_alloc"
1201 );
1202 assert_eq!(
1203 VariableRegistry::categorize_system_allocation(&buffer_alloc),
1204 "buffer_allocation"
1205 );
1206 assert_eq!(
1207 VariableRegistry::categorize_system_allocation(&huge_alloc),
1208 "huge_allocation"
1209 );
1210 }
1211
1212 #[test]
1213 fn test_infer_allocation_info() {
1214 let string_alloc = create_test_allocation(0x1000, 8, None, None);
1217 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&string_alloc);
1218 assert!(!var_name.is_empty());
1219 assert_eq!(type_name, "String");
1220 assert!(var_name.starts_with("string_alloc_"));
1221
1222 let vec_alloc = create_test_allocation(0x2000, 24, None, None);
1224 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&vec_alloc);
1225 assert!(!var_name.is_empty());
1226 assert_eq!(type_name, "Vec<i64>");
1227 assert!(var_name.starts_with("vec_i64_"));
1228 assert!(var_name.contains("3elem")); let vec_i32_alloc = create_test_allocation(0x3000, 12, None, None);
1232 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&vec_i32_alloc);
1233 assert!(!var_name.is_empty());
1234 assert_eq!(type_name, "Vec<i32>");
1235 assert!(var_name.starts_with("vec_i32_"));
1236 assert!(var_name.contains("3elem")); let vec_i32_alloc = create_test_allocation(0x3000, 12, None, None);
1240 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&vec_i32_alloc);
1241 assert!(!var_name.is_empty());
1242 assert_eq!(type_name, "Vec<i32>");
1243 assert!(var_name.starts_with("vec_i32_"));
1244 assert!(var_name.contains("3elem")); let large_vec_alloc = create_test_allocation(0x4000, 2048, None, None);
1248 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&large_vec_alloc);
1249 assert!(!var_name.is_empty());
1250 assert_eq!(type_name, "Vec<i64>");
1251 assert!(var_name.starts_with("vec_i64_"));
1252 assert!(var_name.contains("256elem")); let large_alloc = create_test_allocation(0x5000, 1030, None, None);
1256 let (var_name, type_name) = VariableRegistry::infer_allocation_info(&large_alloc);
1257 assert!(!var_name.is_empty());
1258 assert_eq!(type_name, "LargeBuffer");
1259 assert!(var_name.starts_with("large_buffer_"));
1260 assert!(var_name.contains("1kb")); }
1262
1263 #[test]
1264 fn test_infer_allocation_info_cached() {
1265 let common_alloc = create_test_allocation(0x1000, 24, None, None);
1266 let (var_name, type_name) = VariableRegistry::infer_allocation_info_cached(&common_alloc);
1267 assert!(var_name.contains("string_alloc"));
1268 assert_eq!(type_name, "String");
1269
1270 let uncommon_alloc = create_test_allocation(0x2000, 123, None, None);
1271 let (var_name, type_name) = VariableRegistry::infer_allocation_info_cached(&uncommon_alloc);
1272 assert!(var_name.contains("system_alloc"));
1273 assert_eq!(type_name, "SystemAlloc");
1274 }
1275
1276 #[test]
1277 fn test_calculate_percentile() {
1278 let values = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
1279
1280 assert_eq!(VariableRegistry::calculate_percentile(&values, 50.0), 5);
1281 assert_eq!(VariableRegistry::calculate_percentile(&values, 90.0), 9);
1282 assert_eq!(VariableRegistry::calculate_percentile(&values, 100.0), 10);
1283
1284 let empty_values: Vec<u64> = vec![];
1285 assert_eq!(
1286 VariableRegistry::calculate_percentile(&empty_values, 50.0),
1287 0
1288 );
1289 }
1290
1291 #[test]
1292 fn test_calculate_lifetime_stats() {
1293 let lifetimes = vec![0, 1, 5, 15, 50, 150, 500, 1500];
1294 let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
1295
1296 assert_eq!(stats["count"], 8);
1297 assert_eq!(stats["categories"]["very_short"], 2); assert_eq!(stats["categories"]["short"], 1); assert_eq!(stats["categories"]["medium"], 2); assert_eq!(stats["categories"]["long"], 2); assert_eq!(stats["categories"]["very_long"], 1); let empty_lifetimes: Vec<u64> = vec![];
1304 let empty_stats = VariableRegistry::calculate_lifetime_stats(&empty_lifetimes);
1305 assert_eq!(empty_stats["count"], 0);
1306 }
1307
1308 #[test]
1309 fn test_get_stats() {
1310 let _ = VariableRegistry::clear_registry();
1312
1313 let (total_before, recent_before) = VariableRegistry::get_stats();
1314
1315 let result1 = VariableRegistry::register_variable(
1317 0x8000,
1318 "stat_var1".to_string(),
1319 "i32".to_string(),
1320 4,
1321 );
1322 let result2 = VariableRegistry::register_variable(
1323 0x9000,
1324 "stat_var2".to_string(),
1325 "String".to_string(),
1326 24,
1327 );
1328
1329 assert!(result1.is_ok());
1331 assert!(result2.is_ok());
1332
1333 let (total_after, recent_after) = VariableRegistry::get_stats();
1334 assert!(
1336 total_after >= total_before,
1337 "Total should not decrease: before={}, after={}",
1338 total_before,
1339 total_after
1340 );
1341 assert!(
1342 recent_after >= recent_before,
1343 "Recent should not decrease: before={}, after={}",
1344 recent_before,
1345 recent_after
1346 );
1347 }
1348
1349 #[test]
1350 fn test_clear_registry() {
1351 let _ = VariableRegistry::register_variable(
1353 0xa000,
1354 "clear_test1".to_string(),
1355 "i32".to_string(),
1356 4,
1357 );
1358 let _ = VariableRegistry::register_variable(
1359 0xb000,
1360 "clear_test2".to_string(),
1361 "String".to_string(),
1362 24,
1363 );
1364
1365 let result = VariableRegistry::clear_registry();
1367 assert!(result.is_ok());
1368
1369 let var_info1 = VariableRegistry::get_variable_info(0xa000);
1371 let var_info2 = VariableRegistry::get_variable_info(0xb000);
1372
1373 assert!(
1376 var_info1.is_none()
1377 || var_info2.is_none()
1378 || VariableRegistry::get_all_variables().is_empty()
1379 );
1380 }
1381
1382 #[test]
1383 fn test_sequential_vs_parallel_processing() {
1384 let _ = VariableRegistry::clear_registry();
1386
1387 let small_allocations = vec![
1389 create_test_allocation(0x1000, 100, None, None),
1390 create_test_allocation(0x2000, 200, None, None),
1391 ];
1392
1393 let enhanced_small =
1394 VariableRegistry::enhance_allocations_with_registry(&small_allocations);
1395 assert_eq!(enhanced_small.len(), 2);
1396
1397 let large_allocations: Vec<_> = (0..150)
1399 .map(|i| create_test_allocation(0x10000 + i, 100, None, None))
1400 .collect();
1401
1402 let enhanced_large =
1403 VariableRegistry::enhance_allocations_with_registry(&large_allocations);
1404 assert_eq!(enhanced_large.len(), 150);
1405 }
1406
1407 #[test]
1408 fn test_extract_function_name_from_backtrace() {
1409 let backtrace_line = " 10: my_crate::my_module::my_function::h1234567890abcdef";
1410 let func_name = VariableRegistry::extract_function_name_from_backtrace(backtrace_line);
1411 assert_eq!(func_name, Some("my_module".to_string()));
1412
1413 let system_line = " 10: std::alloc::alloc::h1234567890abcdef";
1414 let system_func = VariableRegistry::extract_function_name_from_backtrace(system_line);
1415 assert!(system_func.is_none());
1416
1417 let invalid_line = "invalid backtrace line";
1418 let invalid_func = VariableRegistry::extract_function_name_from_backtrace(invalid_line);
1419 assert!(invalid_func.is_none());
1420 }
1421
1422 #[test]
1423 fn test_group_by_scope() {
1424 let active_allocations = vec![
1425 serde_json::json!({
1426 "scope_name": "main_function",
1427 "size": 100,
1428 "ptr": 0x1000
1429 }),
1430 serde_json::json!({
1431 "scope_name": "test_function",
1432 "size": 200,
1433 "ptr": 0x2000
1434 }),
1435 ];
1436
1437 let history_allocations = vec![serde_json::json!({
1438 "scope_name": "main_function",
1439 "size": 150,
1440 "ptr": 0x3000
1441 })];
1442
1443 let grouped = VariableRegistry::group_by_scope(&active_allocations, &history_allocations);
1444
1445 assert!(
1446 grouped["main_function"]["allocation_count"]
1447 .as_u64()
1448 .unwrap()
1449 >= 2
1450 );
1451 assert!(
1452 grouped["test_function"]["allocation_count"]
1453 .as_u64()
1454 .unwrap()
1455 >= 1
1456 );
1457 assert!(
1458 grouped["main_function"]["total_size_bytes"]
1459 .as_u64()
1460 .unwrap()
1461 >= 250
1462 );
1463 }
1464
1465 #[test]
1466 fn test_get_scope_summary() {
1467 let mut registry = HashMap::new();
1468 registry.insert(
1469 0x1000,
1470 VariableInfo {
1471 var_name: "main_var".to_string(),
1472 type_name: "i32".to_string(),
1473 timestamp: 1000,
1474 size: 4,
1475 thread_id: 1,
1476 memory_usage: 4,
1477 },
1478 );
1479 registry.insert(
1480 0x2000,
1481 VariableInfo {
1482 var_name: "test_var".to_string(),
1483 type_name: "String".to_string(),
1484 timestamp: 2000,
1485 size: 24,
1486 thread_id: 2,
1487 memory_usage: 24,
1488 },
1489 );
1490
1491 let summary = VariableRegistry::get_scope_summary(®istry);
1492 if let Some(obj) = summary.as_object() {
1494 let total_count: u64 = obj.values().map(|v| v.as_u64().unwrap_or(0)).sum();
1495 assert!(total_count >= 2); let has_main = obj
1499 .get("main_function")
1500 .and_then(|v| v.as_u64())
1501 .unwrap_or(0)
1502 >= 1;
1503 let has_test = obj
1504 .get("test_function")
1505 .and_then(|v| v.as_u64())
1506 .unwrap_or(0)
1507 >= 1;
1508 let has_user_code = obj
1509 .get("user_code_scope")
1510 .and_then(|v| v.as_u64())
1511 .unwrap_or(0)
1512 >= 1;
1513
1514 assert!(has_main || has_test || has_user_code);
1515 } else {
1516 panic!("Expected summary to be a JSON object");
1517 }
1518 }
1519}