]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-138122: Skip threads on EPERM in blocking mode profiler (GH-143368)
authorPablo Galindo Salgado <Pablogsal@gmail.com>
Sat, 3 Jan 2026 14:37:16 +0000 (14:37 +0000)
committerGitHub <noreply@github.com>
Sat, 3 Jan 2026 14:37:16 +0000 (15:37 +0100)
When using blocking mode in the remote debugging profiler, ptrace calls
to seize threads can fail with EPERM if the thread has exited between
listing and attaching, is in a special kernel state, or is already being
traced. Previously this raised a RuntimeError that was caught by the
Python sampling loop,and retried indefinitely since EPERM is
a persistent condition that will not resolve on its own.

Treat EPERM the same as ESRCH by returning 1 (skip this thread) instead
of -1 (fatal error). This allows profiling to continue with the threads
that can be traced rather than entering an endless retry loop printing
the same error message repeatedly.

Misc/NEWS.d/next/Library/2026-01-02-17-26-33.gh-issue-143368.m3EF9E.rst [new file with mode: 0644]
Modules/_remote_debugging/threads.c

diff --git a/Misc/NEWS.d/next/Library/2026-01-02-17-26-33.gh-issue-143368.m3EF9E.rst b/Misc/NEWS.d/next/Library/2026-01-02-17-26-33.gh-issue-143368.m3EF9E.rst
new file mode 100644 (file)
index 0000000..4c07e45
--- /dev/null
@@ -0,0 +1,3 @@
+Fix endless retry loop in :mod:`profiling.sampling` blocking mode when
+threads cannot be seized due to ``EPERM``. Such threads are now skipped
+instead of causing repeated error messages. Patch by Pablo Galindo.
index d40031abbdc0c7347d37a02d56a9b84e13c8adda..3100b83c8f489978803d22470e7656de34937476 100644 (file)
@@ -632,6 +632,12 @@ seize_thread(pid_t tid)
     if (errno == ESRCH) {
         return 1;  // Thread gone, skip
     }
+    if (errno == EPERM) {
+        // Thread may have exited, be in a special state, or already be traced.
+        // Skip rather than fail - this avoids endless retry loops when
+        // threads transiently become inaccessible.
+        return 1;
+    }
     if (errno == EINVAL || errno == EIO) {
         // Fallback for older kernels
         if (ptrace(PTRACE_ATTACH, tid, NULL, NULL) == 0) {
@@ -639,8 +645,8 @@ seize_thread(pid_t tid)
             waitpid(tid, &status, __WALL);
             return 0;
         }
-        if (errno == ESRCH) {
-            return 1;  // Thread gone
+        if (errno == ESRCH || errno == EPERM) {
+            return 1;  // Thread gone or inaccessible
         }
     }
     return -1;  // Real error