Files
herolib_python/herolib/infra/tmuxrunner/process_monitor.py
2025-08-22 13:18:50 +02:00

89 lines
3.3 KiB
Python

import psutil
from typing import List, Optional, Tuple
from datetime import datetime
from libtmux.pane import Pane
from .model import ProcessMetrics
class ProcessMonitor:
"""Monitor processes running in tmux panes using psutil."""
@staticmethod
def get_pane_process_tree(pane: Pane) -> Tuple[Optional[psutil.Process], List[psutil.Process]]:
"""Get the main process and all child processes for a tmux pane."""
try:
pane_pid = pane.pane_pid
if pane_pid is None:
return None, []
# Get the main process
try:
main_process = psutil.Process(int(pane_pid))
except (psutil.NoSuchProcess, ValueError):
return None, []
# Get all children recursively
children = []
try:
children = main_process.children(recursive=True)
except psutil.NoSuchProcess:
pass
return main_process, children
except Exception as e:
print(f"Error getting process tree: {e}")
return None, []
@staticmethod
def get_process_metrics(pane: Pane) -> ProcessMetrics:
"""Get CPU and memory metrics for all processes in a pane."""
metrics = ProcessMetrics()
metrics.last_updated = datetime.now().isoformat()
main_proc, children = ProcessMonitor.get_pane_process_tree(pane)
if main_proc is None:
return metrics
try:
# Get main process metrics
if main_proc.is_running():
metrics.cpu_percent = main_proc.cpu_percent(interval=0.1)
mem_info = main_proc.memory_info()
metrics.memory_rss = mem_info.rss
metrics.memory_vms = mem_info.vms
metrics.memory_percent = main_proc.memory_percent()
metrics.num_threads = main_proc.num_threads()
# Get children metrics
metrics.num_children = len(children)
for child in children:
try:
if child.is_running():
metrics.children_cpu_percent += child.cpu_percent(interval=0.1)
child_mem = child.memory_info()
metrics.children_memory_rss += child_mem.rss
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
print(f"Error getting process metrics: {e}")
return metrics
@staticmethod
def is_process_running_command(pane: Pane, command_pattern: str) -> bool:
"""Check if a specific command is running in the pane."""
main_proc, children = ProcessMonitor.get_pane_process_tree(pane)
all_processes = [main_proc] + children if main_proc else children
for proc in all_processes:
try:
if proc and proc.is_running():
cmdline = " ".join(proc.cmdline())
if command_pattern in cmdline:
return True
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return False