1use crate::core::{MemScopeError, MemScopeResult};
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
42mod inference;
43
44impl VariableRegistry {
45 pub fn register_variable(
56 address: usize,
57 var_name: String,
58 type_name: String,
59 size: usize,
60 ) -> MemScopeResult<()> {
61 let thread_id = {
62 static THREAD_COUNTER: std::sync::atomic::AtomicUsize =
64 std::sync::atomic::AtomicUsize::new(1);
65 static THREAD_ID_MAP: std::sync::OnceLock<
66 std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, usize>>,
67 > = std::sync::OnceLock::new();
68
69 let map = THREAD_ID_MAP
70 .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
71 let current_thread_id = std::thread::current().id();
72
73 if let Ok(mut map) = map.try_lock() {
74 *map.entry(current_thread_id).or_insert_with(|| {
75 THREAD_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
76 })
77 } else {
78 1
80 }
81 };
82 let timestamp = std::time::SystemTime::now()
83 .duration_since(std::time::UNIX_EPOCH)
84 .unwrap_or_default()
85 .as_nanos() as u64;
86
87 let var_info = VariableInfo {
88 var_name,
89 type_name,
90 timestamp,
91 size,
92 thread_id,
93 memory_usage: size as u64,
94 };
95
96 let mut retry_count = 0;
98 const MAX_RETRIES: usize = 5;
99 const INITIAL_BACKOFF_MS: u64 = 1;
100
101 loop {
102 if let Ok(mut registry) = get_global_registry().try_lock() {
103 registry.insert(address, var_info);
104 break;
105 } else {
106 retry_count += 1;
107 if retry_count >= MAX_RETRIES {
108 tracing::warn!(
109 "Variable registration for '{}' dropped after {} retries due to lock contention. ptr=0x{:x}, size={}",
110 var_info.var_name, MAX_RETRIES, address, size
111 );
112 break;
113 }
114
115 let backoff_ms = INITIAL_BACKOFF_MS * (1 << (retry_count - 1));
117 std::thread::sleep(std::time::Duration::from_millis(backoff_ms));
118 }
119 }
120
121 Ok(())
122 }
123
124 pub fn get_variable_info(address: usize) -> Option<VariableInfo> {
126 if let Ok(registry) = get_global_registry().try_lock() {
127 registry.get(&address).cloned()
128 } else {
129 None
130 }
131 }
132
133 pub fn mark_variable_destroyed(address: usize, destruction_time: u64) -> MemScopeResult<()> {
135 tracing::debug!(
138 "Variable at address 0x{:x} destroyed at {}",
139 address,
140 destruction_time
141 );
142 Ok(())
143 }
144
145 pub fn get_all_variables() -> HashMap<usize, VariableInfo> {
147 if let Ok(registry) = get_global_registry().try_lock() {
148 registry.clone()
149 } else {
150 HashMap::new()
151 }
152 }
153
154 pub fn enhance_allocations_with_registry(
156 allocations: &[crate::core::types::AllocationInfo],
157 ) -> Vec<serde_json::Value> {
158 if allocations.len() < 100 {
160 return Self::enhance_allocations_sequential(allocations);
161 }
162
163 tracing::info!(
164 "🚀 Processing {} allocations with parallel optimization...",
165 allocations.len()
166 );
167
168 let registry = Self::get_all_variables();
169 let start_time = std::time::Instant::now();
170
171 let allocations: Vec<crate::capture::types::AllocationInfo> =
173 allocations.iter().map(|a| a.clone().into()).collect();
174
175 let enhanced: Vec<serde_json::Value> = allocations
177 .par_iter()
178 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
179 .collect();
180
181 let duration = start_time.elapsed();
182 tracing::info!(
183 "✅ Parallel processing completed in {:?} ({:.2} allocs/ms)",
184 duration,
185 allocations.len() as f64 / duration.as_millis() as f64
186 );
187
188 enhanced
189 }
190
191 fn enhance_allocations_sequential(
193 allocations: &[crate::core::types::AllocationInfo],
194 ) -> Vec<serde_json::Value> {
195 let registry = Self::get_all_variables();
196
197 let allocations: Vec<crate::capture::types::AllocationInfo> =
199 allocations.iter().map(|a| a.clone().into()).collect();
200
201 allocations
202 .iter()
203 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
204 .collect()
205 }
206
207 fn classify_and_enhance_allocations(
209 allocations: &[crate::capture::types::allocation::AllocationInfo],
210 registry: &HashMap<usize, VariableInfo>,
211 ) -> Vec<serde_json::Value> {
212 allocations
213 .par_iter()
214 .map(|alloc| Self::classify_single_allocation(alloc, registry))
215 .collect()
216 }
217
218 fn classify_single_allocation(
220 alloc: &crate::capture::types::allocation::AllocationInfo,
221 registry: &HashMap<usize, VariableInfo>,
222 ) -> serde_json::Value {
223 if let Some(var_info) = registry.get(&alloc.ptr) {
225 return serde_json::json!({
226 "ptr": alloc.ptr,
227 "size": alloc.size,
228 "timestamp_alloc": alloc.timestamp_alloc,
229 "timestamp_dealloc": alloc.timestamp_dealloc,
230 "variable_name": var_info.var_name,
231 "type_name": var_info.type_name,
232 "scope_name": Self::extract_scope_from_var_name(&var_info.var_name),
233 "allocation_source": "user",
234 "tracking_method": "track_var_macro",
235 "registry_timestamp": var_info.timestamp,
236 "registry_size": var_info.size,
237 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
238 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
239 ),
240 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
241 let current_time = std::time::SystemTime::now()
243 .duration_since(std::time::UNIX_EPOCH)
244 .unwrap_or_default()
245 .as_nanos() as u64;
246 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
247 } else {
248 None
249 },
250 "is_active": alloc.timestamp_dealloc.is_none(),
251 "is_leaked": alloc.is_leaked
252 });
253 }
254
255 if let (Some(var_name), Some(type_name)) = (&alloc.var_name, &alloc.type_name) {
257 return serde_json::json!({
258 "ptr": alloc.ptr,
259 "size": alloc.size,
260 "timestamp_alloc": alloc.timestamp_alloc,
261 "timestamp_dealloc": alloc.timestamp_dealloc,
262 "variable_name": var_name,
263 "type_name": type_name,
264 "scope_name": alloc.scope_name.as_deref().unwrap_or("user_scope"),
265 "allocation_source": "user",
266 "tracking_method": "explicit_tracking",
267 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
268 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
269 ),
270 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
271 let current_time = std::time::SystemTime::now()
272 .duration_since(std::time::UNIX_EPOCH)
273 .unwrap_or_default()
274 .as_nanos() as u64;
275 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
276 } else {
277 None
278 },
279 "is_active": alloc.timestamp_dealloc.is_none(),
280 "is_leaked": alloc.is_leaked
281 });
282 }
283
284 let (inferred_var_name, inferred_type_name) = Self::infer_allocation_info_cached(alloc);
286 let system_category = Self::categorize_system_allocation(alloc);
287
288 serde_json::json!({
289 "ptr": alloc.ptr,
290 "size": alloc.size,
291 "timestamp_alloc": alloc.timestamp_alloc,
292 "timestamp_dealloc": alloc.timestamp_dealloc,
293 "variable_name": inferred_var_name,
294 "type_name": inferred_type_name,
295 "scope_name": "system",
296 "allocation_source": "system",
297 "tracking_method": "automatic_inference",
298 "system_category": system_category,
299 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
300 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
301 ),
302 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
303 let current_time = std::time::SystemTime::now()
304 .duration_since(std::time::UNIX_EPOCH)
305 .unwrap_or_default()
306 .as_nanos() as u64;
307 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
308 } else {
309 None
310 },
311 "is_active": alloc.timestamp_dealloc.is_none(),
312 "is_leaked": alloc.is_leaked
313 })
314 }
315
316 fn extract_scope_from_var_name(var_name: &str) -> String {
318 if let Some(current_scope) = Self::get_current_scope_name() {
320 return current_scope;
321 }
322
323 if var_name.contains("::") {
325 if let Some(scope_part) = var_name.split("::").next() {
326 return scope_part.to_string();
327 }
328 }
329
330 if var_name.starts_with("main_") {
332 "main_function".to_string()
333 } else if var_name.starts_with("test_") {
334 "test_function".to_string()
335 } else if var_name.starts_with("fn_") {
336 "user_function".to_string()
337 } else if var_name.contains("_vec")
338 || var_name.contains("_string")
339 || var_name.contains("_data")
340 || var_name.starts_with("boxed_")
341 || var_name.starts_with("rc_")
342 || var_name.starts_with("arc_")
343 {
344 "user_scope".to_string()
345 } else {
346 "user_scope".to_string()
348 }
349 }
350
351 fn get_current_scope_name() -> Option<String> {
353 if let Some(scope_name) = Self::get_scope_from_tracker() {
355 return Some(scope_name);
356 }
357
358 Self::infer_scope_from_call_stack()
360 }
361
362 fn get_scope_from_tracker() -> Option<String> {
364 use crate::core::scope_tracker::get_global_scope_tracker;
365
366 let scope_tracker = get_global_scope_tracker();
367 let thread_id = format!("{:?}", std::thread::current().id());
368
369 if let Some(thread_stack) = scope_tracker
370 .scope_stack
371 .read()
372 .ok()
373 .and_then(|s| s.get(&thread_id).cloned())
374 {
375 if let Some(current_scope_id) = thread_stack.last() {
376 if let Some(scope_info) = scope_tracker
377 .active_scopes
378 .read()
379 .ok()
380 .and_then(|s| s.get(current_scope_id).cloned())
381 {
382 return Some(scope_info.name);
383 }
384 }
385 }
386
387 None
388 }
389
390 fn infer_scope_from_call_stack() -> Option<String> {
392 let backtrace = std::backtrace::Backtrace::capture();
394 let backtrace_str = format!("{backtrace:?}");
395
396 for line in backtrace_str.lines() {
398 if line.contains("::main") {
399 return Some("main_function".to_string());
400 }
401 if line.contains("test_") || line.contains("tests::") {
402 return Some("test_function".to_string());
403 }
404 if let Some(func_name) = Self::extract_function_name_from_backtrace(line) {
406 return Some(format!("function_{func_name}"));
407 }
408 }
409
410 Some("user_code_scope".to_string())
412 }
413
414 fn extract_function_name_from_backtrace(line: &str) -> Option<String> {
416 if let Some(start) = line.find("::") {
418 if let Some(end) = line[start + 2..].find("::") {
419 let func_name = &line[start + 2..start + 2 + end];
420 if !func_name.starts_with("_")
422 && !func_name.contains("alloc")
423 && !func_name.contains("std")
424 && !func_name.contains("core")
425 && func_name.len() > 2
426 {
427 return Some(func_name.to_string());
428 }
429 }
430 }
431 None
432 }
433
434 fn categorize_system_allocation(
436 alloc: &crate::capture::types::allocation::AllocationInfo,
437 ) -> String {
438 match alloc.size {
439 1..=16 => "small_system_alloc",
440 17..=64 => "medium_system_alloc",
441 65..=1024 => "large_system_alloc",
442 1025..=65536 => "buffer_allocation",
443 _ => "huge_allocation",
444 }
445 .to_string()
446 }
447
448 fn group_by_scope(
450 active: &[serde_json::Value],
451 history: &[serde_json::Value],
452 ) -> serde_json::Value {
453 let mut scopes: HashMap<String, Vec<&serde_json::Value>> = HashMap::new();
454
455 for alloc in active {
457 if let Some(scope) = alloc["scope_name"].as_str() {
458 scopes.entry(scope.to_string()).or_default().push(alloc);
459 }
460 }
461
462 for alloc in history {
464 if let Some(scope) = alloc["scope_name"].as_str() {
465 scopes.entry(scope.to_string()).or_default().push(alloc);
466 }
467 }
468
469 let scope_summary: HashMap<String, serde_json::Value> = scopes
470 .into_iter()
471 .map(|(scope_name, allocations)| {
472 let total_size: u64 = allocations
473 .iter()
474 .map(|a| a["size"].as_u64().unwrap_or(0))
475 .sum();
476
477 (
478 scope_name.clone(),
479 serde_json::json!({
480 "scope_name": scope_name,
481 "allocation_count": allocations.len(),
482 "total_size_bytes": total_size,
483 "allocations": allocations
484 }),
485 )
486 })
487 .collect();
488
489 serde_json::json!(scope_summary)
490 }
491
492 fn get_scope_summary(registry: &HashMap<usize, VariableInfo>) -> serde_json::Value {
494 let mut scope_counts: HashMap<String, usize> = HashMap::new();
495
496 for var_info in registry.values() {
497 let scope = Self::extract_scope_from_var_name(&var_info.var_name);
498 *scope_counts.entry(scope).or_insert(0) += 1;
499 }
500
501 serde_json::json!(scope_counts)
502 }
503
504 fn analyze_lifecycle_statistics(
506 user_active: &[serde_json::Value],
507 user_history: &[serde_json::Value],
508 system_active: &[serde_json::Value],
509 system_history: &[serde_json::Value],
510 ) -> serde_json::Value {
511 let all_user: Vec<&serde_json::Value> =
513 user_active.iter().chain(user_history.iter()).collect();
514 let all_system: Vec<&serde_json::Value> =
515 system_active.iter().chain(system_history.iter()).collect();
516
517 let user_lifetimes: Vec<u64> = all_user
519 .iter()
520 .filter_map(|a| a["lifetime_ms"].as_u64())
521 .collect();
522
523 let user_active_count = all_user
524 .iter()
525 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
526 .count();
527
528 let user_deallocated_count = all_user
529 .iter()
530 .filter(|a| !a["timestamp_dealloc"].is_null())
531 .count();
532
533 let system_lifetimes: Vec<u64> = all_system
535 .iter()
536 .filter_map(|a| a["lifetime_ms"].as_u64())
537 .collect();
538
539 let system_active_count = all_system
540 .iter()
541 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
542 .count();
543
544 let system_deallocated_count = all_system
545 .iter()
546 .filter(|a| !a["timestamp_dealloc"].is_null())
547 .count();
548
549 let user_leaked_count = all_user
553 .iter()
554 .filter(|a| a["is_leaked"].as_bool().unwrap_or(false))
555 .count();
556 let system_leaked_count = all_system
557 .iter()
558 .filter(|a| a["is_leaked"].as_bool().unwrap_or(false))
559 .count();
560
561 serde_json::json!({
562 "user_allocations": {
563 "total_count": all_user.len(),
564 "active_count": user_active_count,
565 "deallocated_count": user_deallocated_count,
566 "leaked_count": user_leaked_count,
567 "lifetime_stats": Self::calculate_lifetime_stats(&user_lifetimes),
568 "average_lifetime_ms": if !user_lifetimes.is_empty() {
569 user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64
570 } else { 0 },
571 "max_lifetime_ms": user_lifetimes.iter().max().copied().unwrap_or(0),
572 "min_lifetime_ms": user_lifetimes.iter().min().copied().unwrap_or(0)
573 },
574 "system_allocations": {
575 "total_count": all_system.len(),
576 "active_count": system_active_count,
577 "deallocated_count": system_deallocated_count,
578 "leaked_count": system_leaked_count,
579 "lifetime_stats": Self::calculate_lifetime_stats(&system_lifetimes),
580 "average_lifetime_ms": if !system_lifetimes.is_empty() {
581 system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64
582 } else { 0 },
583 "max_lifetime_ms": system_lifetimes.iter().max().copied().unwrap_or(0),
584 "min_lifetime_ms": system_lifetimes.iter().min().copied().unwrap_or(0)
585 },
586 "comparison": {
587 "user_vs_system_active_ratio": if system_active_count > 0 {
588 user_active_count as f64 / system_active_count as f64
589 } else { 0.0 },
590 "user_vs_system_lifetime_ratio": if !system_lifetimes.is_empty() && !user_lifetimes.is_empty() {
591 (user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64) as f64 /
592 (system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64) as f64
593 } else { 0.0 }
594 }
595 })
596 }
597
598 fn analyze_deallocation_patterns(
600 user_active: &[serde_json::Value],
601 user_history: &[serde_json::Value],
602 system_active: &[serde_json::Value],
603 system_history: &[serde_json::Value],
604 ) -> serde_json::Value {
605 let all_user: Vec<&serde_json::Value> =
606 user_active.iter().chain(user_history.iter()).collect();
607 let all_system: Vec<&serde_json::Value> =
608 system_active.iter().chain(system_history.iter()).collect();
609
610 let user_dealloc_times: Vec<u64> = all_user
612 .iter()
613 .filter_map(|a| a["timestamp_dealloc"].as_u64())
614 .collect();
615
616 let system_dealloc_times: Vec<u64> = all_system
617 .iter()
618 .filter_map(|a| a["timestamp_dealloc"].as_u64())
619 .collect();
620
621 let user_null_dealloc = all_user
623 .iter()
624 .filter(|a| a["timestamp_dealloc"].is_null())
625 .count();
626
627 let system_null_dealloc = all_system
628 .iter()
629 .filter(|a| a["timestamp_dealloc"].is_null())
630 .count();
631
632 serde_json::json!({
633 "user_deallocations": {
634 "total_deallocated": user_dealloc_times.len(),
635 "still_active": user_null_dealloc,
636 "deallocation_rate": if !all_user.is_empty() {
637 user_dealloc_times.len() as f64 / all_user.len() as f64 * 100.0
638 } else { 0.0 },
639 "earliest_dealloc": user_dealloc_times.iter().min().copied(),
640 "latest_dealloc": user_dealloc_times.iter().max().copied(),
641 "deallocation_timespan_ms": if user_dealloc_times.len() > 1 {
642 user_dealloc_times.iter().max().unwrap_or(&0) -
643 user_dealloc_times.iter().min().unwrap_or(&0)
644 } else { 0 }
645 },
646 "system_deallocations": {
647 "total_deallocated": system_dealloc_times.len(),
648 "still_active": system_null_dealloc,
649 "deallocation_rate": if !all_system.is_empty() {
650 system_dealloc_times.len() as f64 / all_system.len() as f64 * 100.0
651 } else { 0.0 },
652 "earliest_dealloc": system_dealloc_times.iter().min().copied(),
653 "latest_dealloc": system_dealloc_times.iter().max().copied(),
654 "deallocation_timespan_ms": if system_dealloc_times.len() > 1 {
655 system_dealloc_times.iter().max().unwrap_or(&0) -
656 system_dealloc_times.iter().min().unwrap_or(&0)
657 } else { 0 }
658 },
659 "memory_leak_analysis": {
660 "user_potential_leaks": user_null_dealloc,
661 "system_potential_leaks": system_null_dealloc,
662 "total_potential_leaks": user_null_dealloc + system_null_dealloc,
663 "user_leak_percentage": if !all_user.is_empty() {
664 user_null_dealloc as f64 / all_user.len() as f64 * 100.0
665 } else { 0.0 },
666 "system_leak_percentage": if !all_system.is_empty() {
667 system_null_dealloc as f64 / all_system.len() as f64 * 100.0
668 } else { 0.0 }
669 }
670 })
671 }
672
673 fn calculate_lifetime_stats(lifetimes: &[u64]) -> serde_json::Value {
675 if lifetimes.is_empty() {
676 return serde_json::json!({
677 "count": 0,
678 "categories": {
679 "very_short": 0, "short": 0, "medium": 0, "long": 0, "very_long": 0 }
685 });
686 }
687
688 let mut very_short = 0;
689 let mut short = 0;
690 let mut medium = 0;
691 let mut long = 0;
692 let mut very_long = 0;
693
694 for &lifetime in lifetimes {
695 match lifetime {
696 0..=1 => very_short += 1,
697 2..=10 => short += 1,
698 11..=100 => medium += 1,
699 101..=1000 => long += 1,
700 _ => very_long += 1,
701 }
702 }
703
704 serde_json::json!({
705 "count": lifetimes.len(),
706 "categories": {
707 "very_short": very_short,
708 "short": short,
709 "medium": medium,
710 "long": long,
711 "very_long": very_long
712 },
713 "percentiles": {
714 "p50": Self::calculate_percentile(lifetimes, 50.0),
715 "p90": Self::calculate_percentile(lifetimes, 90.0),
716 "p95": Self::calculate_percentile(lifetimes, 95.0),
717 "p99": Self::calculate_percentile(lifetimes, 99.0)
718 }
719 })
720 }
721
722 fn calculate_percentile(sorted_values: &[u64], percentile: f64) -> u64 {
724 if sorted_values.is_empty() {
725 return 0;
726 }
727
728 let mut values = sorted_values.to_vec();
729 values.sort_unstable();
730
731 let index = (percentile / 100.0 * (values.len() - 1) as f64) as usize;
732 values[index.min(values.len() - 1)]
733 }
734
735 pub fn generate_comprehensive_export(
737 tracker: &crate::core::tracker::MemoryTracker,
738 ) -> MemScopeResult<serde_json::Value> {
739 let start_time = std::time::Instant::now();
740 tracing::info!(
741 "🔄 Starting comprehensive export generation with allocation classification..."
742 );
743
744 let active_allocations = tracker.get_active_allocations().map_err(|e| {
746 MemScopeError::error(
747 "variable_registry",
748 "generate_comprehensive_export",
749 e.to_string(),
750 )
751 })?;
752 let allocation_history = tracker.get_active_allocations().map_err(|e| {
753 MemScopeError::error(
754 "variable_registry",
755 "generate_comprehensive_export",
756 e.to_string(),
757 )
758 })?;
759 let memory_by_type = tracker.get_memory_by_type().map_err(|e| {
760 MemScopeError::error(
761 "variable_registry",
762 "generate_comprehensive_export",
763 e.to_string(),
764 )
765 })?;
766 let stats = tracker.get_stats().map_err(|e| {
767 MemScopeError::error(
768 "variable_registry",
769 "generate_comprehensive_export",
770 e.to_string(),
771 )
772 })?;
773 let registry = Self::get_all_variables();
774
775 tracing::info!(
776 "📊 Data loaded: {} active, {} history, {} registry entries",
777 active_allocations.len(),
778 allocation_history.len(),
779 registry.len()
780 );
781
782 let filtered_active: Vec<_> = if active_allocations.len() > 10000 {
784 active_allocations
785 .into_iter()
786 .filter(|alloc| alloc.size >= 8)
787 .collect()
788 } else {
789 active_allocations
790 };
791
792 let filtered_history: Vec<_> = if allocation_history.len() > 50000 {
793 allocation_history
794 .into_iter()
795 .filter(|alloc| alloc.size >= 8)
796 .collect()
797 } else {
798 allocation_history
799 };
800
801 let filtered_active: Vec<crate::capture::types::AllocationInfo> = filtered_active
803 .into_iter()
804 .map(|a: crate::capture::backends::core_types::AllocationInfo| a.into())
805 .collect();
806 let filtered_history: Vec<crate::capture::types::AllocationInfo> = filtered_history
807 .into_iter()
808 .map(|a: crate::capture::backends::core_types::AllocationInfo| a.into())
809 .collect();
810
811 let (classified_active, classified_history) = rayon::join(
813 || Self::classify_and_enhance_allocations(&filtered_active, ®istry),
814 || Self::classify_and_enhance_allocations(&filtered_history, ®istry),
815 );
816
817 let (user_active, system_active): (Vec<_>, Vec<_>) = classified_active
819 .into_iter()
820 .partition(|alloc| alloc["allocation_source"] == "user");
821
822 let (user_history, system_history): (Vec<_>, Vec<_>) = classified_history
823 .into_iter()
824 .partition(|alloc| alloc["allocation_source"] == "user");
825
826 let user_scopes = Self::group_by_scope(&user_active, &user_history);
828
829 let comprehensive_data = serde_json::json!({
831 "memory_analysis": {
832 "user_allocations": {
833 "active": user_active,
834 "history": user_history,
835 "by_scope": user_scopes,
836 "total_count": user_active.len() + user_history.len()
837 },
838 "system_allocations": {
839 "active": system_active,
840 "history": system_history,
841 "total_count": system_active.len() + system_history.len()
842 },
843 "memory_by_type": memory_by_type,
844 "statistics": {
845 "overall": stats,
846 "user_vs_system": {
847 "user_active_count": user_active.len(),
848 "system_active_count": system_active.len(),
849 "user_total_size": user_active.iter()
850 .map(|a| a["size"].as_u64().unwrap_or(0))
851 .sum::<u64>(),
852 "system_total_size": system_active.iter()
853 .map(|a| a["size"].as_u64().unwrap_or(0))
854 .sum::<u64>()
855 },
856 "lifecycle_analysis": Self::analyze_lifecycle_statistics(&user_active, &user_history, &system_active, &system_history),
857 "deallocation_analysis": Self::analyze_deallocation_patterns(&user_active, &user_history, &system_active, &system_history)
858 }
859 },
860 "variable_registry": {
861 "total_variables": registry.len(),
862 "user_variables": registry.values().collect::<Vec<_>>(),
863 "scope_summary": Self::get_scope_summary(®istry)
864 },
865 "export_metadata": {
866 "timestamp": std::time::SystemTime::now()
867 .duration_since(std::time::UNIX_EPOCH)
868 .unwrap_or_default()
869 .as_secs(),
870 "total_allocations": user_active.len() + user_history.len() + system_active.len() + system_history.len(),
871 "processing_time_ms": start_time.elapsed().as_millis(),
872 "classification_features": [
873 "user_vs_system_separation",
874 "scope_based_grouping",
875 "allocation_source_tracking",
876 "enhanced_type_inference"
877 ]
878 }
879 });
880
881 let total_time = start_time.elapsed();
882 tracing::info!(
883 "✅ Export completed in {:?} - User: {}, System: {}",
884 total_time,
885 user_active.len() + user_history.len(),
886 system_active.len() + system_history.len()
887 );
888
889 Ok(comprehensive_data)
890 }
891
892 pub fn clear_registry() -> MemScopeResult<()> {
894 if let Ok(mut registry) = get_global_registry().try_lock() {
895 registry.clear();
896 }
897 Ok(())
898 }
899
900 pub fn get_stats() -> (usize, usize) {
902 if let Ok(registry) = get_global_registry().try_lock() {
903 let total = registry.len();
904 let recent = registry
905 .values()
906 .filter(|v| {
907 let now = std::time::SystemTime::now()
908 .duration_since(std::time::UNIX_EPOCH)
909 .unwrap_or_default()
910 .as_nanos() as u64;
911 now - v.timestamp < 1_000_000_000 })
913 .count();
914 (total, recent)
915 } else {
916 (0, 0)
917 }
918 }
919}
920
921#[cfg(test)]
922mod tests;