https://github.com/python/cpython/commit/27434c68f8c9d8f5e3e1708948a339999172a4bc
commit: 27434c68f8c9d8f5e3e1708948a339999172a4bc
branch: main
author: Pablo Galindo Salgado <[email protected]>
committer: ambv <[email protected]>
date: 2026-01-03T15:37:16+01:00
summary:
gh-138122: Skip threads on EPERM in blocking mode profiler (GH-143368)
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.
files:
A Misc/NEWS.d/next/Library/2026-01-02-17-26-33.gh-issue-143368.m3EF9E.rst
M 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
index 00000000000000..4c07e4569acc3c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-01-02-17-26-33.gh-issue-143368.m3EF9E.rst
@@ -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.
diff --git a/Modules/_remote_debugging/threads.c
b/Modules/_remote_debugging/threads.c
index d40031abbdc0c7..3100b83c8f4899 100644
--- a/Modules/_remote_debugging/threads.c
+++ b/Modules/_remote_debugging/threads.c
@@ -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
_______________________________________________
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]