(self)
| 168 | pass |
| 169 | |
| 170 | def _monitor_loop(self): |
| 171 | # Note: There is an inherent TOCTOU race between discovering a child |
| 172 | # process and checking if it's Python. This is expected for process monitoring. |
| 173 | while not self._stop_event.is_set(): |
| 174 | try: |
| 175 | self._poll_count += 1 |
| 176 | |
| 177 | # Periodically clean up completed profilers to avoid memory buildup |
| 178 | if self._poll_count % _CLEANUP_INTERVAL_CYCLES == 0: |
| 179 | self._cleanup_completed_profilers() |
| 180 | |
| 181 | children = set(get_child_pids(self.parent_pid, recursive=True)) |
| 182 | |
| 183 | with self._lock: |
| 184 | new_children = children - self._known_children |
| 185 | self._known_children.update(new_children) |
| 186 | |
| 187 | for child_pid in new_children: |
| 188 | # Only spawn profiler if this is actually a Python process |
| 189 | if is_python_process(child_pid): |
| 190 | self._spawn_profiler_for_child(child_pid) |
| 191 | |
| 192 | except ProcessLookupError: |
| 193 | # Parent process exited, stop monitoring |
| 194 | break |
| 195 | except Exception as e: |
| 196 | # Log error but continue monitoring |
| 197 | print( |
| 198 | f"Warning: Error in child monitor loop: {e}", |
| 199 | file=sys.stderr, |
| 200 | ) |
| 201 | |
| 202 | self._stop_event.wait(timeout=_CHILD_POLL_INTERVAL_SEC) |
| 203 | |
| 204 | def _cleanup_completed_profilers(self): |
| 205 | with self._lock: |
nothing calls this directly
no test coverage detected