Enhance memory usage tracking with improved CPU calculation and error handling

This commit is contained in:
2025-08-18 09:49:28 +02:00
parent 2f8b3ba872
commit b6662d81da
+76 -62
View File
@@ -1617,50 +1617,71 @@ class MainWindow(QtWidgets.QMainWindow):
logger.info('Automation started successfully.') logger.info('Automation started successfully.')
def get_memory_usage(self): def get_memory_usage(self):
""" """Return dict of process/system resource usage with improved CPU calculation."""
Get current memory usage information for monitoring, including GPU memory.
Uses timeout protection to prevent UI hanging.
"""
try: try:
import psutil import psutil
process = psutil.Process() proc = psutil.Process()
memory_info = process.memory_info() mem_info = proc.memory_info()
cpu_percent = process.cpu_percent()
# Improved per-process CPU percent (delta since last call)
# Get system info cpu_percent = 0.0
system_memory = psutil.virtual_memory()
system_cpu = psutil.cpu_percent(interval=0.1) # System-wide CPU usage
# Get GPU information with error handling (non-blocking)
try: try:
gpu_info = get_gpu_memory_info() now = time.time()
times = proc.cpu_times()
if self._last_proc_cpu_times is not None and self._last_cpu_timestamp is not None:
prev_total = (self._last_proc_cpu_times.user + getattr(self._last_proc_cpu_times, 'system', 0))
curr_total = (times.user + getattr(times, 'system', 0))
delta_proc = curr_total - prev_total
delta_time = max(1e-6, now - self._last_cpu_timestamp)
cpu_percent = (delta_proc / delta_time) * 100.0 / max(1, self._num_cpus)
if cpu_percent < 0:
cpu_percent = 0.0
elif cpu_percent > 100:
cpu_percent = min(cpu_percent, 100.0)
self._last_proc_cpu_times = times
self._last_cpu_timestamp = now
except Exception as e: except Exception as e:
logger.debug(f"GPU detection failed: {e}") logger.debug(f"CPU calc delta failed, fallback: {e}")
gpu_info = {'has_gpu': False, 'total_mb': 0, 'used_mb': 0, 'free_mb': 0, 'utilization_percent': 0, 'gpu_name': 'Error', 'method': 'error'} try:
cpu_percent = proc.cpu_percent(interval=0.0)
except Exception:
cpu_percent = 0.0
# System metrics (non-blocking)
virt = psutil.virtual_memory()
try:
system_cpu = psutil.cpu_percent(interval=0.0)
except Exception:
system_cpu = psutil.cpu_percent(interval=0.05)
# GPU metrics
try:
gpu = get_gpu_memory_info()
except Exception as ge:
logger.debug(f"GPU info failed: {ge}")
gpu = {'has_gpu': False, 'total_mb': 0, 'used_mb': 0, 'free_mb': 0, 'utilization_percent': 0, 'gpu_name': 'Error', 'method': 'error'}
return { return {
'process_memory_mb': memory_info.rss / 1024 / 1024, # MB 'process_memory_mb': mem_info.rss / 1024 / 1024,
'process_memory_percent': process.memory_percent(), 'process_memory_percent': proc.memory_percent(),
'cpu_percent': cpu_percent, # Process CPU 'cpu_percent': cpu_percent,
'system_cpu_percent': system_cpu, # System-wide CPU 'system_cpu_percent': system_cpu,
'system_memory_percent': system_memory.percent, 'system_memory_percent': virt.percent,
'system_memory_available_gb': system_memory.available / 1024 / 1024 / 1024, # GB 'system_memory_available_gb': virt.available / 1024 / 1024 / 1024,
'system_memory_total_gb': system_memory.total / 1024 / 1024 / 1024, # GB 'system_memory_total_gb': virt.total / 1024 / 1024 / 1024,
'system_memory_used_gb': system_memory.used / 1024 / 1024 / 1024, # GB 'system_memory_used_gb': virt.used / 1024 / 1024 / 1024,
'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0,
'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0,
'has_psutil': True, 'has_psutil': True,
# GPU information 'gpu_has_gpu': gpu['has_gpu'],
'gpu_has_gpu': gpu_info['has_gpu'], 'gpu_total_mb': gpu['total_mb'],
'gpu_total_mb': gpu_info['total_mb'], 'gpu_used_mb': gpu['used_mb'],
'gpu_used_mb': gpu_info['used_mb'], 'gpu_free_mb': gpu['free_mb'],
'gpu_free_mb': gpu_info['free_mb'], 'gpu_utilization_percent': gpu['utilization_percent'],
'gpu_utilization_percent': gpu_info['utilization_percent'], 'gpu_name': gpu['gpu_name'],
'gpu_name': gpu_info['gpu_name'], 'gpu_method': gpu.get('method', 'none')
'gpu_method': gpu_info.get('method', 'none')
} }
except ImportError: except ImportError:
# psutil not available, minimal info without GPU detection to prevent hanging
return { return {
'process_memory_mb': 0, 'process_memory_mb': 0,
'process_memory_percent': 0, 'process_memory_percent': 0,
@@ -1673,7 +1694,6 @@ class MainWindow(QtWidgets.QMainWindow):
'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0,
'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0,
'has_psutil': False, 'has_psutil': False,
# Minimal GPU info to prevent hanging
'gpu_has_gpu': False, 'gpu_has_gpu': False,
'gpu_total_mb': 0, 'gpu_total_mb': 0,
'gpu_used_mb': 0, 'gpu_used_mb': 0,
@@ -1683,19 +1703,19 @@ class MainWindow(QtWidgets.QMainWindow):
'gpu_method': 'disabled' 'gpu_method': 'disabled'
} }
except Exception as e: except Exception as e:
logger.warning(f"Error getting memory usage (non-critical): {e}") logger.warning(f"get_memory_usage fallback: {e}")
# Fallback minimal info to prevent hanging
return { return {
'process_memory_mb': 0, 'process_memory_mb': 0,
'process_memory_percent': 0, 'process_memory_percent': 0,
'cpu_percent': 0, 'cpu_percent': 0,
'system_cpu_percent': 0,
'system_memory_percent': 0, 'system_memory_percent': 0,
'system_memory_available_gb': 0, 'system_memory_available_gb': 0,
'system_memory_total_gb': 0,
'system_memory_used_gb': 0,
'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0, 'template_cache_size': len(template_cache._cache) if hasattr(template_cache, '_cache') else 0,
'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0, 'cooldown_entries': len(self._step_cooldown) if hasattr(self, '_step_cooldown') else 0,
'has_psutil': False, 'has_psutil': False,
'error': str(e),
# Minimal GPU info to prevent hanging
'gpu_has_gpu': False, 'gpu_has_gpu': False,
'gpu_total_mb': 0, 'gpu_total_mb': 0,
'gpu_used_mb': 0, 'gpu_used_mb': 0,
@@ -1786,6 +1806,15 @@ class MainWindow(QtWidgets.QMainWindow):
self._memory_cleanup_timer.start(300000) # Cleanup every 5 minutes self._memory_cleanup_timer.start(300000) # Cleanup every 5 minutes
self._last_memory_cleanup = time.time() self._last_memory_cleanup = time.time()
# Improved CPU usage tracking (store previous CPU times for delta calculation)
self._last_proc_cpu_times = None
self._last_cpu_timestamp = None
try:
import psutil # Local import guarded; may fail if psutil missing
self._num_cpus = psutil.cpu_count(logical=True) or 1
except Exception:
self._num_cpus = 1
# Resource usage tracking for max/average values # Resource usage tracking for max/average values
self._resource_history = { self._resource_history = {
'memory_mb': [], 'memory_mb': [],
@@ -1829,29 +1858,20 @@ class MainWindow(QtWidgets.QMainWindow):
QtCore.QTimer.singleShot(1000, self._save_window_geometry) QtCore.QTimer.singleShot(1000, self._save_window_geometry)
def _check_system_theme_change(self): def _check_system_theme_change(self):
""" """Detect system theme change when in 'system' mode and re-apply theme."""
Check if the system theme has changed and update accordingly.
Only runs when theme_mode is 'system'.
"""
if self.theme_mode != 'system': if self.theme_mode != 'system':
self.theme_monitor_timer.stop() self.theme_monitor_timer.stop()
return return
try: try:
current_system_theme = self.is_system_dark_theme() current_system_theme = self.is_system_dark_theme()
if self._last_system_theme is None: if self._last_system_theme is None:
self._last_system_theme = current_system_theme self._last_system_theme = current_system_theme
return return
if current_system_theme != self._last_system_theme: if current_system_theme != self._last_system_theme:
logger.info(f'System theme changed to: {"Dark" if current_system_theme else "Light"}')
self._last_system_theme = current_system_theme self._last_system_theme = current_system_theme
self.apply_theme() self.apply_theme()
self._update_theme_combo_text()
except Exception as e: except Exception as e:
logger.debug(f"Error checking system theme change: {e}") logger.debug(f"System theme check failed: {e}")
def _update_resource_history(self, memory_info): def _update_resource_history(self, memory_info):
""" """
@@ -1937,7 +1957,6 @@ class MainWindow(QtWidgets.QMainWindow):
return return
try: try:
# Add timeout protection for memory info gathering
memory_info = self.get_memory_usage() memory_info = self.get_memory_usage()
# Update resource display (this should be fast) # Update resource display (this should be fast)
@@ -1967,9 +1986,9 @@ class MainWindow(QtWidgets.QMainWindow):
cpu_msg = f"High CPU usage: {memory_info['cpu_percent']:.1f}%" cpu_msg = f"High CPU usage: {memory_info['cpu_percent']:.1f}%"
self._log_warning_once("cpu_high", cpu_msg) self._log_warning_once("cpu_high", cpu_msg)
# GPU memory usage warning # GPU utilization warning
if memory_info.get('gpu_has_gpu', False) and memory_info.get('gpu_utilization_percent', 0) > 90: if memory_info.get('gpu_has_gpu', False) and memory_info.get('gpu_utilization_percent', 0) > 90:
gpu_msg = f"High GPU memory usage: {memory_info['gpu_utilization_percent']:.1f}%" gpu_msg = f"High GPU usage: {memory_info['gpu_utilization_percent']:.1f}%"
self._log_warning_once("gpu_high", gpu_msg) self._log_warning_once("gpu_high", gpu_msg)
except Exception as e: except Exception as e:
@@ -2984,17 +3003,12 @@ class MainWindow(QtWidgets.QMainWindow):
subcontrol-position: center right; subcontrol-position: center right;
}} }}
QComboBox::down-arrow {{ QComboBox::down-arrow {{
image: none; /* Use native arrow; prior triangle via borders rendered as rectangles on some systems */
width: 0px; width: 12px;
height: 0px; height: 12px;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-top: 6px solid {colors['foreground']};
margin: 0px;
padding: 0px;
}} }}
QComboBox::down-arrow:hover {{ QComboBox::down-arrow:hover {{
border-top-color: {colors['accent']}; /* Keep default hover */
}} }}
QComboBox QAbstractItemView {{ QComboBox QAbstractItemView {{
background-color: {colors['input_bg']}; background-color: {colors['input_bg']};