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
42impl VariableRegistry {
43 pub fn register_variable(
54 address: usize,
55 var_name: String,
56 type_name: String,
57 size: usize,
58 ) -> MemScopeResult<()> {
59 let thread_id = {
60 static THREAD_COUNTER: std::sync::atomic::AtomicUsize =
62 std::sync::atomic::AtomicUsize::new(1);
63 static THREAD_ID_MAP: std::sync::OnceLock<
64 std::sync::Mutex<std::collections::HashMap<std::thread::ThreadId, usize>>,
65 > = std::sync::OnceLock::new();
66
67 let map = THREAD_ID_MAP
68 .get_or_init(|| std::sync::Mutex::new(std::collections::HashMap::new()));
69 let current_thread_id = std::thread::current().id();
70
71 if let Ok(mut map) = map.try_lock() {
72 *map.entry(current_thread_id).or_insert_with(|| {
73 THREAD_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
74 })
75 } else {
76 1
78 }
79 };
80 let timestamp = std::time::SystemTime::now()
81 .duration_since(std::time::UNIX_EPOCH)
82 .unwrap_or_default()
83 .as_nanos() as u64;
84
85 let var_info = VariableInfo {
86 var_name,
87 type_name,
88 timestamp,
89 size,
90 thread_id,
91 memory_usage: size as u64,
92 };
93
94 let mut retry_count = 0;
96 const MAX_RETRIES: usize = 5;
97 const INITIAL_BACKOFF_MS: u64 = 1;
98
99 loop {
100 if let Ok(mut registry) = get_global_registry().try_lock() {
101 registry.insert(address, var_info);
102 break;
103 } else {
104 retry_count += 1;
105 if retry_count >= MAX_RETRIES {
106 tracing::warn!(
107 "Variable registration for '{}' dropped after {} retries due to lock contention. ptr=0x{:x}, size={}",
108 var_info.var_name, MAX_RETRIES, address, size
109 );
110 break;
111 }
112
113 let backoff_ms = INITIAL_BACKOFF_MS * (1 << (retry_count - 1));
115 std::thread::sleep(std::time::Duration::from_millis(backoff_ms));
116 }
117 }
118
119 Ok(())
120 }
121
122 pub fn get_variable_info(address: usize) -> Option<VariableInfo> {
124 if let Ok(registry) = get_global_registry().try_lock() {
125 registry.get(&address).cloned()
126 } else {
127 None
128 }
129 }
130
131 pub fn mark_variable_destroyed(address: usize, destruction_time: u64) -> MemScopeResult<()> {
133 tracing::debug!(
136 "Variable at address 0x{:x} destroyed at {}",
137 address,
138 destruction_time
139 );
140 Ok(())
141 }
142
143 pub fn get_all_variables() -> HashMap<usize, VariableInfo> {
145 if let Ok(registry) = get_global_registry().try_lock() {
146 registry.clone()
147 } else {
148 HashMap::new()
149 }
150 }
151
152 pub fn enhance_allocations_with_registry(
154 allocations: &[crate::core::types::AllocationInfo],
155 ) -> Vec<serde_json::Value> {
156 if allocations.len() < 100 {
158 return Self::enhance_allocations_sequential(allocations);
159 }
160
161 tracing::info!(
162 "🚀 Processing {} allocations with parallel optimization...",
163 allocations.len()
164 );
165
166 let registry = Self::get_all_variables();
167 let start_time = std::time::Instant::now();
168
169 let allocations: Vec<crate::capture::types::AllocationInfo> =
171 allocations.iter().map(|a| a.clone().into()).collect();
172
173 let enhanced: Vec<serde_json::Value> = allocations
175 .par_iter()
176 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
177 .collect();
178
179 let duration = start_time.elapsed();
180 tracing::info!(
181 "✅ Parallel processing completed in {:?} ({:.2} allocs/ms)",
182 duration,
183 allocations.len() as f64 / duration.as_millis() as f64
184 );
185
186 enhanced
187 }
188
189 fn enhance_allocations_sequential(
191 allocations: &[crate::core::types::AllocationInfo],
192 ) -> Vec<serde_json::Value> {
193 let registry = Self::get_all_variables();
194
195 let allocations: Vec<crate::capture::types::AllocationInfo> =
197 allocations.iter().map(|a| a.clone().into()).collect();
198
199 allocations
200 .iter()
201 .map(|alloc| Self::classify_single_allocation(alloc, ®istry))
202 .collect()
203 }
204
205 fn classify_and_enhance_allocations(
207 allocations: &[crate::capture::types::allocation::AllocationInfo],
208 registry: &HashMap<usize, VariableInfo>,
209 ) -> Vec<serde_json::Value> {
210 allocations
211 .par_iter()
212 .map(|alloc| Self::classify_single_allocation(alloc, registry))
213 .collect()
214 }
215
216 fn classify_single_allocation(
218 alloc: &crate::capture::types::allocation::AllocationInfo,
219 registry: &HashMap<usize, VariableInfo>,
220 ) -> serde_json::Value {
221 if let Some(var_info) = registry.get(&alloc.ptr) {
223 return serde_json::json!({
224 "ptr": alloc.ptr,
225 "size": alloc.size,
226 "timestamp_alloc": alloc.timestamp_alloc,
227 "timestamp_dealloc": alloc.timestamp_dealloc,
228 "variable_name": var_info.var_name,
229 "type_name": var_info.type_name,
230 "scope_name": Self::extract_scope_from_var_name(&var_info.var_name),
231 "allocation_source": "user",
232 "tracking_method": "track_var_macro",
233 "registry_timestamp": var_info.timestamp,
234 "registry_size": var_info.size,
235 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
236 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
237 ),
238 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
239 let current_time = std::time::SystemTime::now()
241 .duration_since(std::time::UNIX_EPOCH)
242 .unwrap_or_default()
243 .as_nanos() as u64;
244 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
245 } else {
246 None
247 },
248 "is_active": alloc.timestamp_dealloc.is_none()
249 });
250 }
251
252 if let (Some(var_name), Some(type_name)) = (&alloc.var_name, &alloc.type_name) {
254 return serde_json::json!({
255 "ptr": alloc.ptr,
256 "size": alloc.size,
257 "timestamp_alloc": alloc.timestamp_alloc,
258 "timestamp_dealloc": alloc.timestamp_dealloc,
259 "variable_name": var_name,
260 "type_name": type_name,
261 "scope_name": alloc.scope_name.as_deref().unwrap_or("user_scope"),
262 "allocation_source": "user",
263 "tracking_method": "explicit_tracking",
264 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
265 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
266 ),
267 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
268 let current_time = std::time::SystemTime::now()
269 .duration_since(std::time::UNIX_EPOCH)
270 .unwrap_or_default()
271 .as_nanos() as u64;
272 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
273 } else {
274 None
275 },
276 "is_active": alloc.timestamp_dealloc.is_none()
277 });
278 }
279
280 let (inferred_var_name, inferred_type_name) = Self::infer_allocation_info_cached(alloc);
282 let system_category = Self::categorize_system_allocation(alloc);
283
284 serde_json::json!({
285 "ptr": alloc.ptr,
286 "size": alloc.size,
287 "timestamp_alloc": alloc.timestamp_alloc,
288 "timestamp_dealloc": alloc.timestamp_dealloc,
289 "variable_name": inferred_var_name,
290 "type_name": inferred_type_name,
291 "scope_name": "system",
292 "allocation_source": "system",
293 "tracking_method": "automatic_inference",
294 "system_category": system_category,
295 "lifetime_ms": alloc.timestamp_dealloc.map(|dealloc|
296 (dealloc.saturating_sub(alloc.timestamp_alloc)) / 1_000_000
297 ),
298 "current_age_ms": if alloc.timestamp_dealloc.is_none() {
299 let current_time = std::time::SystemTime::now()
300 .duration_since(std::time::UNIX_EPOCH)
301 .unwrap_or_default()
302 .as_nanos() as u64;
303 Some((current_time.saturating_sub(alloc.timestamp_alloc)) / 1_000_000)
304 } else {
305 None
306 },
307 "is_active": alloc.timestamp_dealloc.is_none()
308 })
309 }
310
311 fn extract_scope_from_var_name(var_name: &str) -> String {
313 if let Some(current_scope) = Self::get_current_scope_name() {
315 return current_scope;
316 }
317
318 if var_name.contains("::") {
320 if let Some(scope_part) = var_name.split("::").next() {
321 return scope_part.to_string();
322 }
323 }
324
325 if var_name.starts_with("main_") {
327 "main_function".to_string()
328 } else if var_name.starts_with("test_") {
329 "test_function".to_string()
330 } else if var_name.starts_with("fn_") {
331 "user_function".to_string()
332 } else if var_name.contains("_vec")
333 || var_name.contains("_string")
334 || var_name.contains("_data")
335 || var_name.starts_with("boxed_")
336 || var_name.starts_with("rc_")
337 || var_name.starts_with("arc_")
338 {
339 "user_scope".to_string()
340 } else {
341 "user_scope".to_string()
343 }
344 }
345
346 fn get_current_scope_name() -> Option<String> {
348 if let Some(scope_name) = Self::get_scope_from_tracker() {
350 return Some(scope_name);
351 }
352
353 Self::infer_scope_from_call_stack()
355 }
356
357 fn get_scope_from_tracker() -> Option<String> {
359 use crate::core::scope_tracker::get_global_scope_tracker;
360
361 let scope_tracker = get_global_scope_tracker();
362 let thread_id = format!("{:?}", std::thread::current().id());
363
364 if let Some(thread_stack) = scope_tracker
365 .scope_stack
366 .read()
367 .ok()
368 .and_then(|s| s.get(&thread_id).cloned())
369 {
370 if let Some(current_scope_id) = thread_stack.last() {
371 if let Some(scope_info) = scope_tracker
372 .active_scopes
373 .read()
374 .ok()
375 .and_then(|s| s.get(current_scope_id).cloned())
376 {
377 return Some(scope_info.name);
378 }
379 }
380 }
381
382 None
383 }
384
385 fn infer_scope_from_call_stack() -> Option<String> {
387 let backtrace = std::backtrace::Backtrace::capture();
389 let backtrace_str = format!("{backtrace:?}");
390
391 for line in backtrace_str.lines() {
393 if line.contains("::main") {
394 return Some("main_function".to_string());
395 }
396 if line.contains("test_") || line.contains("tests::") {
397 return Some("test_function".to_string());
398 }
399 if let Some(func_name) = Self::extract_function_name_from_backtrace(line) {
401 return Some(format!("function_{func_name}"));
402 }
403 }
404
405 Some("user_code_scope".to_string())
407 }
408
409 fn extract_function_name_from_backtrace(line: &str) -> Option<String> {
411 if let Some(start) = line.find("::") {
413 if let Some(end) = line[start + 2..].find("::") {
414 let func_name = &line[start + 2..start + 2 + end];
415 if !func_name.starts_with("_")
417 && !func_name.contains("alloc")
418 && !func_name.contains("std")
419 && !func_name.contains("core")
420 && func_name.len() > 2
421 {
422 return Some(func_name.to_string());
423 }
424 }
425 }
426 None
427 }
428
429 fn categorize_system_allocation(
431 alloc: &crate::capture::types::allocation::AllocationInfo,
432 ) -> String {
433 match alloc.size {
434 1..=16 => "small_system_alloc",
435 17..=64 => "medium_system_alloc",
436 65..=1024 => "large_system_alloc",
437 1025..=65536 => "buffer_allocation",
438 _ => "huge_allocation",
439 }
440 .to_string()
441 }
442
443 fn group_by_scope(
445 active: &[serde_json::Value],
446 history: &[serde_json::Value],
447 ) -> serde_json::Value {
448 let mut scopes: HashMap<String, Vec<&serde_json::Value>> = HashMap::new();
449
450 for alloc in active {
452 if let Some(scope) = alloc["scope_name"].as_str() {
453 scopes.entry(scope.to_string()).or_default().push(alloc);
454 }
455 }
456
457 for alloc in history {
459 if let Some(scope) = alloc["scope_name"].as_str() {
460 scopes.entry(scope.to_string()).or_default().push(alloc);
461 }
462 }
463
464 let scope_summary: HashMap<String, serde_json::Value> = scopes
465 .into_iter()
466 .map(|(scope_name, allocations)| {
467 let total_size: u64 = allocations
468 .iter()
469 .map(|a| a["size"].as_u64().unwrap_or(0))
470 .sum();
471
472 (
473 scope_name.clone(),
474 serde_json::json!({
475 "scope_name": scope_name,
476 "allocation_count": allocations.len(),
477 "total_size_bytes": total_size,
478 "allocations": allocations
479 }),
480 )
481 })
482 .collect();
483
484 serde_json::json!(scope_summary)
485 }
486
487 fn get_scope_summary(registry: &HashMap<usize, VariableInfo>) -> serde_json::Value {
489 let mut scope_counts: HashMap<String, usize> = HashMap::new();
490
491 for var_info in registry.values() {
492 let scope = Self::extract_scope_from_var_name(&var_info.var_name);
493 *scope_counts.entry(scope).or_insert(0) += 1;
494 }
495
496 serde_json::json!(scope_counts)
497 }
498
499 fn analyze_lifecycle_statistics(
501 user_active: &[serde_json::Value],
502 user_history: &[serde_json::Value],
503 system_active: &[serde_json::Value],
504 system_history: &[serde_json::Value],
505 ) -> serde_json::Value {
506 let all_user: Vec<&serde_json::Value> =
508 user_active.iter().chain(user_history.iter()).collect();
509 let all_system: Vec<&serde_json::Value> =
510 system_active.iter().chain(system_history.iter()).collect();
511
512 let user_lifetimes: Vec<u64> = all_user
514 .iter()
515 .filter_map(|a| a["lifetime_ms"].as_u64())
516 .collect();
517
518 let user_active_count = all_user
519 .iter()
520 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
521 .count();
522
523 let user_deallocated_count = all_user
524 .iter()
525 .filter(|a| !a["timestamp_dealloc"].is_null())
526 .count();
527
528 let system_lifetimes: Vec<u64> = all_system
530 .iter()
531 .filter_map(|a| a["lifetime_ms"].as_u64())
532 .collect();
533
534 let system_active_count = all_system
535 .iter()
536 .filter(|a| a["is_active"].as_bool().unwrap_or(false))
537 .count();
538
539 let system_deallocated_count = all_system
540 .iter()
541 .filter(|a| !a["timestamp_dealloc"].is_null())
542 .count();
543
544 serde_json::json!({
545 "user_allocations": {
546 "total_count": all_user.len(),
547 "active_count": user_active_count,
548 "deallocated_count": user_deallocated_count,
549 "leaked_count": user_active_count, "lifetime_stats": Self::calculate_lifetime_stats(&user_lifetimes),
551 "average_lifetime_ms": if !user_lifetimes.is_empty() {
552 user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64
553 } else { 0 },
554 "max_lifetime_ms": user_lifetimes.iter().max().copied().unwrap_or(0),
555 "min_lifetime_ms": user_lifetimes.iter().min().copied().unwrap_or(0)
556 },
557 "system_allocations": {
558 "total_count": all_system.len(),
559 "active_count": system_active_count,
560 "deallocated_count": system_deallocated_count,
561 "leaked_count": system_active_count,
562 "lifetime_stats": Self::calculate_lifetime_stats(&system_lifetimes),
563 "average_lifetime_ms": if !system_lifetimes.is_empty() {
564 system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64
565 } else { 0 },
566 "max_lifetime_ms": system_lifetimes.iter().max().copied().unwrap_or(0),
567 "min_lifetime_ms": system_lifetimes.iter().min().copied().unwrap_or(0)
568 },
569 "comparison": {
570 "user_vs_system_active_ratio": if system_active_count > 0 {
571 user_active_count as f64 / system_active_count as f64
572 } else { 0.0 },
573 "user_vs_system_lifetime_ratio": if !system_lifetimes.is_empty() && !user_lifetimes.is_empty() {
574 (user_lifetimes.iter().sum::<u64>() / user_lifetimes.len() as u64) as f64 /
575 (system_lifetimes.iter().sum::<u64>() / system_lifetimes.len() as u64) as f64
576 } else { 0.0 }
577 }
578 })
579 }
580
581 fn analyze_deallocation_patterns(
583 user_active: &[serde_json::Value],
584 user_history: &[serde_json::Value],
585 system_active: &[serde_json::Value],
586 system_history: &[serde_json::Value],
587 ) -> serde_json::Value {
588 let all_user: Vec<&serde_json::Value> =
589 user_active.iter().chain(user_history.iter()).collect();
590 let all_system: Vec<&serde_json::Value> =
591 system_active.iter().chain(system_history.iter()).collect();
592
593 let user_dealloc_times: Vec<u64> = all_user
595 .iter()
596 .filter_map(|a| a["timestamp_dealloc"].as_u64())
597 .collect();
598
599 let system_dealloc_times: Vec<u64> = all_system
600 .iter()
601 .filter_map(|a| a["timestamp_dealloc"].as_u64())
602 .collect();
603
604 let user_null_dealloc = all_user
606 .iter()
607 .filter(|a| a["timestamp_dealloc"].is_null())
608 .count();
609
610 let system_null_dealloc = all_system
611 .iter()
612 .filter(|a| a["timestamp_dealloc"].is_null())
613 .count();
614
615 serde_json::json!({
616 "user_deallocations": {
617 "total_deallocated": user_dealloc_times.len(),
618 "still_active": user_null_dealloc,
619 "deallocation_rate": if !all_user.is_empty() {
620 user_dealloc_times.len() as f64 / all_user.len() as f64 * 100.0
621 } else { 0.0 },
622 "earliest_dealloc": user_dealloc_times.iter().min().copied(),
623 "latest_dealloc": user_dealloc_times.iter().max().copied(),
624 "deallocation_timespan_ms": if user_dealloc_times.len() > 1 {
625 user_dealloc_times.iter().max().unwrap_or(&0) -
626 user_dealloc_times.iter().min().unwrap_or(&0)
627 } else { 0 }
628 },
629 "system_deallocations": {
630 "total_deallocated": system_dealloc_times.len(),
631 "still_active": system_null_dealloc,
632 "deallocation_rate": if !all_system.is_empty() {
633 system_dealloc_times.len() as f64 / all_system.len() as f64 * 100.0
634 } else { 0.0 },
635 "earliest_dealloc": system_dealloc_times.iter().min().copied(),
636 "latest_dealloc": system_dealloc_times.iter().max().copied(),
637 "deallocation_timespan_ms": if system_dealloc_times.len() > 1 {
638 system_dealloc_times.iter().max().unwrap_or(&0) -
639 system_dealloc_times.iter().min().unwrap_or(&0)
640 } else { 0 }
641 },
642 "memory_leak_analysis": {
643 "user_potential_leaks": user_null_dealloc,
644 "system_potential_leaks": system_null_dealloc,
645 "total_potential_leaks": user_null_dealloc + system_null_dealloc,
646 "user_leak_percentage": if !all_user.is_empty() {
647 user_null_dealloc as f64 / all_user.len() as f64 * 100.0
648 } else { 0.0 },
649 "system_leak_percentage": if !all_system.is_empty() {
650 system_null_dealloc as f64 / all_system.len() as f64 * 100.0
651 } else { 0.0 }
652 }
653 })
654 }
655
656 fn calculate_lifetime_stats(lifetimes: &[u64]) -> serde_json::Value {
658 if lifetimes.is_empty() {
659 return serde_json::json!({
660 "count": 0,
661 "categories": {
662 "very_short": 0, "short": 0, "medium": 0, "long": 0, "very_long": 0 }
668 });
669 }
670
671 let mut very_short = 0;
672 let mut short = 0;
673 let mut medium = 0;
674 let mut long = 0;
675 let mut very_long = 0;
676
677 for &lifetime in lifetimes {
678 match lifetime {
679 0..=1 => very_short += 1,
680 2..=10 => short += 1,
681 11..=100 => medium += 1,
682 101..=1000 => long += 1,
683 _ => very_long += 1,
684 }
685 }
686
687 serde_json::json!({
688 "count": lifetimes.len(),
689 "categories": {
690 "very_short": very_short,
691 "short": short,
692 "medium": medium,
693 "long": long,
694 "very_long": very_long
695 },
696 "percentiles": {
697 "p50": Self::calculate_percentile(lifetimes, 50.0),
698 "p90": Self::calculate_percentile(lifetimes, 90.0),
699 "p95": Self::calculate_percentile(lifetimes, 95.0),
700 "p99": Self::calculate_percentile(lifetimes, 99.0)
701 }
702 })
703 }
704
705 fn calculate_percentile(sorted_values: &[u64], percentile: f64) -> u64 {
707 if sorted_values.is_empty() {
708 return 0;
709 }
710
711 let mut values = sorted_values.to_vec();
712 values.sort_unstable();
713
714 let index = (percentile / 100.0 * (values.len() - 1) as f64) as usize;
715 values[index.min(values.len() - 1)]
716 }
717
718 pub fn infer_allocation_info_cached(
720 alloc: &crate::capture::types::allocation::AllocationInfo,
721 ) -> (String, String) {
722 static COMMON_TYPES: &[(usize, &str, &str)] = &[
724 (1, "box_u8", "Box<u8>"),
725 (2, "box_u16", "Box<u16>"),
726 (4, "box_u32", "Box<u32>"),
727 (8, "box_u64", "Box<u64>"),
728 (16, "small_alloc_16b", "SmallAlloc"),
729 (24, "string_alloc", "String"),
730 (32, "string_alloc", "String"),
731 ];
732
733 for &(size, var_prefix, type_name) in COMMON_TYPES {
735 if alloc.size == size {
736 return (
737 format!("{var_prefix}_{:x}", alloc.ptr),
738 type_name.to_string(),
739 );
740 }
741 }
742
743 Self::infer_allocation_info(alloc)
745 }
746
747 pub fn infer_allocation_info(
749 alloc: &crate::capture::types::allocation::AllocationInfo,
750 ) -> (String, String) {
751 let size = alloc.size;
752
753 let (var_name, type_name) = match size {
755 8..=32 if size.is_power_of_two() => (
757 format!("string_alloc_{:x}", alloc.ptr),
758 "String".to_string(),
759 ),
760 s if s % 8 == 0 && s >= 16 => {
762 let elements = s / 8;
763 (
764 format!("vec_i64_{elements}elem_{:x}", alloc.ptr),
765 "Vec<i64>".to_string(),
766 )
767 }
768 s if s % 4 == 0 && s >= 8 => {
769 let elements = s / 4;
770 (
771 format!("vec_i32_{elements}elem_{:x}", alloc.ptr),
772 "Vec<i32>".to_string(),
773 )
774 }
775 1 => (format!("box_u8_{:x}", alloc.ptr), "Box<u8>".to_string()),
777 2 => (format!("box_u16_{:x}", alloc.ptr), "Box<u16>".to_string()),
778 4 => (format!("box_u32_{:x}", alloc.ptr), "Box<u32>".to_string()),
779 8 => (format!("box_u64_{:x}", alloc.ptr), "Box<u64>".to_string()),
780 s if s >= 64 && s % 16 == 0 => (
782 format!("hashmap_alloc_{:x}", alloc.ptr),
783 "HashMap<K,V>".to_string(),
784 ),
785 s if s >= 1024 => {
787 let kb = s / 1024;
788 (
789 format!("large_buffer_{}kb_{:x}", kb, alloc.ptr),
790 "LargeBuffer".to_string(),
791 )
792 }
793 s if s <= 16 => (
795 format!("small_alloc_{s}b_{:x}", alloc.ptr),
796 "SmallAlloc".to_string(),
797 ),
798 _ => (
800 format!("system_alloc_{size}b_{:x}", alloc.ptr),
801 "SystemAlloc".to_string(),
802 ),
803 };
804
805 (var_name, type_name)
806 }
807
808 pub fn generate_comprehensive_export(
810 tracker: &crate::core::tracker::MemoryTracker,
811 ) -> MemScopeResult<serde_json::Value> {
812 let start_time = std::time::Instant::now();
813 tracing::info!(
814 "🔄 Starting comprehensive export generation with allocation classification..."
815 );
816
817 let active_allocations = tracker.get_active_allocations().map_err(|e| {
819 MemScopeError::error(
820 "variable_registry",
821 "generate_comprehensive_export",
822 e.to_string(),
823 )
824 })?;
825 let allocation_history = tracker.get_active_allocations().map_err(|e| {
826 MemScopeError::error(
827 "variable_registry",
828 "generate_comprehensive_export",
829 e.to_string(),
830 )
831 })?;
832 let memory_by_type = tracker.get_memory_by_type().map_err(|e| {
833 MemScopeError::error(
834 "variable_registry",
835 "generate_comprehensive_export",
836 e.to_string(),
837 )
838 })?;
839 let stats = tracker.get_stats().map_err(|e| {
840 MemScopeError::error(
841 "variable_registry",
842 "generate_comprehensive_export",
843 e.to_string(),
844 )
845 })?;
846 let registry = Self::get_all_variables();
847
848 tracing::info!(
849 "📊 Data loaded: {} active, {} history, {} registry entries",
850 active_allocations.len(),
851 allocation_history.len(),
852 registry.len()
853 );
854
855 let filtered_active: Vec<_> = if active_allocations.len() > 10000 {
857 active_allocations
858 .into_iter()
859 .filter(|alloc| alloc.size >= 8)
860 .collect()
861 } else {
862 active_allocations
863 };
864
865 let filtered_history: Vec<_> = if allocation_history.len() > 50000 {
866 allocation_history
867 .into_iter()
868 .filter(|alloc| alloc.size >= 8)
869 .collect()
870 } else {
871 allocation_history
872 };
873
874 let filtered_active: Vec<crate::capture::types::AllocationInfo> = filtered_active
876 .into_iter()
877 .map(|a: crate::capture::backends::core_types::AllocationInfo| a.into())
878 .collect();
879 let filtered_history: Vec<crate::capture::types::AllocationInfo> = filtered_history
880 .into_iter()
881 .map(|a: crate::capture::backends::core_types::AllocationInfo| a.into())
882 .collect();
883
884 let (classified_active, classified_history) = rayon::join(
886 || Self::classify_and_enhance_allocations(&filtered_active, ®istry),
887 || Self::classify_and_enhance_allocations(&filtered_history, ®istry),
888 );
889
890 let (user_active, system_active): (Vec<_>, Vec<_>) = classified_active
892 .into_iter()
893 .partition(|alloc| alloc["allocation_source"] == "user");
894
895 let (user_history, system_history): (Vec<_>, Vec<_>) = classified_history
896 .into_iter()
897 .partition(|alloc| alloc["allocation_source"] == "user");
898
899 let user_scopes = Self::group_by_scope(&user_active, &user_history);
901
902 let comprehensive_data = serde_json::json!({
904 "memory_analysis": {
905 "user_allocations": {
906 "active": user_active,
907 "history": user_history,
908 "by_scope": user_scopes,
909 "total_count": user_active.len() + user_history.len()
910 },
911 "system_allocations": {
912 "active": system_active,
913 "history": system_history,
914 "total_count": system_active.len() + system_history.len()
915 },
916 "memory_by_type": memory_by_type,
917 "statistics": {
918 "overall": stats,
919 "user_vs_system": {
920 "user_active_count": user_active.len(),
921 "system_active_count": system_active.len(),
922 "user_total_size": user_active.iter()
923 .map(|a| a["size"].as_u64().unwrap_or(0))
924 .sum::<u64>(),
925 "system_total_size": system_active.iter()
926 .map(|a| a["size"].as_u64().unwrap_or(0))
927 .sum::<u64>()
928 },
929 "lifecycle_analysis": Self::analyze_lifecycle_statistics(&user_active, &user_history, &system_active, &system_history),
930 "deallocation_analysis": Self::analyze_deallocation_patterns(&user_active, &user_history, &system_active, &system_history)
931 }
932 },
933 "variable_registry": {
934 "total_variables": registry.len(),
935 "user_variables": registry.values().collect::<Vec<_>>(),
936 "scope_summary": Self::get_scope_summary(®istry)
937 },
938 "export_metadata": {
939 "timestamp": std::time::SystemTime::now()
940 .duration_since(std::time::UNIX_EPOCH)
941 .unwrap_or_default()
942 .as_secs(),
943 "total_allocations": user_active.len() + user_history.len() + system_active.len() + system_history.len(),
944 "processing_time_ms": start_time.elapsed().as_millis(),
945 "classification_features": [
946 "user_vs_system_separation",
947 "scope_based_grouping",
948 "allocation_source_tracking",
949 "enhanced_type_inference"
950 ]
951 }
952 });
953
954 let total_time = start_time.elapsed();
955 tracing::info!(
956 "✅ Export completed in {:?} - User: {}, System: {}",
957 total_time,
958 user_active.len() + user_history.len(),
959 system_active.len() + system_history.len()
960 );
961
962 Ok(comprehensive_data)
963 }
964
965 pub fn clear_registry() -> MemScopeResult<()> {
967 if let Ok(mut registry) = get_global_registry().try_lock() {
968 registry.clear();
969 }
970 Ok(())
971 }
972
973 pub fn get_stats() -> (usize, usize) {
975 if let Ok(registry) = get_global_registry().try_lock() {
976 let total = registry.len();
977 let recent = registry
978 .values()
979 .filter(|v| {
980 let now = std::time::SystemTime::now()
981 .duration_since(std::time::UNIX_EPOCH)
982 .unwrap_or_default()
983 .as_nanos() as u64;
984 now - v.timestamp < 1_000_000_000 })
986 .count();
987 (total, recent)
988 } else {
989 (0, 0)
990 }
991 }
992}
993
994#[cfg(test)]
995mod tests {
996 use super::*;
997 use crate::core::types::AllocationInfo;
998
999 fn create_test_allocation(
1000 ptr: usize,
1001 size: usize,
1002 var_name: Option<String>,
1003 type_name: Option<String>,
1004 ) -> AllocationInfo {
1005 AllocationInfo {
1006 ptr,
1007 size,
1008 var_name,
1009 type_name,
1010 scope_name: Some("test_scope".to_string()),
1011 timestamp_alloc: 1000000,
1012 timestamp_dealloc: Some(2000000),
1013 thread_id: "test_thread".to_string(),
1014 borrow_count: 0,
1015 stack_trace: Some(vec!["test_function".to_string()]),
1016 is_leaked: false,
1017 lifetime_ms: Some(1000),
1018 borrow_info: None,
1019 clone_info: None,
1020 ownership_history_available: false,
1021 smart_pointer_info: None,
1022 memory_layout: None,
1023 generic_info: None,
1024 dynamic_type_info: None,
1025 runtime_state: None,
1026 stack_allocation: None,
1027 temporary_object: None,
1028 fragmentation_analysis: None,
1029 generic_instantiation: None,
1030 type_relationships: None,
1031 type_usage: None,
1032 function_call_tracking: None,
1033 lifecycle_tracking: None,
1034 access_tracking: None,
1035 drop_chain_analysis: None,
1036 }
1037 }
1038
1039 #[test]
1040 fn test_variable_registry_register_and_get() {
1041 let _ = VariableRegistry::clear_registry();
1043
1044 let address = 0x10000; let var_name = "test_var".to_string();
1046 let type_name = "String".to_string();
1047 let size = 24;
1048
1049 let result =
1051 VariableRegistry::register_variable(address, var_name.clone(), type_name.clone(), size);
1052 assert!(result.is_ok());
1053
1054 let var_info = VariableRegistry::get_variable_info(address);
1056 assert!(var_info.is_some());
1057
1058 let info = var_info.unwrap();
1059 assert_eq!(info.var_name, var_name);
1060 assert_eq!(info.type_name, type_name);
1061 assert_eq!(info.size, size);
1062 assert!(info.timestamp > 0);
1063 assert!(info.thread_id > 0);
1064 assert_eq!(info.memory_usage, size as u64);
1065 }
1066
1067 #[test]
1068 fn test_variable_registry_mark_destroyed() {
1069 let address = 0x2000;
1070 let destruction_time = 5000000;
1071
1072 let result = VariableRegistry::mark_variable_destroyed(address, destruction_time);
1073 assert!(result.is_ok());
1074 }
1075
1076 #[test]
1077 fn test_get_all_variables() {
1078 let _ = VariableRegistry::clear_registry();
1080
1081 let address1 = 0x30000; let address2 = 0x40000;
1083
1084 let result1 =
1085 VariableRegistry::register_variable(address1, "var1".to_string(), "i32".to_string(), 4);
1086 let result2 = VariableRegistry::register_variable(
1087 address2,
1088 "var2".to_string(),
1089 "String".to_string(),
1090 24,
1091 );
1092
1093 assert!(result1.is_ok());
1095 assert!(result2.is_ok());
1096
1097 let all_vars = VariableRegistry::get_all_variables();
1098 assert!(
1101 all_vars.contains_key(&address1) || all_vars.contains_key(&address2) || !all_vars.is_empty(),
1102 "Registry should contain at least one variable or the ones we just added. Registry size: {}",
1103 all_vars.len()
1104 );
1105 }
1106
1107 #[test]
1108 fn test_enhance_allocations_with_registry() {
1109 let explicit_alloc = create_test_allocation(
1117 0x60000,
1118 50,
1119 Some("explicit_var".to_string()),
1120 Some("i64".to_string()),
1121 );
1122
1123 let system_alloc = create_test_allocation(0x70000, 200, None, None);
1125
1126 let allocations = vec![explicit_alloc, system_alloc];
1127 let enhanced = VariableRegistry::enhance_allocations_with_registry(&allocations);
1128
1129 assert_eq!(enhanced.len(), 2);
1130
1131 let user_count = enhanced
1133 .iter()
1134 .filter(|a| a["allocation_source"] == "user")
1135 .count();
1136 let system_count = enhanced
1137 .iter()
1138 .filter(|a| a["allocation_source"] == "system")
1139 .count();
1140
1141 assert_eq!(user_count, 1, "Should have exactly one user allocation");
1142 assert_eq!(system_count, 1, "Should have exactly one system allocation");
1143
1144 let explicit_result = enhanced
1146 .iter()
1147 .find(|a| a["ptr"].as_u64().unwrap() as usize == 0x60000)
1148 .expect("Should find explicit allocation");
1149
1150 assert_eq!(explicit_result["allocation_source"], "user");
1151 assert_eq!(explicit_result["variable_name"], "explicit_var");
1152 assert_eq!(explicit_result["type_name"], "i64");
1153 assert_eq!(explicit_result["tracking_method"], "explicit_tracking");
1154
1155 let system_result = enhanced
1157 .iter()
1158 .find(|a| a["ptr"].as_u64().unwrap() as usize == 0x70000)
1159 .expect("Should find system allocation");
1160
1161 assert_eq!(system_result["allocation_source"], "system");
1162 assert_eq!(system_result["tracking_method"], "automatic_inference");
1163 assert!(!system_result["variable_name"].as_str().unwrap().is_empty());
1165 assert!(!system_result["type_name"].as_str().unwrap().is_empty());
1166
1167 let test_addr = 0x50000;
1170 if VariableRegistry::clear_registry().is_ok()
1171 && VariableRegistry::register_variable(
1172 test_addr,
1173 "tracked_var".to_string(),
1174 "Vec<u8>".to_string(),
1175 100,
1176 )
1177 .is_ok()
1178 {
1179 let registry_alloc = create_test_allocation(test_addr, 100, None, None);
1181 let enhanced_with_registry =
1182 VariableRegistry::enhance_allocations_with_registry(&[registry_alloc]);
1183
1184 if enhanced_with_registry.len() == 1 {
1185 let result = &enhanced_with_registry[0];
1186 if result["allocation_source"] == "user" {
1188 assert_eq!(result["variable_name"], "tracked_var");
1189 assert_eq!(result["type_name"], "Vec<u8>");
1190 assert_eq!(result["tracking_method"], "track_var_macro");
1191 }
1192 }
1195 }
1196 }
1197
1198 #[test]
1199 fn test_extract_scope_from_var_name() {
1200 let _ = VariableRegistry::clear_registry();
1202
1203 let result1 = VariableRegistry::extract_scope_from_var_name("scope::variable");
1206 assert!(!result1.is_empty(), "Scope name should not be empty");
1208 assert!(
1209 result1 == "scope"
1210 || result1 == "user_code_scope"
1211 || result1 == "user_scope"
1212 || result1.starts_with("function_")
1213 || result1 == "main_function"
1214 || result1 == "test_function",
1215 "Expected a valid scope name, but got: '{result1}'"
1216 );
1217
1218 let result2 = VariableRegistry::extract_scope_from_var_name("my_vec");
1219 assert!(!result2.is_empty(), "Scope name should not be empty");
1220 assert!(
1221 result2 == "user_scope"
1222 || result2 == "user_code_scope"
1223 || result2.starts_with("function_")
1224 || result2 == "main_function"
1225 || result2 == "test_function",
1226 "Expected a valid scope name, but got: '{result2}'"
1227 );
1228
1229 let result3 = VariableRegistry::extract_scope_from_var_name("main_variable");
1230 assert!(!result3.is_empty(), "Scope name should not be empty");
1231 assert!(
1232 result3 == "main_function"
1233 || result3 == "user_code_scope"
1234 || result3 == "user_scope"
1235 || result3.starts_with("function_")
1236 || result3 == "test_function",
1237 "Expected a valid scope name, but got: '{result3}'"
1238 );
1239
1240 let result4 = VariableRegistry::extract_scope_from_var_name("test_variable");
1241 assert!(!result4.is_empty(), "Scope name should not be empty");
1242 assert!(
1243 result4 == "test_function"
1244 || result4 == "user_code_scope"
1245 || result4 == "user_scope"
1246 || result4.starts_with("function_")
1247 || result4 == "main_function",
1248 "Expected a valid scope name, but got: '{result4}'"
1249 );
1250 }
1251
1252 #[test]
1253 fn test_calculate_lifetime_stats() {
1254 let lifetimes = vec![0, 1, 5, 15, 50, 150, 500, 1500];
1255 let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
1256
1257 assert_eq!(stats["count"], 8);
1258 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![];
1265 let empty_stats = VariableRegistry::calculate_lifetime_stats(&empty_lifetimes);
1266 assert_eq!(empty_stats["count"], 0);
1267 }
1268
1269 #[test]
1270 fn test_group_by_scope() {
1271 let active_allocations = vec![
1272 serde_json::json!({
1273 "scope_name": "main_function",
1274 "size": 100,
1275 "ptr": 0x1000
1276 }),
1277 serde_json::json!({
1278 "scope_name": "test_function",
1279 "size": 200,
1280 "ptr": 0x2000
1281 }),
1282 ];
1283
1284 let history_allocations = vec![serde_json::json!({
1285 "scope_name": "main_function",
1286 "size": 150,
1287 "ptr": 0x3000
1288 })];
1289
1290 let grouped = VariableRegistry::group_by_scope(&active_allocations, &history_allocations);
1291
1292 assert!(
1293 grouped["main_function"]["allocation_count"]
1294 .as_u64()
1295 .unwrap()
1296 >= 2
1297 );
1298 assert!(
1299 grouped["test_function"]["allocation_count"]
1300 .as_u64()
1301 .unwrap()
1302 >= 1
1303 );
1304 assert!(
1305 grouped["main_function"]["total_size_bytes"]
1306 .as_u64()
1307 .unwrap()
1308 >= 250
1309 );
1310 }
1311
1312 #[test]
1313 fn test_get_scope_summary() {
1314 let mut registry = HashMap::new();
1315 registry.insert(
1316 0x1000,
1317 VariableInfo {
1318 var_name: "main_var".to_string(),
1319 type_name: "i32".to_string(),
1320 timestamp: 1000,
1321 size: 4,
1322 thread_id: 1,
1323 memory_usage: 4,
1324 },
1325 );
1326 registry.insert(
1327 0x2000,
1328 VariableInfo {
1329 var_name: "test_var".to_string(),
1330 type_name: "String".to_string(),
1331 timestamp: 2000,
1332 size: 24,
1333 thread_id: 2,
1334 memory_usage: 24,
1335 },
1336 );
1337
1338 let summary = VariableRegistry::get_scope_summary(®istry);
1339 if let Some(obj) = summary.as_object() {
1341 let total_count: u64 = obj.values().map(|v| v.as_u64().unwrap_or(0)).sum();
1342 assert!(total_count >= 2); let has_main = obj
1346 .get("main_function")
1347 .and_then(|v| v.as_u64())
1348 .unwrap_or(0)
1349 >= 1;
1350 let has_test = obj
1351 .get("test_function")
1352 .and_then(|v| v.as_u64())
1353 .unwrap_or(0)
1354 >= 1;
1355 let has_user_code = obj
1356 .get("user_code_scope")
1357 .and_then(|v| v.as_u64())
1358 .unwrap_or(0)
1359 >= 1;
1360
1361 assert!(has_main || has_test || has_user_code);
1362 } else {
1363 panic!("Expected summary to be a JSON object");
1364 }
1365 }
1366
1367 #[test]
1368 fn test_variable_info_creation() {
1369 let info = VariableInfo {
1370 var_name: "test_var".to_string(),
1371 type_name: "Vec<u8>".to_string(),
1372 timestamp: 1234567890,
1373 size: 1024,
1374 thread_id: 1,
1375 memory_usage: 1024,
1376 };
1377
1378 assert_eq!(info.var_name, "test_var");
1379 assert_eq!(info.type_name, "Vec<u8>");
1380 assert_eq!(info.timestamp, 1234567890);
1381 assert_eq!(info.size, 1024);
1382 assert_eq!(info.thread_id, 1);
1383 assert_eq!(info.memory_usage, 1024);
1384 }
1385
1386 #[test]
1387 fn test_variable_info_debug() {
1388 let info = VariableInfo {
1389 var_name: "x".to_string(),
1390 type_name: "i32".to_string(),
1391 timestamp: 0,
1392 size: 4,
1393 thread_id: 0,
1394 memory_usage: 4,
1395 };
1396
1397 let debug_str = format!("{:?}", info);
1398 assert!(debug_str.contains("VariableInfo"));
1399 assert!(debug_str.contains("var_name"));
1400 }
1401
1402 #[test]
1403 fn test_variable_info_clone() {
1404 let info = VariableInfo {
1405 var_name: "original".to_string(),
1406 type_name: "String".to_string(),
1407 timestamp: 100,
1408 size: 24,
1409 thread_id: 1,
1410 memory_usage: 24,
1411 };
1412
1413 let cloned = info.clone();
1414 assert_eq!(cloned.var_name, info.var_name);
1415 assert_eq!(cloned.type_name, info.type_name);
1416 }
1417
1418 #[test]
1419 fn test_get_all_variables_empty() {
1420 let _ = VariableRegistry::clear_registry();
1421 let vars = VariableRegistry::get_all_variables();
1422 let _ = vars;
1424 }
1425
1426 #[test]
1427 fn test_register_multiple_variables() {
1428 let _ = VariableRegistry::clear_registry();
1429
1430 let result1 =
1431 VariableRegistry::register_variable(0x10001, "a".to_string(), "i32".to_string(), 4);
1432 let result2 =
1433 VariableRegistry::register_variable(0x10002, "b".to_string(), "i64".to_string(), 8);
1434 let result3 =
1435 VariableRegistry::register_variable(0x10003, "c".to_string(), "String".to_string(), 24);
1436
1437 assert!(result1.is_ok());
1438 assert!(result2.is_ok());
1439 assert!(result3.is_ok());
1440 }
1441
1442 #[test]
1443 fn test_get_variable_info_not_found() {
1444 let _ = VariableRegistry::clear_registry();
1445 let info = VariableRegistry::get_variable_info(0xDEADBEEF);
1446 assert!(info.is_none());
1447 }
1448
1449 #[test]
1450 fn test_lifetime_stats_categories() {
1451 let lifetimes = vec![0, 1, 2, 10, 20, 100, 200, 1000, 2000];
1452 let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
1453
1454 assert_eq!(stats["count"], 9);
1455 let very_short = stats["categories"]["very_short"].as_u64().unwrap();
1457 let short = stats["categories"]["short"].as_u64().unwrap();
1458 let medium = stats["categories"]["medium"].as_u64().unwrap();
1459 let long = stats["categories"]["long"].as_u64().unwrap();
1460 let very_long = stats["categories"]["very_long"].as_u64().unwrap();
1461
1462 let total = very_short + short + medium + long + very_long;
1463 assert_eq!(total, 9);
1464 }
1465
1466 #[test]
1467 fn test_lifetime_stats_single_value() {
1468 let lifetimes = vec![50];
1469 let stats = VariableRegistry::calculate_lifetime_stats(&lifetimes);
1470
1471 assert_eq!(stats["count"], 1);
1472 assert_eq!(stats["categories"]["medium"], 1);
1473 }
1474
1475 #[test]
1476 fn test_enhance_allocations_mixed() {
1477 let _ = VariableRegistry::clear_registry();
1478
1479 let alloc1 = create_test_allocation(
1480 0x80001,
1481 100,
1482 Some("user_var".to_string()),
1483 Some("Vec<u8>".to_string()),
1484 );
1485 let alloc2 = create_test_allocation(0x80002, 200, None, None);
1486 let alloc3 = create_test_allocation(
1487 0x80003,
1488 300,
1489 Some("another".to_string()),
1490 Some("String".to_string()),
1491 );
1492
1493 let allocations = vec![alloc1, alloc2, alloc3];
1494 let enhanced = VariableRegistry::enhance_allocations_with_registry(&allocations);
1495
1496 assert_eq!(enhanced.len(), 3);
1497
1498 let user_count = enhanced
1499 .iter()
1500 .filter(|a| a["allocation_source"] == "user")
1501 .count();
1502 assert_eq!(user_count, 2);
1503 }
1504
1505 #[test]
1506 fn test_calculate_percentile_empty() {
1507 let values: Vec<u64> = vec![];
1508 let result = VariableRegistry::calculate_percentile(&values, 50.0);
1509 assert_eq!(result, 0);
1510 }
1511
1512 #[test]
1513 fn test_calculate_percentile_single() {
1514 let values = vec![100];
1515 let result = VariableRegistry::calculate_percentile(&values, 50.0);
1516 assert_eq!(result, 100);
1517 }
1518
1519 #[test]
1520 fn test_calculate_percentile_multiple() {
1521 let values = vec![10, 20, 30, 40, 50];
1522 let p50 = VariableRegistry::calculate_percentile(&values, 50.0);
1523 let p90 = VariableRegistry::calculate_percentile(&values, 90.0);
1524
1525 assert!((20..=40).contains(&p50));
1526 assert!((40..=50).contains(&p90));
1527 }
1528
1529 #[test]
1530 fn test_get_stats_basic() {
1531 let _ = VariableRegistry::clear_registry();
1532 let (total, recent) = VariableRegistry::get_stats();
1533 let _ = (total, recent);
1534 }
1535
1536 #[test]
1537 fn test_get_stats_with_variables() {
1538 let _ = VariableRegistry::clear_registry();
1539
1540 let _ = VariableRegistry::register_variable(
1541 0x99901,
1542 "stat_var".to_string(),
1543 "i32".to_string(),
1544 4,
1545 );
1546
1547 let (total, _recent) = VariableRegistry::get_stats();
1548 assert!(total <= 1);
1549 }
1550
1551 #[test]
1552 fn test_analyze_lifecycle_statistics() {
1553 let user_active = vec![serde_json::json!({
1554 "lifetime_ms": 50,
1555 "is_active": true,
1556 "timestamp_dealloc": null
1557 })];
1558
1559 let user_history = vec![serde_json::json!({
1560 "lifetime_ms": 100,
1561 "is_active": false,
1562 "timestamp_dealloc": 2000
1563 })];
1564
1565 let system_active = vec![serde_json::json!({
1566 "lifetime_ms": 25,
1567 "is_active": true,
1568 "timestamp_dealloc": null
1569 })];
1570
1571 let system_history = vec![];
1572
1573 let result = VariableRegistry::analyze_lifecycle_statistics(
1574 &user_active,
1575 &user_history,
1576 &system_active,
1577 &system_history,
1578 );
1579
1580 assert_eq!(result["user_allocations"]["total_count"], 2);
1581 assert_eq!(result["system_allocations"]["total_count"], 1);
1582 assert_eq!(result["user_allocations"]["active_count"], 1);
1583 }
1584
1585 #[test]
1586 fn test_analyze_deallocation_patterns() {
1587 let user_active = vec![serde_json::json!({
1588 "timestamp_dealloc": null
1589 })];
1590
1591 let user_history = vec![serde_json::json!({
1592 "timestamp_dealloc": 2000
1593 })];
1594
1595 let system_active = vec![];
1596 let system_history = vec![];
1597
1598 let result = VariableRegistry::analyze_deallocation_patterns(
1599 &user_active,
1600 &user_history,
1601 &system_active,
1602 &system_history,
1603 );
1604
1605 assert_eq!(result["user_deallocations"]["total_deallocated"], 1);
1606 assert_eq!(result["user_deallocations"]["still_active"], 1);
1607 }
1608
1609 #[test]
1610 fn test_clear_registry() {
1611 let _ = VariableRegistry::clear_registry();
1612 let result = VariableRegistry::register_variable(
1613 0xABC01,
1614 "clear_test".to_string(),
1615 "i32".to_string(),
1616 4,
1617 );
1618 assert!(result.is_ok());
1619
1620 let clear_result = VariableRegistry::clear_registry();
1621 assert!(clear_result.is_ok());
1622 }
1623
1624 #[test]
1625 fn test_mark_variable_destroyed() {
1626 let result = VariableRegistry::mark_variable_destroyed(0x12345, 999999);
1627 assert!(result.is_ok());
1628 }
1629
1630 #[test]
1631 fn test_extract_function_name_from_backtrace() {
1632 let line = "test::module::function_name::h12345678";
1633 let result = VariableRegistry::extract_function_name_from_backtrace(line);
1634 let _ = result;
1635 }
1636
1637 #[test]
1638 fn test_extract_function_name_system_filter() {
1639 let line = "std::alloc::alloc::h12345678";
1640 let result = VariableRegistry::extract_function_name_from_backtrace(line);
1641 assert!(result.is_none() || !result.unwrap().contains("std"));
1642 }
1643
1644 #[test]
1645 fn test_extract_scope_from_var_name_main() {
1646 let scope = VariableRegistry::extract_scope_from_var_name("main_buffer");
1647 assert!(!scope.is_empty());
1648 }
1649
1650 #[test]
1651 fn test_extract_scope_from_var_name_test() {
1652 let scope = VariableRegistry::extract_scope_from_var_name("test_data");
1653 assert!(!scope.is_empty());
1654 }
1655
1656 #[test]
1657 fn test_extract_scope_from_var_name_fn() {
1658 let scope = VariableRegistry::extract_scope_from_var_name("fn_result");
1659 assert!(!scope.is_empty());
1660 }
1661
1662 #[test]
1663 fn test_extract_scope_from_var_name_vec() {
1664 let scope = VariableRegistry::extract_scope_from_var_name("my_vec_data");
1665 assert!(!scope.is_empty());
1666 }
1667
1668 #[test]
1669 fn test_extract_scope_from_var_name_string() {
1670 let scope = VariableRegistry::extract_scope_from_var_name("my_string_buffer");
1671 assert!(!scope.is_empty());
1672 }
1673
1674 #[test]
1675 fn test_extract_scope_from_var_name_boxed() {
1676 let scope = VariableRegistry::extract_scope_from_var_name("boxed_value");
1677 assert!(!scope.is_empty());
1678 }
1679
1680 #[test]
1681 fn test_extract_scope_from_var_name_rc() {
1682 let scope = VariableRegistry::extract_scope_from_var_name("rc_pointer");
1683 assert!(!scope.is_empty());
1684 }
1685
1686 #[test]
1687 fn test_extract_scope_from_var_name_arc() {
1688 let scope = VariableRegistry::extract_scope_from_var_name("arc_shared");
1689 assert!(!scope.is_empty());
1690 }
1691
1692 #[test]
1693 fn test_extract_scope_from_var_name_module() {
1694 let scope = VariableRegistry::extract_scope_from_var_name("mymodule::myvar");
1695 assert!(!scope.is_empty());
1696 }
1697
1698 #[test]
1699 fn test_extract_scope_from_var_name_default() {
1700 let scope = VariableRegistry::extract_scope_from_var_name("simple_var");
1701 assert!(!scope.is_empty());
1702 }
1703
1704 #[test]
1705 fn test_get_current_scope_name() {
1706 let scope = VariableRegistry::get_current_scope_name();
1707 let _ = scope;
1708 }
1709
1710 #[test]
1711 fn test_get_scope_from_tracker() {
1712 let scope = VariableRegistry::get_scope_from_tracker();
1713 let _ = scope;
1714 }
1715}