Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2025-05-20 - Optimize psutil.process_iter overhead
**Learning:** Querying all attributes (like `memory_info` and `status`) in `psutil.process_iter` for every process on the system is highly inefficient, as it fetches expensive data for many processes we don't care about.
**Action:** Only query `['pid', 'name']` initially. Lazily fetch expensive attributes like `memory_info()` and `status()` on the individual process objects only after filtering for our target processes (e.g., 'excel.exe'). Wrap these in try-except to safely catch process termination exceptions like `psutil.NoSuchProcess`, `psutil.AccessDenied`, and `psutil.ZombieProcess`.
18 changes: 14 additions & 4 deletions desktop_services/excel_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,17 +206,27 @@ def get_running_instances(self) -> List[Dict[str, Any]]:
def _get_excel_process_snapshots(self) -> Dict[int, Dict[str, Any]]:
"""Find Excel processes with one psutil scan for refresh performance."""
snapshots: Dict[int, Dict[str, Any]] = {}
for proc in psutil.process_iter(["pid", "name", "memory_info", "status"]):
for proc in psutil.process_iter(["pid", "name"]):
try:
info = proc.info
process_name = info.get("name") or ""
if process_name.lower() != "excel.exe":
if process_name.casefold() != "excel.exe":
continue
pid = int(info["pid"])

# Lazily fetch expensive attributes only for target processes to avoid
# querying them for all system processes during the psutil iteration.
try:
memory_info = proc.memory_info()
status = proc.status()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
memory_info = None
status = "running"
Comment on lines +222 to +224
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip vanished Excel processes during lazy snapshot fetch

When proc.memory_info()/proc.status() raises NoSuchProcess in _get_excel_process_snapshots, the code now falls back to memory_info=None and status="running" and still records the PID. This creates phantom Excel instances if a process exits between process_iter and the lazy attribute reads, so get_running_instances() can report dead processes as live with 0 MB memory. In this exception path, the process should be skipped instead of added.

Useful? React with πŸ‘Β / πŸ‘Ž.


snapshots[pid] = {
"name": process_name,
"memory_mb": self._memory_mb_from_info(info.get("memory_info")),
"status": info.get("status") or "running",
"memory_mb": self._memory_mb_from_info(memory_info),
"status": status or "running",
}
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
Expand Down
17 changes: 13 additions & 4 deletions desktop_services/operations_cockpit_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,19 +852,28 @@ def _default_processes() -> list[dict[str, Any]]:
if psutil is None:
return []
processes = []
for proc in psutil.process_iter(["pid", "name", "memory_info", "status"]):
for proc in psutil.process_iter(["pid", "name"]):
try:
info = proc.info
except (psutil.NoSuchProcess, psutil.AccessDenied):
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
if str(info.get("name") or "").casefold() != "excel.exe":
continue

# Lazily fetch expensive attributes only for target processes to avoid
# querying them for all system processes during the psutil iteration.
try:
memory_info = proc.memory_info()
status = proc.status()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
memory_info = None
status = None
Comment on lines +868 to +870
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude terminated processes from Excel guard defaults

In ExcelSessionGuard._default_processes, the lazy memory_info()/status() exception handler catches NoSuchProcess but still appends that process with null fields. If Excel closes during iteration, snapshot() will overcount running_count and may surface stale risk signals for processes that no longer exist. This path should continue on NoSuchProcess/ZombieProcess rather than keeping the entry.

Useful? React with πŸ‘Β / πŸ‘Ž.

processes.append(
{
"pid": info.get("pid"),
"name": info.get("name"),
"memory_info": info.get("memory_info"),
"status": info.get("status"),
"memory_info": memory_info,
"status": status,
}
)
return processes
Expand Down
Loading