use crate::error::{Result, TorshError};
use std::alloc::{GlobalAlloc, Layout, System};
use std::backtrace::Backtrace;
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
static MEMORY_DEBUGGER: std::sync::OnceLock<Arc<Mutex<MemoryDebugger>>> =
std::sync::OnceLock::new();
#[derive(Debug)]
pub struct AllocationInfo {
pub id: u64,
pub size: usize,
pub layout: Layout,
pub timestamp: Instant,
pub backtrace: Option<String>,
pub tag: Option<String>,
pub is_active: bool,
pub thread_id: std::thread::ThreadId,
}
impl Clone for AllocationInfo {
fn clone(&self) -> Self {
Self {
id: self.id,
size: self.size,
layout: self.layout,
timestamp: self.timestamp,
backtrace: self.backtrace.clone(),
tag: self.tag.clone(),
is_active: self.is_active,
thread_id: self.thread_id,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct MemoryStats {
pub total_allocated: usize,
pub peak_allocated: usize,
pub total_allocations: u64,
pub total_deallocations: u64,
pub active_allocations: u64,
pub average_allocation_size: f64,
pub lifetime_allocated: usize,
pub lifetime_deallocated: usize,
}
#[derive(Debug, Clone)]
pub struct MemoryLeak {
pub allocation: AllocationInfo,
pub age: Duration,
pub leak_probability: f64,
pub confidence: f64,
pub risk_level: LeakRiskLevel,
pub suggested_actions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum LeakRiskLevel {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct AllocationPattern {
pub size_class: String,
pub frequency: u64,
pub average_lifetime: Duration,
pub common_stacks: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct MemoryDebugConfig {
pub capture_backtraces: bool,
pub max_tracked_allocations: usize,
pub track_patterns: bool,
pub min_tracked_size: usize,
pub enable_leak_detection: bool,
pub leak_detection_frequency: u64,
pub leak_threshold: Duration,
pub enable_real_time_monitoring: bool,
pub monitoring_interval: Duration,
pub enable_auto_mitigation: bool,
pub memory_warning_threshold: usize,
pub memory_critical_threshold: usize,
}
#[derive(Debug, Clone)]
pub struct RealTimeStats {
pub current_usage: usize,
pub usage_trend: f64,
pub recent_allocations: u64,
pub recent_deallocations: u64,
pub leak_detection_rate: f64,
pub system_pressure: SystemPressureLevel,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SystemPressureLevel {
Normal,
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone)]
pub struct LeakStats {
pub total_leaks: usize,
pub critical_leaks: usize,
pub high_leaks: usize,
pub medium_leaks: usize,
pub low_leaks: usize,
pub total_leaked_bytes: usize,
pub average_leak_age: u64,
}
impl Default for MemoryDebugConfig {
fn default() -> Self {
Self {
capture_backtraces: true,
max_tracked_allocations: 10000,
track_patterns: true,
min_tracked_size: 1024, enable_leak_detection: true,
leak_detection_frequency: 1000,
leak_threshold: Duration::from_secs(300), enable_real_time_monitoring: true,
monitoring_interval: Duration::from_secs(10),
enable_auto_mitigation: false,
memory_warning_threshold: 1024 * 1024 * 1024, memory_critical_threshold: 2 * 1024 * 1024 * 1024, }
}
}
#[derive(Debug)]
pub struct MemoryDebugger {
config: MemoryDebugConfig,
allocations: HashMap<u64, AllocationInfo>,
stats: MemoryStats,
next_id: u64,
allocation_history: VecDeque<AllocationInfo>,
detected_leaks: Vec<MemoryLeak>,
last_leak_check: Instant,
realtime_stats: RealTimeStats,
last_monitoring: Instant,
previous_usage: usize,
allocations_since_monitoring: u64,
deallocations_since_monitoring: u64,
}
impl MemoryDebugger {
pub fn new() -> Self {
Self::with_config(MemoryDebugConfig::default())
}
pub fn with_config(config: MemoryDebugConfig) -> Self {
let now = Instant::now();
Self {
config,
allocations: HashMap::new(),
stats: MemoryStats::default(),
next_id: 1,
allocation_history: VecDeque::new(),
detected_leaks: Vec::new(),
last_leak_check: now,
realtime_stats: RealTimeStats {
current_usage: 0,
usage_trend: 0.0,
recent_allocations: 0,
recent_deallocations: 0,
leak_detection_rate: 0.0,
system_pressure: SystemPressureLevel::Normal,
},
last_monitoring: now,
previous_usage: 0,
allocations_since_monitoring: 0,
deallocations_since_monitoring: 0,
}
}
pub fn record_allocation(&mut self, size: usize, layout: Layout, tag: Option<String>) -> u64 {
if size < self.config.min_tracked_size {
return 0; }
let id = self.next_id;
self.next_id += 1;
let allocation = AllocationInfo {
id,
size,
layout,
timestamp: Instant::now(),
backtrace: if self.config.capture_backtraces {
Some(format!("{}", Backtrace::capture()))
} else {
None
},
tag,
is_active: true,
thread_id: std::thread::current().id(),
};
self.stats.total_allocated += size;
self.stats.peak_allocated = self.stats.peak_allocated.max(self.stats.total_allocated);
self.stats.total_allocations += 1;
self.stats.active_allocations += 1;
self.stats.lifetime_allocated += size;
self.stats.average_allocation_size =
self.stats.lifetime_allocated as f64 / self.stats.total_allocations as f64;
self.allocations_since_monitoring += 1;
self.realtime_stats.current_usage = self.stats.total_allocated;
self.update_realtime_monitoring();
self.allocations.insert(id, allocation.clone());
if self.config.track_patterns {
self.allocation_history.push_back(allocation);
while self.allocation_history.len() > self.config.max_tracked_allocations {
self.allocation_history.pop_front();
}
}
if self.config.enable_leak_detection
&& self
.stats
.total_allocations
.is_multiple_of(self.config.leak_detection_frequency)
{
self.detect_leaks();
}
id
}
pub fn record_deallocation(&mut self, id: u64) {
if let Some(mut allocation) = self.allocations.remove(&id) {
allocation.is_active = false;
self.stats.total_allocated = self.stats.total_allocated.saturating_sub(allocation.size);
self.stats.total_deallocations += 1;
self.stats.active_allocations = self.stats.active_allocations.saturating_sub(1);
self.stats.lifetime_deallocated += allocation.size;
self.deallocations_since_monitoring += 1;
self.realtime_stats.current_usage = self.stats.total_allocated;
self.update_realtime_monitoring();
}
}
fn update_realtime_monitoring(&mut self) {
if !self.config.enable_real_time_monitoring {
return;
}
let now = Instant::now();
let time_elapsed = now.duration_since(self.last_monitoring);
if time_elapsed >= self.config.monitoring_interval {
let time_seconds = time_elapsed.as_secs_f64();
let usage_change = self.stats.total_allocated as i64 - self.previous_usage as i64;
self.realtime_stats.usage_trend = usage_change as f64 / time_seconds;
self.realtime_stats.recent_allocations = self.allocations_since_monitoring;
self.realtime_stats.recent_deallocations = self.deallocations_since_monitoring;
let total_recent_ops =
self.allocations_since_monitoring + self.deallocations_since_monitoring;
self.realtime_stats.leak_detection_rate = if total_recent_ops > 0 {
self.detected_leaks.len() as f64 / total_recent_ops as f64
} else {
0.0
};
self.realtime_stats.system_pressure = self.calculate_system_pressure();
self.previous_usage = self.stats.total_allocated;
self.allocations_since_monitoring = 0;
self.deallocations_since_monitoring = 0;
self.last_monitoring = now;
}
}
fn calculate_system_pressure(&self) -> SystemPressureLevel {
let current_usage = self.stats.total_allocated;
let warning_threshold = self.config.memory_warning_threshold;
let critical_threshold = self.config.memory_critical_threshold;
if current_usage >= critical_threshold {
SystemPressureLevel::Critical
} else if current_usage >= warning_threshold {
SystemPressureLevel::High
} else if current_usage >= warning_threshold * 3 / 4 {
SystemPressureLevel::Medium
} else if current_usage >= warning_threshold / 2 {
SystemPressureLevel::Low
} else {
SystemPressureLevel::Normal
}
}
pub fn stats(&self) -> MemoryStats {
self.stats.clone()
}
pub fn realtime_stats(&self) -> RealTimeStats {
self.realtime_stats.clone()
}
pub fn is_under_pressure(&self) -> bool {
matches!(
self.realtime_stats.system_pressure,
SystemPressureLevel::High | SystemPressureLevel::Critical
)
}
pub fn get_pressure_level(&self) -> SystemPressureLevel {
self.realtime_stats.system_pressure.clone()
}
pub fn force_leak_detection(&mut self) -> Vec<MemoryLeak> {
self.detect_leaks()
}
pub fn leak_stats(&self) -> LeakStats {
let total_leaks = self.detected_leaks.len();
let critical_leaks = self
.detected_leaks
.iter()
.filter(|l| l.risk_level == LeakRiskLevel::Critical)
.count();
let high_leaks = self
.detected_leaks
.iter()
.filter(|l| l.risk_level == LeakRiskLevel::High)
.count();
let medium_leaks = self
.detected_leaks
.iter()
.filter(|l| l.risk_level == LeakRiskLevel::Medium)
.count();
let low_leaks = self
.detected_leaks
.iter()
.filter(|l| l.risk_level == LeakRiskLevel::Low)
.count();
LeakStats {
total_leaks,
critical_leaks,
high_leaks,
medium_leaks,
low_leaks,
total_leaked_bytes: self.detected_leaks.iter().map(|l| l.allocation.size).sum(),
average_leak_age: if total_leaks > 0 {
self.detected_leaks
.iter()
.map(|l| l.age.as_secs())
.sum::<u64>()
/ total_leaks as u64
} else {
0
},
}
}
pub fn detect_leaks(&mut self) -> Vec<MemoryLeak> {
let now = Instant::now();
let threshold = self.config.leak_threshold;
let mut new_leaks = Vec::new();
for allocation in self.allocations.values() {
if !allocation.is_active {
continue;
}
let age = now.duration_since(allocation.timestamp);
if age > threshold {
let age_factor = age.as_secs_f64() / threshold.as_secs_f64();
let size_factor = (allocation.size as f64).log2() / 20.0; let leak_probability = (age_factor * 0.7 + size_factor * 0.3).min(1.0);
if leak_probability > 0.5 {
let confidence = (age_factor * 0.8 + size_factor * 0.2).min(1.0);
let risk_level = if allocation.size > 1024 * 1024 && age.as_secs() > 3600 {
LeakRiskLevel::Critical
} else if allocation.size > 64 * 1024 || age.as_secs() > 1800 {
LeakRiskLevel::High
} else if allocation.size > 4 * 1024 || age.as_secs() > 600 {
LeakRiskLevel::Medium
} else {
LeakRiskLevel::Low
};
let mut suggested_actions = Vec::new();
if age.as_secs() > 1800 {
suggested_actions
.push("Consider reviewing allocation lifetime".to_string());
}
if allocation.size > 64 * 1024 {
suggested_actions.push("Review large allocation usage".to_string());
}
if allocation.backtrace.is_some() {
suggested_actions.push("Check allocation backtrace for source".to_string());
}
if suggested_actions.is_empty() {
suggested_actions
.push("Monitor allocation for continued growth".to_string());
}
new_leaks.push(MemoryLeak {
allocation: allocation.clone(),
age,
leak_probability,
confidence,
risk_level,
suggested_actions,
});
}
}
}
self.detected_leaks.extend(new_leaks.clone());
self.last_leak_check = now;
new_leaks
}
pub fn analyze_patterns(&self) -> Vec<AllocationPattern> {
if !self.config.track_patterns {
return Vec::new();
}
let mut size_patterns: HashMap<String, (u64, Duration, Vec<String>)> = HashMap::new();
for allocation in &self.allocation_history {
let size_class = Self::classify_size(allocation.size);
let lifetime = if allocation.is_active {
Instant::now().duration_since(allocation.timestamp)
} else {
Duration::from_secs(0) };
let stack_trace = allocation
.backtrace
.as_ref()
.cloned()
.unwrap_or_else(|| "No backtrace".to_string());
let entry = size_patterns
.entry(size_class)
.or_insert((0, Duration::ZERO, Vec::new()));
entry.0 += 1; entry.1 += lifetime; if !entry.2.contains(&stack_trace) && entry.2.len() < 5 {
entry.2.push(stack_trace); }
}
size_patterns
.into_iter()
.map(
|(size_class, (frequency, total_lifetime, stacks))| AllocationPattern {
size_class,
frequency,
average_lifetime: total_lifetime / frequency.max(1) as u32,
common_stacks: stacks,
},
)
.collect()
}
fn classify_size(size: usize) -> String {
match size {
0..=1024 => "small".to_string(),
1025..=65536 => "medium".to_string(),
65537..=1048576 => "large".to_string(),
_ => "huge".to_string(),
}
}
pub fn get_leaks(&self) -> &[MemoryLeak] {
&self.detected_leaks
}
pub fn clear(&mut self) {
self.allocations.clear();
self.allocation_history.clear();
self.detected_leaks.clear();
self.stats = MemoryStats::default();
self.next_id = 1;
}
pub fn generate_report(&self) -> MemoryReport {
MemoryReport {
stats: self.stats.clone(),
leaks: self.detected_leaks.clone(),
patterns: self.analyze_patterns(),
config: self.config.clone(),
timestamp: Instant::now(),
}
}
}
impl Default for MemoryDebugger {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct MemoryReport {
pub stats: MemoryStats,
pub leaks: Vec<MemoryLeak>,
pub patterns: Vec<AllocationPattern>,
pub config: MemoryDebugConfig,
pub timestamp: Instant,
}
impl fmt::Display for MemoryReport {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "=== Memory Debug Report ===")?;
writeln!(f, "Generated at: {:?}", self.timestamp)?;
writeln!(f)?;
writeln!(f, "Memory Statistics:")?;
writeln!(f, " Total allocated: {} bytes", self.stats.total_allocated)?;
writeln!(f, " Peak allocated: {} bytes", self.stats.peak_allocated)?;
writeln!(f, " Active allocations: {}", self.stats.active_allocations)?;
writeln!(f, " Total allocations: {}", self.stats.total_allocations)?;
writeln!(
f,
" Total deallocations: {}",
self.stats.total_deallocations
)?;
writeln!(
f,
" Average allocation size: {:.2} bytes",
self.stats.average_allocation_size
)?;
writeln!(f)?;
if !self.leaks.is_empty() {
writeln!(f, "Memory Leaks Detected ({}):", self.leaks.len())?;
for (i, leak) in self.leaks.iter().enumerate() {
writeln!(
f,
" {}. ID: {}, Size: {} bytes, Age: {:?}, Probability: {:.2}",
i + 1,
leak.allocation.id,
leak.allocation.size,
leak.age,
leak.leak_probability
)?;
}
writeln!(f)?;
}
if !self.patterns.is_empty() {
writeln!(f, "Allocation Patterns:")?;
for pattern in &self.patterns {
writeln!(
f,
" {}: {} allocations, avg lifetime: {:?}",
pattern.size_class, pattern.frequency, pattern.average_lifetime
)?;
}
}
Ok(())
}
}
pub fn init_memory_debugger() -> Result<()> {
let debugger = Arc::new(Mutex::new(MemoryDebugger::new()));
MEMORY_DEBUGGER
.set(debugger)
.map_err(|_| TorshError::ConfigError("Memory debugger already initialized".to_string()))?;
Ok(())
}
pub fn init_memory_debugger_with_config(config: MemoryDebugConfig) -> Result<()> {
let debugger = Arc::new(Mutex::new(MemoryDebugger::with_config(config)));
MEMORY_DEBUGGER
.set(debugger)
.map_err(|_| TorshError::ConfigError("Memory debugger already initialized".to_string()))?;
Ok(())
}
pub fn record_allocation(size: usize, layout: Layout, tag: Option<String>) -> u64 {
if let Some(debugger) = MEMORY_DEBUGGER.get() {
if let Ok(mut debugger) = debugger.lock() {
return debugger.record_allocation(size, layout, tag);
}
}
0
}
pub fn record_deallocation(id: u64) {
if let Some(debugger) = MEMORY_DEBUGGER.get() {
if let Ok(mut debugger) = debugger.lock() {
debugger.record_deallocation(id);
}
}
}
pub fn get_memory_stats() -> Option<MemoryStats> {
MEMORY_DEBUGGER.get()?.lock().ok().map(|d| d.stats())
}
pub fn detect_memory_leaks() -> Option<Vec<MemoryLeak>> {
MEMORY_DEBUGGER
.get()?
.lock()
.ok()
.map(|mut d| d.detect_leaks())
}
pub fn generate_memory_report() -> Option<MemoryReport> {
MEMORY_DEBUGGER
.get()?
.lock()
.ok()
.map(|d| d.generate_report())
}
pub fn get_realtime_stats() -> Option<RealTimeStats> {
MEMORY_DEBUGGER
.get()?
.lock()
.ok()
.map(|d| d.realtime_stats())
}
pub fn is_under_memory_pressure() -> bool {
MEMORY_DEBUGGER
.get()
.and_then(|d| d.lock().ok())
.map(|d| d.is_under_pressure())
.unwrap_or(false)
}
pub fn get_pressure_level() -> SystemPressureLevel {
MEMORY_DEBUGGER
.get()
.and_then(|d| d.lock().ok())
.map(|d| d.get_pressure_level())
.unwrap_or(SystemPressureLevel::Normal)
}
pub fn force_leak_detection() -> Option<Vec<MemoryLeak>> {
MEMORY_DEBUGGER
.get()?
.lock()
.ok()
.map(|mut d| d.force_leak_detection())
}
pub fn get_leak_stats() -> Option<LeakStats> {
MEMORY_DEBUGGER.get()?.lock().ok().map(|d| d.leak_stats())
}
pub struct DebuggingAllocator<A: GlobalAlloc> {
inner: A,
}
impl<A: GlobalAlloc> DebuggingAllocator<A> {
pub const fn new(inner: A) -> Self {
Self { inner }
}
}
unsafe impl<A: GlobalAlloc> GlobalAlloc for DebuggingAllocator<A> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = self.inner.alloc(layout);
if !ptr.is_null() {
record_allocation(layout.size(), layout, None);
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
record_deallocation(0); self.inner.dealloc(ptr, layout);
}
}
pub type SystemDebuggingAllocator = DebuggingAllocator<System>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_memory_debugger_basic() {
let mut debugger = MemoryDebugger::new();
let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
let id = debugger.record_allocation(1024, layout, Some("test".to_string()));
assert_eq!(debugger.stats().total_allocated, 1024);
assert_eq!(debugger.stats().active_allocations, 1);
debugger.record_deallocation(id);
assert_eq!(debugger.stats().total_allocated, 0);
assert_eq!(debugger.stats().active_allocations, 0);
}
#[test]
fn test_leak_detection() {
let config = MemoryDebugConfig {
leak_threshold: Duration::from_millis(1),
..Default::default()
};
let mut debugger = MemoryDebugger::with_config(config);
let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
let _id = debugger.record_allocation(1024, layout, Some("potential_leak".to_string()));
std::thread::sleep(Duration::from_millis(2));
let leaks = debugger.detect_leaks();
assert!(!leaks.is_empty());
}
#[test]
fn test_pattern_analysis() {
let mut debugger = MemoryDebugger::new();
let layout_small = Layout::from_size_align(512, 8).expect("layout should be valid");
let layout_large = Layout::from_size_align(2048, 8).expect("layout should be valid");
for _ in 0..5 {
debugger.record_allocation(512, layout_small, Some("small".to_string()));
}
for _ in 0..3 {
debugger.record_allocation(2048, layout_large, Some("large".to_string()));
}
let patterns = debugger.analyze_patterns();
assert!(!patterns.is_empty());
let has_small = patterns.iter().any(|p| p.size_class == "small");
let has_medium = patterns.iter().any(|p| p.size_class == "medium");
assert!(has_small || has_medium); }
#[test]
fn test_enhanced_leak_detection() {
let config = MemoryDebugConfig {
leak_threshold: Duration::from_millis(1),
..Default::default()
};
let mut debugger = MemoryDebugger::with_config(config);
let layout = Layout::from_size_align(1024 * 1024, 8).expect("layout should be valid");
let _id =
debugger.record_allocation(1024 * 1024, layout, Some("large_allocation".to_string()));
std::thread::sleep(Duration::from_millis(10));
let leaks = debugger.detect_leaks();
assert!(!leaks.is_empty());
let leak = &leaks[0];
assert!(leak.confidence > 0.0);
assert!(!leak.suggested_actions.is_empty());
assert!(matches!(
leak.risk_level,
LeakRiskLevel::High | LeakRiskLevel::Critical
));
}
#[test]
fn test_realtime_monitoring() {
let config = MemoryDebugConfig {
monitoring_interval: Duration::from_millis(1),
enable_real_time_monitoring: true,
..Default::default()
};
let mut debugger = MemoryDebugger::with_config(config);
let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
for _ in 0..5 {
debugger.record_allocation(1024, layout, None);
}
std::thread::sleep(Duration::from_millis(10));
debugger.record_allocation(1024, layout, None);
let stats = debugger.realtime_stats();
assert!(stats.current_usage > 0);
assert!(stats.recent_allocations > 0);
}
#[test]
fn test_pressure_level_calculation() {
let config = MemoryDebugConfig {
memory_warning_threshold: 1000,
memory_critical_threshold: 2000,
min_tracked_size: 1, monitoring_interval: Duration::from_millis(1), ..Default::default()
};
let mut debugger = MemoryDebugger::with_config(config);
let layout = Layout::from_size_align(1000, 8).expect("layout should be valid");
assert_eq!(
debugger.calculate_system_pressure(),
SystemPressureLevel::Normal
);
let _id1 = debugger.record_allocation(600, layout, None);
std::thread::sleep(Duration::from_millis(2)); assert_eq!(
debugger.calculate_system_pressure(),
SystemPressureLevel::Low
);
let _id2 = debugger.record_allocation(200, layout, None);
assert_eq!(
debugger.calculate_system_pressure(),
SystemPressureLevel::Medium
);
let _id3 = debugger.record_allocation(200, layout, None);
assert_eq!(
debugger.calculate_system_pressure(),
SystemPressureLevel::High
);
let _id4 = debugger.record_allocation(1000, layout, None);
assert_eq!(
debugger.calculate_system_pressure(),
SystemPressureLevel::Critical
);
}
#[test]
fn test_leak_stats() {
let config = MemoryDebugConfig {
leak_threshold: Duration::from_millis(1),
..Default::default()
};
let mut debugger = MemoryDebugger::with_config(config);
let layout_small = Layout::from_size_align(1024, 8).expect("layout should be valid");
let layout_large = Layout::from_size_align(1024 * 1024, 8).expect("layout should be valid");
let _id1 = debugger.record_allocation(1024, layout_small, Some("small_leak".to_string()));
let _id2 =
debugger.record_allocation(1024 * 1024, layout_large, Some("large_leak".to_string()));
std::thread::sleep(Duration::from_millis(50));
let leaks = debugger.detect_leaks();
assert!(!leaks.is_empty(), "Expected leaks to be detected");
let stats = debugger.leak_stats();
assert!(stats.total_leaks > 0);
assert!(stats.total_leaked_bytes > 0);
}
#[test]
fn test_global_api_functions() {
let config = MemoryDebugConfig {
enable_real_time_monitoring: true,
monitoring_interval: Duration::from_millis(1),
..Default::default()
};
let _ = init_memory_debugger_with_config(config);
let layout = Layout::from_size_align(1024, 8).expect("layout should be valid");
let id = record_allocation(1024, layout, Some("test".to_string()));
let stats = get_memory_stats();
assert!(stats.is_some());
let pressure_level = get_pressure_level();
assert_eq!(pressure_level, SystemPressureLevel::Normal);
let is_under_pressure = is_under_memory_pressure();
assert!(!is_under_pressure);
record_deallocation(id);
let report = generate_memory_report();
assert!(report.is_some());
}
}