Skip to content

GH-126910: Add GNU backtrace support for unwinding JIT frames#149104

Open
diegorusso wants to merge 7 commits intopython:mainfrom
diegorusso:jit-backtrace-support
Open

GH-126910: Add GNU backtrace support for unwinding JIT frames#149104
diegorusso wants to merge 7 commits intopython:mainfrom
diegorusso:jit-backtrace-support

Conversation

@diegorusso
Copy link
Copy Markdown
Contributor

@diegorusso diegorusso commented Apr 28, 2026

@diegorusso
Copy link
Copy Markdown
Contributor Author

Performance wise, I didn't see any hit. Numbers are in the noise.

@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented Apr 28, 2026

Documentation build overview

📚 cpython-previews | 🛠️ Build #32512327 | 📁 Comparing 5cd2e87 against main (2b6a137)

  🔍 Preview build  

41 files changed · ± 41 modified

± Modified

@diegorusso diegorusso force-pushed the jit-backtrace-support branch from abc1072 to db967dc Compare May 2, 2026 17:59
@diegorusso
Copy link
Copy Markdown
Contributor Author

Apologies.. I had to force push.

Comment thread Include/internal/pycore_jit_unwind.h Outdated
Comment thread Include/internal/pycore_jit_unwind.h Outdated

#if defined(_Py_JIT) && defined(__linux__) && defined(__ELF__)
# define PY_HAVE_JIT_GDB_UNWIND
# if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This needs a separate configure link probe. HAVE_BACKTRACE only tells us that backtrace() is available, but this code also calls libgcc-specific symbols: __register_frame and __deregister_frame.

For example, Linux builds with clang -rtlib=compiler-rt can have execinfo.h / backtrace() while those frame registration symbols are not linkable. In that case thismacro gets enabled and the build fails later at link time.

Please add a configure check for __register_frame __deregister_frame and gate PY_HAVE_JIT_GNU_BACKTRACE_UNWIND on that result.

Copy link
Copy Markdown
Member

@pablogsal pablogsal May 2, 2026

Choose a reason for hiding this comment

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

For example this can break in:

  • Alpine / musl + libexecinfo (no libgcc_s __register_frame)
  • clang + compiler-rt without libgcc_s
  • LLVM libunwind builds (different FDE table format)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ok, it makes sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

link probe implemented here: ef9ea52

@support.requires_gil_enabled("test requires the GIL enabled")
@unittest.skipIf(support.is_wasi, "test not supported on WASI")
@unittest.skipUnless(sys.platform == "linux", "GNU backtrace unwinding test requires Linux")
class GnuBacktraceUnwindTests(unittest.TestCase):
Copy link
Copy Markdown
Member

@pablogsal pablogsal May 2, 2026

Choose a reason for hiding this comment

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

The new test only asserts python_frames > 0 and jit_frames > 0, which a stub unwinder would pass. Two things that would help:

  • Tighten to python_frames >= 10 (the recursion depth) so the count actually means something.
  • Add a test that JIT frames disappear from backtrace() after the executor is freed: that's the property the deregister code exists to guarantee and nothing covers it today.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed here: 01df239

* A NULL result is valid when publication succeeded only through backends
* with no unregister step, such as perf map output.
*/
_PyJitCodeRegistration *_PyJit_RegisterCode(const void *code_addr,
Copy link
Copy Markdown
Member

@pablogsal pablogsal May 2, 2026

Choose a reason for hiding this comment

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

_PyJit_RegisterCode returns NULL for three different reasons (perf-only success, calloc failure, all backends failed) and the caller can't tell them apart. Could you rename registered -> any_registered and add a one-liner near the perf branch noting it's intentionally not counted? The deleted comment from jit_record_code about partial-failure being non-fatal would also be nice to restore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed here: cf16da0

Comment thread Python/jit_publish.c
#if defined(PY_HAVE_JIT_GDB_UNWIND) \
|| defined(PY_HAVE_JIT_GNU_BACKTRACE_UNWIND)
struct _PyJitCodeRegistration {
# if defined(PY_HAVE_JIT_GDB_UNWIND)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Do we really need to guard every field of the struct?

@diegorusso diegorusso requested a review from corona10 as a code owner May 3, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants