Files
Modus/debug_memory.py
T
2025-09-01 12:39:28 +05:45

166 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""
Real-time memory monitor for debugging expanded player memory leaks.
This module provides functions to track memory usage in real-time.
"""
import psutil
import os
import gc
import threading
import time
from loguru import logger
class MemoryMonitor:
"""Real-time memory monitoring for debugging memory leaks."""
def __init__(self):
self.process = psutil.Process(os.getpid())
self.baseline_memory = None
self.last_memory = None
self.monitoring = False
self.monitor_thread = None
def get_memory_usage(self):
"""Get current memory usage in MB."""
return self.process.memory_info().rss / 1024 / 1024
def get_memory_details(self):
"""Get detailed memory information."""
memory_info = self.process.memory_info()
memory_percent = self.process.memory_percent()
return {
"rss_mb": memory_info.rss / 1024 / 1024,
"vms_mb": memory_info.vms / 1024 / 1024,
"percent": memory_percent,
"num_threads": self.process.num_threads(),
}
def set_baseline(self, label="Baseline"):
"""Set the baseline memory usage."""
self.baseline_memory = self.get_memory_usage()
logger.info(f"🎯 {label} memory: {self.baseline_memory:.1f} MB")
return self.baseline_memory
def log_memory_change(self, label="Memory Check", force_gc=True):
"""Log current memory usage and change from baseline."""
if force_gc:
gc.collect()
current_memory = self.get_memory_usage()
details = self.get_memory_details()
if self.baseline_memory:
delta = current_memory - self.baseline_memory
logger.info(
f"📊 {label}: {current_memory:.1f} MB (Δ: {delta:+.1f} MB) | Threads: {
details['num_threads']
}"
)
else:
logger.info(
f"📊 {label}: {current_memory:.1f} MB | Threads: {
details['num_threads']
}"
)
self.last_memory = current_memory
return current_memory
def log_memory_spike(self, threshold_mb=10):
"""Log if there's a significant memory increase."""
if self.last_memory:
current = self.get_memory_usage()
increase = current - self.last_memory
if increase > threshold_mb:
logger.warning(
f"🚨 MEMORY SPIKE: +{increase:.1f} MB (from {self.last_memory:.1f} to {current:.1f} MB)"
)
return True
return False
def start_continuous_monitoring(self, interval_seconds=2):
"""Start continuous background monitoring."""
if self.monitoring:
return
self.monitoring = True
def monitor_loop():
while self.monitoring:
try:
self.log_memory_change("Continuous Monitor", force_gc=False)
time.sleep(interval_seconds)
except Exception as e:
logger.error(f"Memory monitor error: {e}")
break
self.monitor_thread = threading.Thread(target=monitor_loop, daemon=True)
self.monitor_thread.start()
logger.info(
f"🔍 Started continuous memory monitoring (every {interval_seconds}s)"
)
def stop_continuous_monitoring(self):
"""Stop continuous monitoring."""
if self.monitoring:
self.monitoring = False
logger.info("🛑 Stopped continuous memory monitoring")
# Global memory monitor instance
memory_monitor = MemoryMonitor()
# Convenience functions for easy use
def set_memory_baseline(label="Baseline"):
"""Set memory baseline."""
return memory_monitor.set_baseline(label)
def log_memory(label="Memory Check"):
"""Log current memory usage."""
return memory_monitor.log_memory_change(label)
def start_memory_monitoring():
"""Start continuous memory monitoring."""
memory_monitor.start_continuous_monitoring()
def stop_memory_monitoring():
"""Stop continuous memory monitoring."""
memory_monitor.stop_continuous_monitoring()
def check_memory_spike():
"""Check for memory spike."""
return memory_monitor.log_memory_spike()
# Test function
def test_memory_monitor():
"""Test the memory monitor."""
print("Testing Memory Monitor...")
monitor = MemoryMonitor()
monitor.set_baseline("Test Start")
# Simulate some memory usage
big_list = [i for i in range(100000)]
monitor.log_memory_change("After creating big list")
del big_list
monitor.log_memory_change("After deleting big list")
print("Memory monitor test complete!")
if __name__ == "__main__":
test_memory_monitor()