From fbd9755e230308c88eed8b6ebc10a55944a52f22 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Tue, 1 Sep 2026 02:32:23 +0200 Subject: [PATCH 1/2] gh-156722: Terminate tracing processes gracefully on timeout for test_dtrace Send SIGTERM to the tracing process group before falling back to SIGKILL. --- Lib/test/test_dtrace.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 4967a18053057b3..a133d8d8971c313 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -90,15 +90,35 @@ def create_process_group(*args, **kwargs): kwargs['start_new_session'] = True return subprocess.Popen(*args, **kwargs) -def kill_process_group(proc): +def terminate_process_group(proc, timeout=10): if USE_PROCESS_GROUP: try: - os.killpg(proc.pid, signal.SIGKILL) + os.killpg(proc.pid, signal.SIGTERM) except ProcessLookupError: pass else: - proc.kill() - proc.communicate() # Clean up + proc.terminate() + + try: + proc.communicate(timeout=timeout) + except subprocess.TimeoutExpired: + if USE_PROCESS_GROUP: + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass + else: + proc.kill() + try: + proc.communicate(timeout=timeout) # Clean up + except subprocess.TimeoutExpired: + for pipe in (proc.stdin, proc.stdout, proc.stderr): + if pipe is not None: + pipe.close() + try: + proc.wait(timeout=timeout) + except subprocess.TimeoutExpired: + pass def run_readelf(cmd): @@ -163,7 +183,7 @@ def trace(self, script_file, subcommand=None, *, timeout=None, try: stdout, _ = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired: - kill_process_group(proc) + terminate_process_group(proc) raise if check_returncode and proc.returncode: raise AssertionError( @@ -350,7 +370,7 @@ def run_case(self, name, optimize_python=None): ) stdout, stderr = proc.communicate(timeout=60) except subprocess.TimeoutExpired: - kill_process_group(proc) + terminate_process_group(proc) raise AssertionError("bpftrace timed out") except (FileNotFoundError, PermissionError) as e: raise unittest.SkipTest(f"bpftrace not available: {e}") @@ -389,7 +409,7 @@ def assert_usable(self): ) stdout, stderr = proc.communicate(timeout=10) except subprocess.TimeoutExpired: - kill_process_group(proc) + terminate_process_group(proc) raise unittest.SkipTest("bpftrace timed out during usability check") except OSError as e: raise unittest.SkipTest(f"bpftrace not available: {e}") From 9a0640b4db63a13b1c0b4f1ef91b1e6195a6be79 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Tue, 22 Sep 2026 02:12:13 +0200 Subject: [PATCH 2/2] Address review --- Lib/test/test_dtrace.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index a133d8d8971c313..41b52662bd0d258 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -84,13 +84,14 @@ def normalize_trace_output(output): USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) +TERMINATE_TIMEOUT = 10 def create_process_group(*args, **kwargs): if USE_PROCESS_GROUP: kwargs['start_new_session'] = True return subprocess.Popen(*args, **kwargs) -def terminate_process_group(proc, timeout=10): +def terminate_process_group(proc): if USE_PROCESS_GROUP: try: os.killpg(proc.pid, signal.SIGTERM) @@ -100,7 +101,7 @@ def terminate_process_group(proc, timeout=10): proc.terminate() try: - proc.communicate(timeout=timeout) + proc.communicate(timeout=TERMINATE_TIMEOUT) except subprocess.TimeoutExpired: if USE_PROCESS_GROUP: try: @@ -109,16 +110,7 @@ def terminate_process_group(proc, timeout=10): pass else: proc.kill() - try: - proc.communicate(timeout=timeout) # Clean up - except subprocess.TimeoutExpired: - for pipe in (proc.stdin, proc.stdout, proc.stderr): - if pipe is not None: - pipe.close() - try: - proc.wait(timeout=timeout) - except subprocess.TimeoutExpired: - pass + proc.communicate(timeout=TERMINATE_TIMEOUT) # Clean up def run_readelf(cmd):