| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
faulthandler.dump_traceback() and faulthandler.dump_traceback_later() cap their output at 100 threads via a hardcoded MAX_NTHREADS = 100 in Python/traceback.c. Past the cap, the dump writes "...\n" and stops.
/* Python/traceback.c */
#define MAX_NTHREADS 100
const char* _Py_NO_SANITIZE_THREAD
_Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
PyThreadState *current_tstate)
{
...
if (nthreads >= MAX_NTHREADS) {
PUTS(fd, "...\n");
break;
}
...
}The constant has been there since faulthandler was added in 2010 (gh-2762). It's still documented as a fixed limit in Doc/library/faulthandler.rst:
It is limited to 100 frames and 100 threads.
Proposal: add a keyword-only max_threads to both functions, default 100. Callers that omit it get identical behavior to today.
faulthandler.dump_traceback(file=sys.stderr, all_threads=True, *, max_threads=100)
faulthandler.dump_traceback_later(timeout, repeat=False, file=sys.stderr,
exit=False, *, max_threads=100)100 was reasonable in 2010, but a few things can make it a problem now:
Library-internal thread counts. A google-cloud-firestore Watch subscription spawns ~2 long-lived gRPC threads (google.api_core.bidi, grpc._channel.consume_request_iterator). 50 active subscriptions ≈ 100 threads. Other widely-used libraries (gRPC channels generally, ThreadPoolExecutor, asyncio's default executor) push it further. Modern server processes routinely run with 100+ long-lived threads, regardless of what the application code itself spawns.
Dump order is newest-thread-first. _Py_DumpTracebackThreads walks tstate via PyInterpreterState_ThreadHead → tstate->next. New thread states are prepended to the list by add_threadstate() in Python/pystate.c, so this walk visits the most-recently-created thread first and the main thread last. With more than 100 threads alive, the cap is guaranteed to cut off the oldest entries - including the main thread.
When you reach for dump_traceback_later as a deadlock-detection watchdog, the dump can arrive missing the main thread - the one you actually wanted.
Searched the issue tracker, PR history, Discourse, and python-dev. Nothing found. gh-98825 is adjacent (per-thread context for tracebacks) but doesn't touch the cap.
None found.
Patch ready: ~180 lines across 12 files (most auto-generated clinic + globals). Builds clean on current main. test_faulthandler passes 50/50, 6 platform skips. Includes:
| File | Change |
|---|---|
| Python/traceback.c | _Py_DumpTracebackThreads gains max_nthreads; MAX_NTHREADS define moves to the header (renamed _Py_TRACEBACK_MAX_NTHREADS). |
| Include/internal/pycore_traceback.h | Expose _Py_TRACEBACK_MAX_NTHREADS; update function signature. |
| Include/internal/pycore_faulthandler.h | Add max_nthreads to the watchdog thread struct. |
| Modules/faulthandler.c | New max_threads clinic kwarg on both functions (default 100). Fatal-signal handler passes _Py_TRACEBACK_MAX_NTHREADS explicitly. |
| Python/pylifecycle.c | _Py_FatalError_DumpTracebacks passes _Py_TRACEBACK_MAX_NTHREADS explicitly. |
| Doc/library/faulthandler.rst | Document the kwarg + versionchanged. |
| Lib/test/test_faulthandler.py | Two new tests. |
Default max_threads=100 matches the old cap, so omitting the kwarg gives no change. In-tree fatal-signal callers and the watchdog all pass _Py_TRACEBACK_MAX_NTHREADS explicitly.
No public C API change. _Py_DumpTracebackThreads and _Py_TRACEBACK_MAX_NTHREADS both live in pycore_traceback.h, gated by Py_BUILD_CORE.
register() could get the same kwarg. Left it out to keep the diff minimal. Signal-handler path has signal-safety constraints that make the rationale weaker there but not zero. Easy follow-up.
MAX_FRAME_DEPTH is similarly hardcoded. Same pattern would apply but ergonomics differ - a deep stack on a deadlocked thread is usually what you want, not what you'd cap. Probably a separate proposal.
I'll open a PR if there's interest as scoped.
3.15 (current main).
Linux, macOS.
This is a minor feature, which does not need previous discussion elsewhere
No response
| Back | FazBrowse Home | New Git URL |
Activity
ZeroIntensity commented on Apr 28, 2026
I think this makes sense, and I'd be happy to review and merge a PR. We should also make the frame limit and the stack limit configurable while we're at it.
Any objections @vstinner?
vstinner commented on Apr 28, 2026
Yes, you can open a PR to add max_threads parameter.
I'm not sure that it's useful. Nobody asked to display more or less frames so far.
gh-149085: Add `max_threads` keyword to `faulthandler.dump_traceback(…