Skip to content

Commit ec3b20d

Browse files
committed
gh-151496: Use process groups in TraceBackend in test_dtrace
Run the generic DTrace/SystemTap commands in a new process group and terminate the whole group on timeout. This prevents a forked tracer child from keeping stdout/stderr pipes open after the direct tracer process is killed.
1 parent 5e0747d commit ec3b20d

1 file changed

Lines changed: 24 additions & 11 deletions

File tree

Lib/test/test_dtrace.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,13 @@ class TraceBackend:
7575
COMMAND_ARGS = []
7676

7777
def run_case(self, name, optimize_python=None):
78-
actual_output = normalize_trace_output(self.trace_python(
79-
script_file=abspath(name + self.EXTENSION),
80-
python_file=abspath(name + ".py"),
81-
optimize_python=optimize_python))
78+
try:
79+
actual_output = normalize_trace_output(self.trace_python(
80+
script_file=abspath(name + self.EXTENSION),
81+
python_file=abspath(name + ".py"),
82+
optimize_python=optimize_python))
83+
except subprocess.TimeoutExpired:
84+
raise AssertionError(f"{self.COMMAND[0]} timed out")
8285

8386
with open(abspath(name + self.EXTENSION + ".expected")) as f:
8487
expected_output = f.read().rstrip()
@@ -91,25 +94,35 @@ def generate_trace_command(self, script_file, subcommand=None):
9194
command += ["-c", subcommand]
9295
return command
9396

94-
def trace(self, script_file, subcommand=None):
97+
def trace(self, script_file, subcommand=None, *, timeout=None):
9598
command = self.generate_trace_command(script_file, subcommand)
96-
stdout, _ = subprocess.Popen(command,
97-
stdout=subprocess.PIPE,
98-
stderr=subprocess.STDOUT,
99-
universal_newlines=True).communicate()
99+
proc = create_process_group(command,
100+
stdout=subprocess.PIPE,
101+
stderr=subprocess.STDOUT,
102+
universal_newlines=True)
103+
try:
104+
stdout, _ = proc.communicate(timeout=timeout)
105+
except subprocess.TimeoutExpired:
106+
kill_process_group(proc)
107+
raise
100108
return stdout
101109

102110
def trace_python(self, script_file, python_file, optimize_python=None):
103111
python_flags = []
104112
if optimize_python:
105113
python_flags.extend(["-O"] * optimize_python)
106114
subcommand = " ".join([sys.executable] + python_flags + [python_file])
107-
return self.trace(script_file, subcommand)
115+
return self.trace(script_file, subcommand, timeout=60)
108116

109117
def assert_usable(self):
110118
try:
111-
output = self.trace(abspath("assert_usable" + self.EXTENSION))
119+
output = self.trace(abspath("assert_usable" + self.EXTENSION),
120+
timeout=10)
112121
output = output.strip()
122+
except subprocess.TimeoutExpired:
123+
raise unittest.SkipTest(
124+
f"{self.COMMAND[0]} timed out during usability check"
125+
)
113126
except (FileNotFoundError, NotADirectoryError, PermissionError) as fnfe:
114127
output = str(fnfe)
115128
if output != "probe: success":

0 commit comments

Comments
 (0)