Skip to main content

torsh_core/
memory_debug.rs

1//! Memory debugging and allocation tracking tools for ToRSh
2//!
3//! This module provides comprehensive memory debugging capabilities including
4//! allocation tracking, leak detection, memory usage profiling, and allocation
5//! pattern analysis.
6
7use crate::error::{Result, TorshError};
8use std::alloc::{GlobalAlloc, Layout, System};
9use std::backtrace::Backtrace;
10use std::collections::{HashMap, VecDeque};
11use std::fmt;
12use std::sync::{Arc, Mutex};
13use std::time::{Duration, Instant};
14
15/// Global memory debugger instance
16static MEMORY_DEBUGGER: std::sync::OnceLock<Arc<Mutex<MemoryDebugger>>> =
17    std::sync::OnceLock::new();
18
19/// Memory allocation information for debugging
20#[derive(Debug)]
21pub struct AllocationInfo {
22    /// Unique allocation ID
23    pub id: u64,
24    /// Size of the allocation in bytes
25    pub size: usize,
26    /// Memory layout information
27    pub layout: Layout,
28    /// Timestamp when allocation was made
29    pub timestamp: Instant,
30    /// Stack trace at allocation site (if enabled)
31    pub backtrace: Option<String>,
32    /// Custom tag for categorizing allocations
33    pub tag: Option<String>,
34    /// Whether this allocation is still active
35    pub is_active: bool,
36    /// Thread ID that made the allocation
37    pub thread_id: std::thread::ThreadId,
38}
39
40impl Clone for AllocationInfo {
41    fn clone(&self) -> Self {
42        Self {
43            id: self.id,
44            size: self.size,
45            layout: self.layout,
46            timestamp: self.timestamp,
47            backtrace: self.backtrace.clone(),
48            tag: self.tag.clone(),
49            is_active: self.is_active,
50            thread_id: self.thread_id,
51        }
52    }
53}
54
55/// Memory usage statistics
56#[derive(Debug, Clone, Default)]
57pub struct MemoryStats {
58    /// Total bytes currently allocated
59    pub total_allocated: usize,
60    /// Peak memory usage in bytes
61    pub peak_allocated: usize,
62    /// Total number of allocations made
63    pub total_allocations: u64,
64    /// Total number of deallocations made
65    pub total_deallocations: u64,
66    /// Number of currently active allocations
67    pub active_allocations: u64,
68    /// Average allocation size
69    pub average_allocation_size: f64,
70    /// Total bytes allocated over lifetime
71    pub lifetime_allocated: usize,
72    /// Total bytes deallocated over lifetime
73    pub lifetime_deallocated: usize,
74}
75
76/// Memory leak detection result
77#[derive(Debug, Clone)]
78pub struct MemoryLeak {
79    /// Allocation information for the leaked memory
80    pub allocation: AllocationInfo,
81    /// How long the allocation has been active
82    pub age: Duration,
83    /// Likelihood this is a leak (0.0-1.0)
84    pub leak_probability: f64,
85    /// Confidence level in leak detection (0.0-1.0)
86    pub confidence: f64,
87    /// Risk level based on size and age
88    pub risk_level: LeakRiskLevel,
89    /// Suggested actions to address the leak
90    pub suggested_actions: Vec<String>,
91}
92
93/// Risk levels for memory leaks
94#[derive(Debug, Clone, PartialEq)]
95pub enum LeakRiskLevel {
96    /// Low risk - small allocation, short-lived
97    Low,
98    /// Medium risk - moderate size or age
99    Medium,
100    /// High risk - large allocation or very old
101    High,
102    /// Critical risk - very large allocation and very old
103    Critical,
104}
105
106/// Memory allocation pattern analysis
107#[derive(Debug, Clone)]
108pub struct AllocationPattern {
109    /// Size class of allocations (e.g., "small", "medium", "large")
110    pub size_class: String,
111    /// Frequency of allocations in this size class
112    pub frequency: u64,
113    /// Average lifetime of allocations in this class
114    pub average_lifetime: Duration,
115    /// Common stack traces for this pattern
116    pub common_stacks: Vec<String>,
117}
118
119/// Memory debugging configuration
120#[derive(Debug, Clone)]
121pub struct MemoryDebugConfig {
122    /// Whether to capture stack traces for allocations
123    pub capture_backtraces: bool,
124    /// Maximum number of allocations to track
125    pub max_tracked_allocations: usize,
126    /// Whether to track allocation patterns
127    pub track_patterns: bool,
128    /// Minimum allocation size to track
129    pub min_tracked_size: usize,
130    /// Whether to enable leak detection
131    pub enable_leak_detection: bool,
132    /// How often to run leak detection (in allocations)
133    pub leak_detection_frequency: u64,
134    /// Threshold for considering an allocation a potential leak
135    pub leak_threshold: Duration,
136    /// Enable real-time monitoring
137    pub enable_real_time_monitoring: bool,
138    /// Real-time monitoring interval
139    pub monitoring_interval: Duration,
140    /// Enable automatic leak mitigation
141    pub enable_auto_mitigation: bool,
142    /// Maximum memory usage before triggering warnings
143    pub memory_warning_threshold: usize,
144    /// Maximum memory usage before triggering critical alerts
145    pub memory_critical_threshold: usize,
146}
147
148/// Real-time memory monitoring data
149#[derive(Debug, Clone)]
150pub struct RealTimeStats {
151    /// Current memory usage
152    pub current_usage: usize,
153    /// Memory usage trend (bytes per second)
154    pub usage_trend: f64,
155    /// Number of allocations in last interval
156    pub recent_allocations: u64,
157    /// Number of deallocations in last interval
158    pub recent_deallocations: u64,
159    /// Current leak detection rate
160    pub leak_detection_rate: f64,
161    /// System memory pressure level
162    pub system_pressure: SystemPressureLevel,
163}
164
165/// System memory pressure levels
166#[derive(Debug, Clone, PartialEq)]
167pub enum SystemPressureLevel {
168    /// No pressure - memory usage is normal
169    Normal,
170    /// Low pressure - memory usage is elevated
171    Low,
172    /// Medium pressure - memory usage is high
173    Medium,
174    /// High pressure - memory usage is critical
175    High,
176    /// Critical pressure - system may be unstable
177    Critical,
178}
179
180/// Leak statistics summary
181#[derive(Debug, Clone)]
182pub struct LeakStats {
183    pub total_leaks: usize,
184    pub critical_leaks: usize,
185    pub high_leaks: usize,
186    pub medium_leaks: usize,
187    pub low_leaks: usize,
188    pub total_leaked_bytes: usize,
189    pub average_leak_age: u64,
190}
191
192impl Default for MemoryDebugConfig {
193    fn default() -> Self {
194        Self {
195            capture_backtraces: true,
196            max_tracked_allocations: 10000,
197            track_patterns: true,
198            min_tracked_size: 1024, // Track allocations >= 1KB
199            enable_leak_detection: true,
200            leak_detection_frequency: 1000,
201            leak_threshold: Duration::from_secs(300), // 5 minutes
202            enable_real_time_monitoring: true,
203            monitoring_interval: Duration::from_secs(10),
204            enable_auto_mitigation: false,
205            memory_warning_threshold: 1024 * 1024 * 1024, // 1GB
206            memory_critical_threshold: 2 * 1024 * 1024 * 1024, // 2GB
207        }
208    }
209}
210
211/// Main memory debugger implementation
212#[derive(Debug)]
213pub struct MemoryDebugger {
214    /// Configuration for memory debugging
215    config: MemoryDebugConfig,
216    /// Map of allocation ID to allocation info
217    allocations: HashMap<u64, AllocationInfo>,
218    /// Memory usage statistics
219    stats: MemoryStats,
220    /// Next allocation ID to assign
221    next_id: u64,
222    /// Recent allocation history for pattern analysis
223    allocation_history: VecDeque<AllocationInfo>,
224    /// Detected memory leaks
225    detected_leaks: Vec<MemoryLeak>,
226    /// Last time leak detection was run
227    last_leak_check: Instant,
228    /// Real-time monitoring data
229    realtime_stats: RealTimeStats,
230    /// Last monitoring timestamp
231    last_monitoring: Instant,
232    /// Previous memory usage for trend calculation
233    previous_usage: usize,
234    /// Allocation count since last monitoring
235    allocations_since_monitoring: u64,
236    /// Deallocation count since last monitoring
237    deallocations_since_monitoring: u64,
238}
239
240impl MemoryDebugger {
241    /// Create a new memory debugger with default configuration
242    pub fn new() -> Self {
243        Self::with_config(MemoryDebugConfig::default())
244    }
245
246    /// Create a new memory debugger with specified configuration
247    pub fn with_config(config: MemoryDebugConfig) -> Self {
248        let now = Instant::now();
249        Self {
250            config,
251            allocations: HashMap::new(),
252            stats: MemoryStats::default(),
253            next_id: 1,
254            allocation_history: VecDeque::new(),
255            detected_leaks: Vec::new(),
256            last_leak_check: now,
257            realtime_stats: RealTimeStats {
258                current_usage: 0,
259                usage_trend: 0.0,
260                recent_allocations: 0,
261                recent_deallocations: 0,
262                leak_detection_rate: 0.0,
263                system_pressure: SystemPressureLevel::Normal,
264            },
265            last_monitoring: now,
266            previous_usage: 0,
267            allocations_since_monitoring: 0,
268            deallocations_since_monitoring: 0,
269        }
270    }
271
272    /// Record a new memory allocation
273    pub fn record_allocation(&mut self, size: usize, layout: Layout, tag: Option<String>) -> u64 {
274        if size < self.config.min_tracked_size {
275            return 0; // Don't track small allocations
276        }
277
278        let id = self.next_id;
279        self.next_id += 1;
280
281        let allocation = AllocationInfo {
282            id,
283            size,
284            layout,
285            timestamp: Instant::now(),
286            backtrace: if self.config.capture_backtraces {
287                Some(format!("{}", Backtrace::capture()))
288            } else {
289                None
290            },
291            tag,
292            is_active: true,
293            thread_id: std::thread::current().id(),
294        };
295
296        // Update statistics
297        self.stats.total_allocated += size;
298        self.stats.peak_allocated = self.stats.peak_allocated.max(self.stats.total_allocated);
299        self.stats.total_allocations += 1;
300        self.stats.active_allocations += 1;
301        self.stats.lifetime_allocated += size;
302        self.stats.average_allocation_size =
303            self.stats.lifetime_allocated as f64 / self.stats.total_allocations as f64;
304
305        // Update real-time monitoring
306        self.allocations_since_monitoring += 1;
307        self.realtime_stats.current_usage = self.stats.total_allocated;
308        self.update_realtime_monitoring();
309
310        // Store allocation info
311        self.allocations.insert(id, allocation.clone());
312
313        // Add to history for pattern analysis
314        if self.config.track_patterns {
315            self.allocation_history.push_back(allocation);
316
317            // Limit history size
318            while self.allocation_history.len() > self.config.max_tracked_allocations {
319                self.allocation_history.pop_front();
320            }
321        }
322
323        // Run leak detection periodically
324        if self.config.enable_leak_detection
325            && self
326                .stats
327                .total_allocations
328                .is_multiple_of(self.config.leak_detection_frequency)
329        {
330            self.detect_leaks();
331        }
332
333        id
334    }
335
336    /// Record a memory deallocation
337    pub fn record_deallocation(&mut self, id: u64) {
338        if let Some(mut allocation) = self.allocations.remove(&id) {
339            allocation.is_active = false;
340
341            // Update statistics
342            self.stats.total_allocated = self.stats.total_allocated.saturating_sub(allocation.size);
343            self.stats.total_deallocations += 1;
344            self.stats.active_allocations = self.stats.active_allocations.saturating_sub(1);
345            self.stats.lifetime_deallocated += allocation.size;
346
347            // Update real-time monitoring
348            self.deallocations_since_monitoring += 1;
349            self.realtime_stats.current_usage = self.stats.total_allocated;
350            self.update_realtime_monitoring();
351        }
352    }
353
354    /// Update real-time monitoring statistics
355    fn update_realtime_monitoring(&mut self) {
356        if !self.config.enable_real_time_monitoring {
357            return;
358        }
359
360        let now = Instant::now();
361        let time_elapsed = now.duration_since(self.last_monitoring);
362
363        if time_elapsed >= self.config.monitoring_interval {
364            let time_seconds = time_elapsed.as_secs_f64();
365            let usage_change = self.stats.total_allocated as i64 - self.previous_usage as i64;
366
367            // Calculate usage trend (bytes per second)
368            self.realtime_stats.usage_trend = usage_change as f64 / time_seconds;
369
370            // Update recent allocation/deallocation counts
371            self.realtime_stats.recent_allocations = self.allocations_since_monitoring;
372            self.realtime_stats.recent_deallocations = self.deallocations_since_monitoring;
373
374            // Calculate leak detection rate
375            let total_recent_ops =
376                self.allocations_since_monitoring + self.deallocations_since_monitoring;
377            self.realtime_stats.leak_detection_rate = if total_recent_ops > 0 {
378                self.detected_leaks.len() as f64 / total_recent_ops as f64
379            } else {
380                0.0
381            };
382
383            // Update system pressure level
384            self.realtime_stats.system_pressure = self.calculate_system_pressure();
385
386            // Reset counters
387            self.previous_usage = self.stats.total_allocated;
388            self.allocations_since_monitoring = 0;
389            self.deallocations_since_monitoring = 0;
390            self.last_monitoring = now;
391        }
392    }
393
394    /// Calculate system memory pressure level
395    fn calculate_system_pressure(&self) -> SystemPressureLevel {
396        let current_usage = self.stats.total_allocated;
397        let warning_threshold = self.config.memory_warning_threshold;
398        let critical_threshold = self.config.memory_critical_threshold;
399
400        if current_usage >= critical_threshold {
401            SystemPressureLevel::Critical
402        } else if current_usage >= warning_threshold {
403            SystemPressureLevel::High
404        } else if current_usage >= warning_threshold * 3 / 4 {
405            SystemPressureLevel::Medium
406        } else if current_usage >= warning_threshold / 2 {
407            SystemPressureLevel::Low
408        } else {
409            SystemPressureLevel::Normal
410        }
411    }
412
413    /// Get current memory usage statistics
414    pub fn stats(&self) -> MemoryStats {
415        self.stats.clone()
416    }
417
418    /// Get real-time monitoring statistics
419    pub fn realtime_stats(&self) -> RealTimeStats {
420        self.realtime_stats.clone()
421    }
422
423    /// Check if system is under memory pressure
424    pub fn is_under_pressure(&self) -> bool {
425        matches!(
426            self.realtime_stats.system_pressure,
427            SystemPressureLevel::High | SystemPressureLevel::Critical
428        )
429    }
430
431    /// Get memory pressure level
432    pub fn get_pressure_level(&self) -> SystemPressureLevel {
433        self.realtime_stats.system_pressure.clone()
434    }
435
436    /// Force a leak detection run
437    pub fn force_leak_detection(&mut self) -> Vec<MemoryLeak> {
438        self.detect_leaks()
439    }
440
441    /// Get leak statistics
442    pub fn leak_stats(&self) -> LeakStats {
443        let total_leaks = self.detected_leaks.len();
444        let critical_leaks = self
445            .detected_leaks
446            .iter()
447            .filter(|l| l.risk_level == LeakRiskLevel::Critical)
448            .count();
449        let high_leaks = self
450            .detected_leaks
451            .iter()
452            .filter(|l| l.risk_level == LeakRiskLevel::High)
453            .count();
454        let medium_leaks = self
455            .detected_leaks
456            .iter()
457            .filter(|l| l.risk_level == LeakRiskLevel::Medium)
458            .count();
459        let low_leaks = self
460            .detected_leaks
461            .iter()
462            .filter(|l| l.risk_level == LeakRiskLevel::Low)
463            .count();
464
465        LeakStats {
466            total_leaks,
467            critical_leaks,
468            high_leaks,
469            medium_leaks,
470            low_leaks,
471            total_leaked_bytes: self.detected_leaks.iter().map(|l| l.allocation.size).sum(),
472            average_leak_age: if total_leaks > 0 {
473                self.detected_leaks
474                    .iter()
475                    .map(|l| l.age.as_secs())
476                    .sum::<u64>()
477                    / total_leaks as u64
478            } else {
479                0
480            },
481        }
482    }
483
484    /// Run leak detection and return any newly detected leaks
485    pub fn detect_leaks(&mut self) -> Vec<MemoryLeak> {
486        let now = Instant::now();
487        let threshold = self.config.leak_threshold;
488        let mut new_leaks = Vec::new();
489
490        for allocation in self.allocations.values() {
491            if !allocation.is_active {
492                continue;
493            }
494
495            let age = now.duration_since(allocation.timestamp);
496            if age > threshold {
497                // Calculate leak probability based on age and size
498                let age_factor = age.as_secs_f64() / threshold.as_secs_f64();
499                let size_factor = (allocation.size as f64).log2() / 20.0; // Larger allocations more suspicious
500                let leak_probability = (age_factor * 0.7 + size_factor * 0.3).min(1.0);
501
502                if leak_probability > 0.5 {
503                    // Calculate confidence based on age and size factors
504                    let confidence = (age_factor * 0.8 + size_factor * 0.2).min(1.0);
505
506                    // Determine risk level
507                    let risk_level = if allocation.size > 1024 * 1024 && age.as_secs() > 3600 {
508                        LeakRiskLevel::Critical
509                    } else if allocation.size > 64 * 1024 || age.as_secs() > 1800 {
510                        LeakRiskLevel::High
511                    } else if allocation.size > 4 * 1024 || age.as_secs() > 600 {
512                        LeakRiskLevel::Medium
513                    } else {
514                        LeakRiskLevel::Low
515                    };
516
517                    // Generate suggested actions
518                    let mut suggested_actions = Vec::new();
519                    if age.as_secs() > 1800 {
520                        suggested_actions
521                            .push("Consider reviewing allocation lifetime".to_string());
522                    }
523                    if allocation.size > 64 * 1024 {
524                        suggested_actions.push("Review large allocation usage".to_string());
525                    }
526                    if allocation.backtrace.is_some() {
527                        suggested_actions.push("Check allocation backtrace for source".to_string());
528                    }
529                    if suggested_actions.is_empty() {
530                        suggested_actions
531                            .push("Monitor allocation for continued growth".to_string());
532                    }
533
534                    new_leaks.push(MemoryLeak {
535                        allocation: allocation.clone(),
536                        age,
537                        leak_probability,
538                        confidence,
539                        risk_level,
540                        suggested_actions,
541                    });
542                }
543            }
544        }
545
546        self.detected_leaks.extend(new_leaks.clone());
547        self.last_leak_check = now;
548        new_leaks
549    }
550
551    /// Analyze allocation patterns and return insights
552    pub fn analyze_patterns(&self) -> Vec<AllocationPattern> {
553        if !self.config.track_patterns {
554            return Vec::new();
555        }
556
557        let mut size_patterns: HashMap<String, (u64, Duration, Vec<String>)> = HashMap::new();
558
559        for allocation in &self.allocation_history {
560            let size_class = Self::classify_size(allocation.size);
561            let lifetime = if allocation.is_active {
562                Instant::now().duration_since(allocation.timestamp)
563            } else {
564                Duration::from_secs(0) // Completed allocation
565            };
566
567            let stack_trace = allocation
568                .backtrace
569                .as_ref()
570                .cloned()
571                .unwrap_or_else(|| "No backtrace".to_string());
572
573            let entry = size_patterns
574                .entry(size_class)
575                .or_insert((0, Duration::ZERO, Vec::new()));
576            entry.0 += 1; // frequency
577            entry.1 += lifetime; // total lifetime
578            if !entry.2.contains(&stack_trace) && entry.2.len() < 5 {
579                entry.2.push(stack_trace); // common stacks (limit to 5)
580            }
581        }
582
583        size_patterns
584            .into_iter()
585            .map(
586                |(size_class, (frequency, total_lifetime, stacks))| AllocationPattern {
587                    size_class,
588                    frequency,
589                    average_lifetime: total_lifetime / frequency.max(1) as u32,
590                    common_stacks: stacks,
591                },
592            )
593            .collect()
594    }
595
596    /// Classify allocation size into categories
597    fn classify_size(size: usize) -> String {
598        match size {
599            0..=1024 => "small".to_string(),
600            1025..=65536 => "medium".to_string(),
601            65537..=1048576 => "large".to_string(),
602            _ => "huge".to_string(),
603        }
604    }
605
606    /// Get all detected memory leaks
607    pub fn get_leaks(&self) -> &[MemoryLeak] {
608        &self.detected_leaks
609    }
610
611    /// Clear all debugging data
612    pub fn clear(&mut self) {
613        self.allocations.clear();
614        self.allocation_history.clear();
615        self.detected_leaks.clear();
616        self.stats = MemoryStats::default();
617        self.next_id = 1;
618    }
619
620    /// Generate a comprehensive memory report
621    pub fn generate_report(&self) -> MemoryReport {
622        MemoryReport {
623            stats: self.stats.clone(),
624            leaks: self.detected_leaks.clone(),
625            patterns: self.analyze_patterns(),
626            config: self.config.clone(),
627            timestamp: Instant::now(),
628        }
629    }
630}
631
632impl Default for MemoryDebugger {
633    fn default() -> Self {
634        Self::new()
635    }
636}
637
638/// Comprehensive memory debugging report
639#[derive(Debug, Clone)]
640pub struct MemoryReport {
641    /// Current memory statistics
642    pub stats: MemoryStats,
643    /// Detected memory leaks
644    pub leaks: Vec<MemoryLeak>,
645    /// Allocation patterns
646    pub patterns: Vec<AllocationPattern>,
647    /// Debugger configuration
648    pub config: MemoryDebugConfig,
649    /// When this report was generated
650    pub timestamp: Instant,
651}
652
653impl fmt::Display for MemoryReport {
654    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
655        writeln!(f, "=== Memory Debug Report ===")?;
656        writeln!(f, "Generated at: {:?}", self.timestamp)?;
657        writeln!(f)?;
658
659        writeln!(f, "Memory Statistics:")?;
660        writeln!(f, "  Total allocated: {} bytes", self.stats.total_allocated)?;
661        writeln!(f, "  Peak allocated: {} bytes", self.stats.peak_allocated)?;
662        writeln!(f, "  Active allocations: {}", self.stats.active_allocations)?;
663        writeln!(f, "  Total allocations: {}", self.stats.total_allocations)?;
664        writeln!(
665            f,
666            "  Total deallocations: {}",
667            self.stats.total_deallocations
668        )?;
669        writeln!(
670            f,
671            "  Average allocation size: {:.2} bytes",
672            self.stats.average_allocation_size
673        )?;
674        writeln!(f)?;
675
676        if !self.leaks.is_empty() {
677            writeln!(f, "Memory Leaks Detected ({}):", self.leaks.len())?;
678            for (i, leak) in self.leaks.iter().enumerate() {
679                writeln!(
680                    f,
681                    "  {}. ID: {}, Size: {} bytes, Age: {:?}, Probability: {:.2}",
682                    i + 1,
683                    leak.allocation.id,
684                    leak.allocation.size,
685                    leak.age,
686                    leak.leak_probability
687                )?;
688            }
689            writeln!(f)?;
690        }
691
692        if !self.patterns.is_empty() {
693            writeln!(f, "Allocation Patterns:")?;
694            for pattern in &self.patterns {
695                writeln!(
696                    f,
697                    "  {}: {} allocations, avg lifetime: {:?}",
698                    pattern.size_class, pattern.frequency, pattern.average_lifetime
699                )?;
700            }
701        }
702
703        Ok(())
704    }
705}
706
707/// Global functions for easy memory debugging access
708pub fn init_memory_debugger() -> Result<()> {
709    let debugger = Arc::new(Mutex::new(MemoryDebugger::new()));
710    MEMORY_DEBUGGER
711        .set(debugger)
712        .map_err(|_| TorshError::ConfigError("Memory debugger already initialized".to_string()))?;
713    Ok(())
714}
715
716pub fn init_memory_debugger_with_config(config: MemoryDebugConfig) -> Result<()> {
717    let debugger = Arc::new(Mutex::new(MemoryDebugger::with_config(config)));
718    MEMORY_DEBUGGER
719        .set(debugger)
720        .map_err(|_| TorshError::ConfigError("Memory debugger already initialized".to_string()))?;
721    Ok(())
722}
723
724pub fn record_allocation(size: usize, layout: Layout, tag: Option<String>) -> u64 {
725    if let Some(debugger) = MEMORY_DEBUGGER.get() {
726        if let Ok(mut debugger) = debugger.lock() {
727            return debugger.record_allocation(size, layout, tag);
728        }
729    }
730    0
731}
732
733pub fn record_deallocation(id: u64) {
734    if let Some(debugger) = MEMORY_DEBUGGER.get() {
735        if let Ok(mut debugger) = debugger.lock() {
736            debugger.record_deallocation(id);
737        }
738    }
739}
740
741pub fn get_memory_stats() -> Option<MemoryStats> {
742    MEMORY_DEBUGGER.get()?.lock().ok().map(|d| d.stats())
743}
744
745pub fn detect_memory_leaks() -> Option<Vec<MemoryLeak>> {
746    MEMORY_DEBUGGER
747        .get()?
748        .lock()
749        .ok()
750        .map(|mut d| d.detect_leaks())
751}
752
753pub fn generate_memory_report() -> Option<MemoryReport> {
754    MEMORY_DEBUGGER
755        .get()?
756        .lock()
757        .ok()
758        .map(|d| d.generate_report())
759}
760
761pub fn get_realtime_stats() -> Option<RealTimeStats> {
762    MEMORY_DEBUGGER
763        .get()?
764        .lock()
765        .ok()
766        .map(|d| d.realtime_stats())
767}
768
769pub fn is_under_memory_pressure() -> bool {
770    MEMORY_DEBUGGER
771        .get()
772        .and_then(|d| d.lock().ok())
773        .map(|d| d.is_under_pressure())
774        .unwrap_or(false)
775}
776
777pub fn get_pressure_level() -> SystemPressureLevel {
778    MEMORY_DEBUGGER
779        .get()
780        .and_then(|d| d.lock().ok())
781        .map(|d| d.get_pressure_level())
782        .unwrap_or(SystemPressureLevel::Normal)
783}
784
785pub fn force_leak_detection() -> Option<Vec<MemoryLeak>> {
786    MEMORY_DEBUGGER
787        .get()?
788        .lock()
789        .ok()
790        .map(|mut d| d.force_leak_detection())
791}
792
793pub fn get_leak_stats() -> Option<LeakStats> {
794    MEMORY_DEBUGGER.get()?.lock().ok().map(|d| d.leak_stats())
795}
796
797/// Custom allocator wrapper that integrates with memory debugging
798pub struct DebuggingAllocator<A: GlobalAlloc> {
799    inner: A,
800}
801
802impl<A: GlobalAlloc> DebuggingAllocator<A> {
803    pub const fn new(inner: A) -> Self {
804        Self { inner }
805    }
806}
807
808unsafe impl<A: GlobalAlloc> GlobalAlloc for DebuggingAllocator<A> {
809    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
810        let ptr = self.inner.alloc(layout);
811        if !ptr.is_null() {
812            record_allocation(layout.size(), layout, None);
813        }
814        ptr
815    }
816
817    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
818        // Note: We can't track which specific allocation is being freed without
819        // maintaining a ptr -> id mapping, which would be expensive
820        record_deallocation(0); // Use 0 to indicate unknown allocation ID
821        self.inner.dealloc(ptr, layout);
822    }
823}
824
825/// Type alias for system allocator with debugging
826pub type SystemDebuggingAllocator = DebuggingAllocator<System>;
827
828#[cfg(test)]
829mod tests {
830    use super::*;
831
832    #[test]
833    fn test_memory_debugger_basic() {
834        let mut debugger = MemoryDebugger::new();
835
836        let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
837        let id = debugger.record_allocation(1024, layout, Some("test".to_string()));
838
839        assert_eq!(debugger.stats().total_allocated, 1024);
840        assert_eq!(debugger.stats().active_allocations, 1);
841
842        debugger.record_deallocation(id);
843
844        assert_eq!(debugger.stats().total_allocated, 0);
845        assert_eq!(debugger.stats().active_allocations, 0);
846    }
847
848    #[test]
849    fn test_leak_detection() {
850        let config = MemoryDebugConfig {
851            leak_threshold: Duration::from_millis(1),
852            ..Default::default()
853        };
854
855        let mut debugger = MemoryDebugger::with_config(config);
856        let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
857
858        let _id = debugger.record_allocation(1024, layout, Some("potential_leak".to_string()));
859
860        // Wait for leak threshold
861        std::thread::sleep(Duration::from_millis(2));
862
863        let leaks = debugger.detect_leaks();
864        assert!(!leaks.is_empty());
865    }
866
867    #[test]
868    fn test_pattern_analysis() {
869        let mut debugger = MemoryDebugger::new();
870        let layout_small = Layout::from_size_align(512, 8).expect("layout should be valid");
871        let layout_large = Layout::from_size_align(2048, 8).expect("layout should be valid");
872
873        // Create small allocations
874        for _ in 0..5 {
875            debugger.record_allocation(512, layout_small, Some("small".to_string()));
876        }
877
878        // Create large allocations
879        for _ in 0..3 {
880            debugger.record_allocation(2048, layout_large, Some("large".to_string()));
881        }
882
883        let patterns = debugger.analyze_patterns();
884        assert!(!patterns.is_empty());
885
886        // Should have patterns for both small and large allocations
887        let has_small = patterns.iter().any(|p| p.size_class == "small");
888        let has_medium = patterns.iter().any(|p| p.size_class == "medium");
889
890        assert!(has_small || has_medium); // 512 bytes might be classified as small or medium
891    }
892
893    #[test]
894    fn test_enhanced_leak_detection() {
895        let config = MemoryDebugConfig {
896            leak_threshold: Duration::from_millis(1),
897            ..Default::default()
898        };
899
900        let mut debugger = MemoryDebugger::with_config(config);
901        let layout = Layout::from_size_align(1024 * 1024, 8).expect("layout should be valid"); // 1MB allocation
902
903        let _id =
904            debugger.record_allocation(1024 * 1024, layout, Some("large_allocation".to_string()));
905
906        // Wait for leak threshold
907        std::thread::sleep(Duration::from_millis(10));
908
909        let leaks = debugger.detect_leaks();
910        assert!(!leaks.is_empty());
911
912        let leak = &leaks[0];
913        assert!(leak.confidence > 0.0);
914        assert!(!leak.suggested_actions.is_empty());
915
916        // Large allocation should trigger high risk
917        assert!(matches!(
918            leak.risk_level,
919            LeakRiskLevel::High | LeakRiskLevel::Critical
920        ));
921    }
922
923    #[test]
924    fn test_realtime_monitoring() {
925        let config = MemoryDebugConfig {
926            monitoring_interval: Duration::from_millis(1),
927            enable_real_time_monitoring: true,
928            ..Default::default()
929        };
930
931        let mut debugger = MemoryDebugger::with_config(config);
932        let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
933
934        // Make some allocations
935        for _ in 0..5 {
936            debugger.record_allocation(1024, layout, None);
937        }
938
939        // Wait for monitoring interval
940        std::thread::sleep(Duration::from_millis(10));
941
942        // Force monitoring update
943        debugger.record_allocation(1024, layout, None);
944
945        let stats = debugger.realtime_stats();
946        assert!(stats.current_usage > 0);
947        assert!(stats.recent_allocations > 0);
948    }
949
950    #[test]
951    fn test_pressure_level_calculation() {
952        let config = MemoryDebugConfig {
953            memory_warning_threshold: 1000,
954            memory_critical_threshold: 2000,
955            min_tracked_size: 1, // Track all allocations for this test
956            monitoring_interval: Duration::from_millis(1), // Short interval for testing
957            ..Default::default()
958        };
959
960        let mut debugger = MemoryDebugger::with_config(config);
961        let layout = Layout::from_size_align(1000, 8).expect("layout should be valid");
962
963        // Test normal pressure
964        assert_eq!(
965            debugger.calculate_system_pressure(),
966            SystemPressureLevel::Normal
967        );
968
969        // Test low pressure (half of warning threshold = 500)
970        let _id1 = debugger.record_allocation(600, layout, None);
971        std::thread::sleep(Duration::from_millis(2)); // Wait for monitoring interval
972        assert_eq!(
973            debugger.calculate_system_pressure(),
974            SystemPressureLevel::Low
975        );
976
977        // Test medium pressure (3/4 of warning threshold = 750, total = 800)
978        let _id2 = debugger.record_allocation(200, layout, None);
979        assert_eq!(
980            debugger.calculate_system_pressure(),
981            SystemPressureLevel::Medium
982        );
983
984        // Test high pressure (warning threshold = 1000, total = 1000)
985        let _id3 = debugger.record_allocation(200, layout, None);
986        assert_eq!(
987            debugger.calculate_system_pressure(),
988            SystemPressureLevel::High
989        );
990
991        // Test critical pressure (critical threshold = 2000, total = 2000)
992        let _id4 = debugger.record_allocation(1000, layout, None);
993        assert_eq!(
994            debugger.calculate_system_pressure(),
995            SystemPressureLevel::Critical
996        );
997    }
998
999    #[test]
1000    fn test_leak_stats() {
1001        let config = MemoryDebugConfig {
1002            leak_threshold: Duration::from_millis(1),
1003            ..Default::default()
1004        };
1005
1006        let mut debugger = MemoryDebugger::with_config(config);
1007        let layout_small = Layout::from_size_align(1024, 8).expect("layout should be valid");
1008        let layout_large = Layout::from_size_align(1024 * 1024, 8).expect("layout should be valid");
1009
1010        // Create different sized allocations
1011        let _id1 = debugger.record_allocation(1024, layout_small, Some("small_leak".to_string()));
1012        let _id2 =
1013            debugger.record_allocation(1024 * 1024, layout_large, Some("large_leak".to_string()));
1014
1015        // Wait for leak threshold
1016        std::thread::sleep(Duration::from_millis(50));
1017
1018        let leaks = debugger.detect_leaks();
1019        assert!(!leaks.is_empty(), "Expected leaks to be detected");
1020
1021        let stats = debugger.leak_stats();
1022
1023        assert!(stats.total_leaks > 0);
1024        assert!(stats.total_leaked_bytes > 0);
1025        // Note: average_leak_age might be 0 due to rounding in integer division
1026        // This is acceptable for the test
1027    }
1028
1029    #[test]
1030    fn test_global_api_functions() {
1031        // Test global API initialization
1032        let config = MemoryDebugConfig {
1033            enable_real_time_monitoring: true,
1034            monitoring_interval: Duration::from_millis(1),
1035            ..Default::default()
1036        };
1037
1038        let _ = init_memory_debugger_with_config(config);
1039
1040        // Test global functions
1041        let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
1042        let id = record_allocation(1024, layout, Some("test".to_string()));
1043
1044        let stats = get_memory_stats();
1045        assert!(stats.is_some());
1046
1047        let pressure_level = get_pressure_level();
1048        assert_eq!(pressure_level, SystemPressureLevel::Normal);
1049
1050        let is_under_pressure = is_under_memory_pressure();
1051        assert!(!is_under_pressure);
1052
1053        record_deallocation(id);
1054
1055        let report = generate_memory_report();
1056        assert!(report.is_some());
1057    }
1058}