Skip to content

Commit f7b8694

Browse files
committed
Clean up popen objects when calling start_process directly
Signed-off-by: Min Lim <min.yeol.lim@intel.com>
1 parent 86b3a3c commit f7b8694

2 files changed

Lines changed: 17 additions & 14 deletions

File tree

gprofiler/profilers/python_ebpf.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
from gprofiler.profilers import python
3535
from gprofiler.profilers.profiler_base import ProfilerBase
3636
from gprofiler.utils import (
37-
poll_process,
3837
random_prefix,
3938
reap_process,
4039
resource_path,
@@ -187,14 +186,14 @@ def test(self) -> None:
187186
# pyperf sometimes has a lot of output to stdout and stderr, which makes the process halt until read.
188187
process = start_process(cmd, tmpdir=self._pyperf_staticx_tmpdir, pipesize=1024 * 1024)
189188
try:
190-
poll_process(process, self._POLL_TIMEOUT, self._profiler_state.stop_event)
191-
except TimeoutError:
189+
wait_event(self._POLL_TIMEOUT, self._profiler_state.stop_event, lambda: process.poll() is not None)
190+
except (TimeoutError, StopEventSetException):
192191
process.kill()
193192
raise
194193
else:
195-
cleanup_process_reference(process)
196194
self._check_output(process, self.output_path)
197195
finally:
196+
cleanup_process_reference(process)
198197
self._staticx_cleanup()
199198

200199
def start(self) -> None:

gprofiler/utils/__init__.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from pathlib import Path
3434
from subprocess import CompletedProcess, Popen, TimeoutExpired
3535
from tempfile import TemporaryDirectory
36-
from threading import Event
36+
from threading import Event, Thread
3737
from types import FrameType
3838
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
3939

@@ -93,6 +93,7 @@ def start_process(
9393
cmd: Union[str, List[str]],
9494
via_staticx: bool = False,
9595
tmpdir: Optional[Path] = None,
96+
direct_call: bool = True,
9697
**kwargs: Any,
9798
) -> Popen:
9899
if isinstance(cmd, str):
@@ -135,7 +136,16 @@ def start_process(
135136
env=env,
136137
**kwargs,
137138
)
139+
140+
def process_exit_handler() -> None:
141+
process.communicate()
142+
# Remove from _processes and do any cleanup
143+
cleanup_process_reference(process)
144+
logger.critical(f"Process {process.pid} exited, total processes tracked {len(_processes)}")
145+
138146
_processes.append(process)
147+
if direct_call:
148+
Thread(target=process_exit_handler, daemon=True).start()
139149
return process
140150

141151

@@ -152,14 +162,6 @@ def wait_event(timeout: float, stop_event: Event, condition: Callable[[], bool],
152162
raise TimeoutError()
153163

154164

155-
def poll_process(process: Popen, timeout: float, stop_event: Event) -> None:
156-
try:
157-
wait_event(timeout, stop_event, lambda: process.poll() is not None)
158-
except StopEventSetException:
159-
process.kill()
160-
raise
161-
162-
163165
def remove_files_by_prefix(prefix: str) -> None:
164166
for f in glob.glob(f"{prefix}*"):
165167
os.unlink(f)
@@ -228,7 +230,7 @@ def run_process(
228230
stderr: bytes
229231

230232
reraise_exc: Optional[BaseException] = None
231-
with start_process(cmd, via_staticx, **kwargs) as process:
233+
with start_process(cmd, via_staticx, direct_call=False, **kwargs) as process:
232234
assert isinstance(process.args, str) or (
233235
isinstance(process.args, list) and all(isinstance(s, str) for s in process.args)
234236
), process.args # mypy
@@ -536,6 +538,8 @@ def cleanup_process_reference(process: Popen) -> None:
536538
def _exit_handler() -> None:
537539
for process in _processes:
538540
process.kill()
541+
# remove process in _processes
542+
cleanup_process_reference(process)
539543

540544

541545
def _sigint_handler(sig: int, frame: Optional[FrameType]) -> None:

0 commit comments

Comments
 (0)