Skip to content

Commit f0de45a

Browse files
committed
Sync processes in test_sample_profiler
1 parent 8d9cbfe commit f0de45a

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

Lib/profile/sample.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def sample(self, collector, duration_sec=10):
5151
collector.collect(stack_frames)
5252
except ProcessLookupError:
5353
break
54-
except (RuntimeError, UnicodeDecodeError, MemoryError):
54+
except (RuntimeError, UnicodeDecodeError, MemoryError, OSError):
5555
errors += 1
5656
except Exception as e:
5757
if not self._is_process_running():

Lib/test/test_sample_profiler.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
import io
55
import marshal
66
import os
7+
import socket
78
import subprocess
89
import sys
910
import tempfile
10-
import time
1111
import unittest
1212
from unittest import mock
1313

@@ -17,7 +17,8 @@
1717
)
1818

1919
from test.support.os_helper import unlink
20-
from test.support import force_not_colorized_test_class
20+
from test.support import force_not_colorized_test_class, SHORT_TIMEOUT
21+
from test.support.socket_helper import find_unused_port
2122

2223
PROCESS_VM_READV_SUPPORTED = False
2324

@@ -47,18 +48,47 @@ def __repr__(self):
4748

4849

4950
@contextlib.contextmanager
50-
def test_subprocess(script, startup_delay=0.1):
51+
def test_subprocess(script):
52+
# Find an unused port for socket communication
53+
port = find_unused_port()
54+
55+
# Inject socket connection code at the beginning of the script
56+
socket_code = f'''
57+
import socket
58+
_test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
59+
_test_sock.connect(('localhost', {port}))
60+
_test_sock.sendall(b"ready")
61+
'''
62+
63+
# Combine socket code with user script
64+
full_script = socket_code + script
65+
66+
# Create server socket to wait for process to be ready
67+
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
68+
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
69+
server_socket.bind(("localhost", port))
70+
server_socket.settimeout(SHORT_TIMEOUT)
71+
server_socket.listen(1)
72+
5173
proc = subprocess.Popen(
52-
[sys.executable, "-c", script],
74+
[sys.executable, "-c", full_script],
5375
stdout=subprocess.DEVNULL,
5476
stderr=subprocess.DEVNULL,
5577
)
5678

79+
client_socket = None
5780
try:
58-
if startup_delay > 0:
59-
time.sleep(startup_delay)
81+
# Wait for process to connect and send ready signal
82+
client_socket, _ = server_socket.accept()
83+
server_socket.close()
84+
response = client_socket.recv(1024)
85+
if response != b"ready":
86+
raise RuntimeError(f"Unexpected response from subprocess: {response}")
87+
6088
yield proc
6189
finally:
90+
if client_socket is not None:
91+
client_socket.close()
6292
if proc.poll() is None:
6393
proc.kill()
6494
proc.wait()
@@ -1604,23 +1634,25 @@ def test_invalid_output_format_with_mocked_profiler(self):
16041634
)
16051635

16061636
def test_is_process_running(self):
1607-
with (test_subprocess("import time; time.sleep(0.2)") as proc,
1637+
with (test_subprocess("import time; time.sleep(1000)") as proc,
16081638
mock.patch("_remote_debugging.RemoteUnwinder") as mock_unwinder_class):
16091639
mock_unwinder_class.return_value = mock.MagicMock()
16101640
profiler = SampleProfiler(pid=proc.pid, sample_interval_usec=1000, all_threads=False)
1611-
16121641
self.assertTrue(profiler._is_process_running())
1642+
proc.kill()
16131643

16141644
# Exit the context manager to ensure the process is terminated
16151645
self.assertFalse(profiler._is_process_running())
16161646

16171647
@unittest.skipUnless(sys.platform == "linux", "Only valid on Linux")
16181648
def test_esrch_signal_handling(self):
1619-
with test_subprocess("import time; time.sleep(0.1)") as proc:
1649+
with test_subprocess("import time; time.sleep(1000)") as proc:
16201650
unwinder = _remote_debugging.RemoteUnwinder(proc.pid)
16211651
initial_trace = unwinder.get_stack_trace()
16221652
self.assertIsNotNone(initial_trace)
16231653

1654+
proc.kill()
1655+
16241656
# Wait for the process to die and try to get another trace
16251657
proc.wait()
16261658

0 commit comments

Comments
 (0)