From 665bb1ddb761e50a5c2762ea877a497109c91280 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 18:19:59 +0200 Subject: [PATCH 1/3] gh-151496: Use process groups in test_dtrace Create a new process group to run bpftrace commands, so it's possible to kill also child processes on timeout. --- Lib/test/test_dtrace.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 6286b6d21b572e3..5b977c347edaf45 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -1,6 +1,7 @@ import dis import os.path import re +import signal import subprocess import sys import sysconfig @@ -50,6 +51,26 @@ def normalize_trace_output(output): ) +USE_PROCESS_GROUP = (hasattr(os, "setsid") and hasattr(os, "killpg")) + +def create_process_group(*args, **kwargs): + if USE_PROCESS_GROUP: + kwargs['start_new_session'] = True + return subprocess.Popen(*args, **kwargs) + +def kill_process_group(proc): + use_killpg = USE_PROCESS_GROUP + if use_killpg: + parent_sid = os.getsid(0) + sid = os.getsid(proc.pid) + use_killpg = (sid != parent_sid) + + if use_killpg: + os.killpg(proc.pid, signal.SIGKILL) + else: + proc.kill() + + class TraceBackend: EXTENSION = None COMMAND = None @@ -205,7 +226,7 @@ def run_case(self, name, optimize_python=None): program = self.PROGRAMS[name].format(python=sys.executable) try: - proc = subprocess.Popen( + proc = create_process_group( ["bpftrace", "-e", program, "-c", " ".join(subcommand)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -213,7 +234,7 @@ def run_case(self, name, optimize_python=None): ) stdout, stderr = proc.communicate(timeout=60) except subprocess.TimeoutExpired: - proc.kill() + kill_process_group(proc) raise AssertionError("bpftrace timed out") except (FileNotFoundError, PermissionError) as e: raise unittest.SkipTest(f"bpftrace not available: {e}") @@ -243,7 +264,7 @@ def assert_usable(self): # Check if bpftrace is available and can attach to USDT probes program = f'usdt:{sys.executable}:python:function__entry {{ printf("probe: success\\n"); exit(); }}' try: - proc = subprocess.Popen( + proc = create_process_group( ["bpftrace", "-e", program, "-c", f"{sys.executable} -c pass"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -251,7 +272,7 @@ def assert_usable(self): ) stdout, stderr = proc.communicate(timeout=10) except subprocess.TimeoutExpired: - proc.kill() + kill_process_group(proc) proc.communicate() # Clean up raise unittest.SkipTest("bpftrace timed out during usability check") except OSError as e: From 81ebe07c8273b2853fcb04cc78b48aca1c027b54 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Jun 2026 18:26:01 +0200 Subject: [PATCH 2/3] Simplify kill_process_group() --- Lib/test/test_dtrace.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 5b977c347edaf45..5ade826756b7c9c 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -59,13 +59,7 @@ def create_process_group(*args, **kwargs): return subprocess.Popen(*args, **kwargs) def kill_process_group(proc): - use_killpg = USE_PROCESS_GROUP - if use_killpg: - parent_sid = os.getsid(0) - sid = os.getsid(proc.pid) - use_killpg = (sid != parent_sid) - - if use_killpg: + if USE_PROCESS_GROUP: os.killpg(proc.pid, signal.SIGKILL) else: proc.kill() From b5bf28700ca1d710e25da538567030cf3e055594 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 16 Jun 2026 12:15:30 +0200 Subject: [PATCH 3/3] Address stratakis' review --- Lib/test/test_dtrace.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_dtrace.py b/Lib/test/test_dtrace.py index 5ade826756b7c9c..592f59d77f92217 100644 --- a/Lib/test/test_dtrace.py +++ b/Lib/test/test_dtrace.py @@ -60,9 +60,13 @@ def create_process_group(*args, **kwargs): def kill_process_group(proc): if USE_PROCESS_GROUP: - os.killpg(proc.pid, signal.SIGKILL) + try: + os.killpg(proc.pid, signal.SIGKILL) + except ProcessLookupError: + pass else: proc.kill() + proc.communicate() # Clean up class TraceBackend: @@ -267,7 +271,6 @@ def assert_usable(self): stdout, stderr = proc.communicate(timeout=10) except subprocess.TimeoutExpired: kill_process_group(proc) - proc.communicate() # Clean up raise unittest.SkipTest("bpftrace timed out during usability check") except OSError as e: raise unittest.SkipTest(f"bpftrace not available: {e}")