https://github.com/python/cpython/commit/37b238f1a6f0d8738e44ccc516ca2a476c38e5ce
commit: 37b238f1a6f0d8738e44ccc516ca2a476c38e5ce
branch: main
author: Zain Nadeem <[email protected]>
committer: pablogsal <[email protected]>
date: 2026-06-28T18:58:56+02:00
summary:

gh-152356: Fix Windows blocking sampling after target process exit (#152471)

files:
A Misc/NEWS.d/next/Library/2026-06-28-12-45-09.gh-issue-152356.Dr4w2Q.rst
M Lib/test/test_profiling/test_sampling_profiler/test_blocking.py
M Modules/_remote_debugging/threads.c

diff --git a/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py 
b/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py
index 1f4b6da32810561..0a5541c733d4c77 100644
--- a/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py
+++ b/Lib/test/test_profiling/test_sampling_profiler/test_blocking.py
@@ -1,6 +1,9 @@
 """Tests for blocking mode sampling profiler."""
 
 import io
+import os
+import subprocess
+import sys
 import textwrap
 import unittest
 from unittest import mock
@@ -15,7 +18,11 @@
         "Test only runs when _remote_debugging is available"
     )
 
-from test.support import requires_remote_subprocess_debugging
+from test.support import (
+    SHORT_TIMEOUT,
+    os_helper,
+    requires_remote_subprocess_debugging,
+)
 
 from .helpers import test_subprocess
 
@@ -158,3 +165,51 @@ def test_generator_not_under_consumer_arithmetic(self):
             f"fibonacci_generator appears in the stack when consume_generator "
             f"is the leaf frame on an arithmetic line. This indicates "
             f"torn/inconsistent stack traces are being captured.")
+
+
+@requires_remote_subprocess_debugging()
[email protected](sys.platform == "win32", "Windows only")
+class TestBlockingModeCLI(unittest.TestCase):
+    def test_run_blocking_exits_after_target_process_exits(self):
+        script = 'print("done")\n'
+
+        tmpdir = os.path.abspath(os_helper.TESTFN + "_profiling_blocking")
+        with os_helper.temp_dir(tmpdir) as tmpdir:
+            script_path = os.path.join(tmpdir, "tiny_target.py")
+            profile_path = os.path.join(tmpdir, "blocking.bin")
+            with open(script_path, "w", encoding="utf-8") as file:
+                file.write(script)
+
+            cmd = [
+                sys.executable, "-m", "profiling.sampling", "run",
+                "--binary", "-o", profile_path,
+                "--mode=cpu", "--blocking", "-r", "100",
+                script_path,
+            ]
+            result = subprocess.run(
+                cmd,
+                cwd=tmpdir,
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+                text=True,
+                timeout=SHORT_TIMEOUT,
+            )
+
+            self.assertEqual(
+                result.returncode, 0,
+                f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}",
+            )
+            self.assertGreater(os.path.getsize(profile_path), 0)
+
+            replay = subprocess.run(
+                [sys.executable, "-m", "profiling.sampling", "replay",
+                 profile_path],
+                stdout=subprocess.PIPE,
+                stderr=subprocess.PIPE,
+                text=True,
+                timeout=SHORT_TIMEOUT,
+            )
+            self.assertEqual(
+                replay.returncode, 0,
+                f"stdout:\n{replay.stdout}\nstderr:\n{replay.stderr}",
+            )
diff --git 
a/Misc/NEWS.d/next/Library/2026-06-28-12-45-09.gh-issue-152356.Dr4w2Q.rst 
b/Misc/NEWS.d/next/Library/2026-06-28-12-45-09.gh-issue-152356.Dr4w2Q.rst
new file mode 100644
index 000000000000000..6a4ceecbe9279e2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-28-12-45-09.gh-issue-152356.Dr4w2Q.rst
@@ -0,0 +1,3 @@
+Fix a hang in ``profiling.sampling run --blocking`` on Windows when the
+target process exits. The profiler now finalizes binary profiles instead of
+continuing to sample the exited process.
diff --git a/Modules/_remote_debugging/threads.c 
b/Modules/_remote_debugging/threads.c
index 29f22f14c9b29f8..04c70cc96d6bd1e 100644
--- a/Modules/_remote_debugging/threads.c
+++ b/Modules/_remote_debugging/threads.c
@@ -862,6 +862,12 @@ _Py_RemoteDebug_StopAllThreads(RemoteUnwinderObject 
*unwinder, _Py_RemoteDebug_T
         return 0;
     }
 
+    if (!is_process_alive(unwinder->handle.hProcess)) {
+        PyErr_Format(PyExc_ProcessLookupError,
+            "Process %d has terminated", unwinder->handle.pid);
+        return -1;
+    }
+
     PyErr_Format(PyExc_RuntimeError, "NtSuspendProcess failed: 0x%lx", status);
     return -1;
 }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to