GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-149085: Add max_threads keyword to faulthandler.dump_traceback() by efroemling · Pull Request #149106 · python/cpython

Conversation

@efroemling

@efroemling efroemling commented Apr 28, 2026 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Closes #149085.

Adds a keyword-only max_threads argument to faulthandler.dump_traceback() and faulthandler.dump_traceback_later(), raising the per-call cap on the number of threads dumped (previously a hardcoded MAX_NTHREADS = 100 in Python/traceback.c). Default of 100 preserves existing behavior.

Motivation (covered in the issue): on server processes with many worker or gRPC threads, watchdog dumps silently lose the main thread because tstates are prepended to the interpreter's thread list and the cap chops the tail. This was the failure mode that prompted the issue.

Scope per @vstinner's confirmation in the issue: max_threads only; the frame/stack limits raised by @ZeroIntensity are left as-is for now.

The hardcoded 100 is moved to a new internal macro _Py_TRACEBACK_MAX_NTHREADS in pycore_traceback.h so the in-tree fatal-signal callers (faulthandler.c, pylifecycle.c) all share one source of truth.

  • Issue: faulthandler: make per-call thread dump cap configurable #149085

📚 Documentation preview 📚: https://cpython-previews--149106.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

brostosjoined reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
@efroemling
pythongh-149085: Add max_threads keyword to faulthandler.dump_traceba…
dbceabf
…ck()

Add a keyword-only `max_threads` argument to `dump_traceback()` and
`dump_traceback_later()`, defaulting to 100 to preserve existing
behavior. Allows server processes with many worker threads to dump
beyond the historical 100-thread cap (previously a hardcoded
`MAX_NTHREADS = 100` in `Python/traceback.c`).

The cap matters in practice: tstates are prepended to the
PyInterpreterState linked list, so the dump walks newest-first. With
more than 100 threads alive, the main thread (oldest, at the tail) is
silently elided from watchdog dumps -- exactly the thread that's
usually wanted.

The hardcoded value is moved to a new internal macro
`_Py_TRACEBACK_MAX_NTHREADS` in `pycore_traceback.h` so the in-tree
fatal-signal callers all reference one source of truth.
@efroemling
efroemling requested review from FFY00, ZeroIntensity and ericsnowcurrently as code owners April 28, 2026 16:44
@bedevere-app bedevere-app Bot added the awaiting review label Apr 28, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Apr 28, 2026
faulthandler: make per-call thread dump cap configurable #149085
Closed
@read-the-docs-community

read-the-docs-community Bot commented Apr 28, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #32472695 | 📁 Comparing 1a6eead against main (8a8d737)

  🔍 Preview build  

6 files changed · ± 6 modified

± Modified

  • contents.html
  • download.html
  • library/asyncio-task.html
  • library/faulthandler.html
  • whatsnew/3.15.html
  • whatsnew/changelog.html
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Apr 28, 2026
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to also add max_threads parameter to enable().

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Modules/faulthandler.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/traceback.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/faulthandler.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/faulthandler.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Doc/library/faulthandler.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Misc/NEWS.d/next/Library/2026-04-28-16-30-48.gh-issue-149085.5aNgBD.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Python/traceback.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_faulthandler.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_faulthandler.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_faulthandler.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@efroemling
Address review feedback from @vstinner
776e790
- Drop _Py_TRACEBACK_MAX_NTHREADS macro; use 0 as sentinel for default
  100 inside _Py_DumpTracebackThreads so internal callers don't have to
  pass the default explicitly.
- Rename max_nthreads -> max_threads everywhere for naming consistency
  with the public Python kwarg.
- Add max_threads kwarg to faulthandler.enable(); store in
  fatal_error.max_threads and pass through faulthandler_dump_traceback
  to the fatal-signal dump path on both POSIX and Windows.
- Drop the three redundant explanatory comments vstinner flagged.
- Doc: tighten the limitations bullet, drop implementation-detail
  mentions of the "..." truncation marker, switch versionchanged
  directives to "next", document the new enable() kwarg.
- Tests: assertEqual exact count, check whole-line "\n...\n" marker,
  use script_helper.assert_python_ok, drop the default-value test,
  add test_enable_max_threads exercising the fatal-signal path.
- NEWS: trim to two lines, mention all three functions.
@efroemling

efroemling commented Apr 28, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback.
I took a pass and tried to address everything (added max_threads to faulthandler.enable(), scaled back excessive comments, removed _Py_TRACEBACK_MAX_THREADS in favor of 0 for historical default, etc.).
Please holler if I missed anything or you notice anything else I can clean up or improve.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@efroemling
Skip test_enable_max_threads on free-threading builds
4689f4e
The fatal-signal handler only dumps the current thread when the GIL
is disabled (pythongh-104812 / 3.14 versionchanged), so the truncation
marker assertion in test_enable_max_threads fails on free-threading
CI runs. Skip it there.
vstinner
vstinner reviewed Apr 29, 2026
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace unsigned int max_threads with Py_ssize_t max_threads? Replace also unsigned int nthreads with Py_ssize_t nthreads in _Py_DumpTracebackThreads().

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction
Comment thread Modules/faulthandler.c Outdated
return;

faulthandler_dump_traceback(user->fd, user->all_threads, user->interp);
faulthandler_dump_traceback(user->fd, user->all_threads, user->interp, 0);

@vstinner vstinner Apr 29, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please modify faulthandler.register() to add a max_threads parameter.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner

vstinner commented Apr 29, 2026

Copy link
Copy Markdown
Member

I took a pass and tried to address everything (added max_threads to faulthandler.enable(), scaled back excessive comments, removed _Py_TRACEBACK_MAX_THREADS in favor of 0 for historical default, etc.).

Thanks for this big update!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity
ZeroIntensity reviewed Apr 29, 2026
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add an entry to "What's New in Python 3.15".

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@efroemling
Address review iteration 2: whatsnew entry + Py_ssize_t
553c3a4
- Add a 'faulthandler' entry to Doc/whatsnew/3.15.rst (per
  @ZeroIntensity).
- Switch the max_threads parameters/fields and the nthreads loop
  counter in _Py_DumpTracebackThreads to Py_ssize_t (per @vstinner).
@efroemling
efroemling requested a review from AA-Turner as a code owner April 29, 2026 14:43
@efroemling

efroemling commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

Ok I added the whats-new entry and switched the max_size args to Py_ssize_t. 👍

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Apr 29, 2026

Copy link
Copy Markdown
Member

Can you also modify faulthandler.register() to add a max_threads parameter?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

efroemling added 2 commits April 29, 2026 11:11
@efroemling
Add max_threads kwarg to faulthandler.register() too
d6f11fa
Per @vstinner's follow-up: also wire the kwarg through register() /
faulthandler_user. Stored in the per-signal user_signal_t struct; the
user-signal handler now passes user->max_threads to
faulthandler_dump_traceback instead of 0.

Doc, whatsnew, and NEWS entries updated to list all four functions.
@efroemling
Add test_register_max_threads
1a6eead
Mirrors test_enable_max_threads but uses register(SIGUSR1, ...) +
os.kill round-trip, matching the existing test_register_* pattern.
@efroemling

efroemling commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

Ok faulthandler.register() now has max_threads too. (and added an associated test). Holler if anything looks off there.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

vstinner
vstinner reviewed Apr 29, 2026
View reviewed changes
Comment thread Lib/test/test_faulthandler.py Outdated
ready.wait()
faulthandler.register(signal.SIGUSR1, all_threads=True,
max_threads=CAP)
os.kill(os.getpid(), signal.SIGUSR1)

@vstinner vstinner Apr 29, 2026

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To raise the signal, you can use:

Suggested change
os.kill(os.getpid(), signal.SIGUSR1)
signal.raise_signal(signal.SIGUSR1)

Can you use test_dump_traceback_max_threads() code to cleanup threads?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@efroemling
Clean up test_register_max_threads per @vstinner
846ce34
- Use signal.raise_signal() instead of os.kill(os.getpid(), ...).
- Match test_dump_traceback_max_threads thread-cleanup pattern:
  keep refs, no daemon=True, set the stop event in a finally block,
  and join() the workers.
@efroemling

efroemling commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

Ok; switched to raise_signal() and now doing test_dump_traceback_max_threads() style cleanup

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner mentioned this pull request Apr 30, 2026
Add PyUnstable_DumpTraceback() and PyUnstable_DumpTracebackThreads() capi-workgroup/decisions#101
Closed
4 of 5 tasks
vstinner
vstinner approved these changes Apr 30, 2026
View reviewed changes

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@bedevere-app bedevere-app Bot added awaiting merge and removed awaiting review labels Apr 30, 2026
@vstinner

vstinner commented Apr 30, 2026

Copy link
Copy Markdown
Member

@ZeroIntensity: Do you want to double check the change?

It would be good to merge this change before next Tuesday to have it in Python 3.15 beta1.

ZeroIntensity reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ZeroIntensity
ZeroIntensity approved these changes Apr 30, 2026
View reviewed changes

@ZeroIntensity ZeroIntensity left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM as well.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ZeroIntensity
ZeroIntensity merged commit 7686abe into python:main Apr 30, 2026
57 checks passed

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot removed the awaiting merge label Apr 30, 2026
@ZeroIntensity

ZeroIntensity commented Apr 30, 2026

Copy link
Copy Markdown
Member

Thanks for contributing :)

vstinner reacted with hooray emoji
All reactions
  • 🎉 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@vstinner vstinner vstinner approved these changes
@ZeroIntensity ZeroIntensity ZeroIntensity approved these changes
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
@FFY00 FFY00 Awaiting requested review from FFY00 FFY00 is a code owner
@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

faulthandler: make per-call thread dump cap configurable

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@efroemling @vstinner @ZeroIntensity
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.