Skip to content

Add experimental debugger support for JIT-compiled C++ - #167

Open
conrade-ctc wants to merge 2 commits into
compiler-research:mainfrom
chicagotrading:upstreaming/jit-debugging
Open

conrade-ctc wants to merge 2 commits into
compiler-research:mainfrom
chicagotrading:upstreaming/jit-debugging

Conversation

@conrade-ctc

@conrade-ctc conrade-ctc commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

This adds experimental support to debug JIT-compiled C++ at the source level with gdb, from Python and from VS Code. It is off by default: only a build with -DCPPJIT_ENABLE_DEBUGGER_SUPPORT=ON has it. Without the option, the build has no new code, exported symbols or package files, and python/cppjit does not change.

In a build with the support, CPPJIT_DEBUGGER_SUPPORT=1 starts a debug session:

  • The JIT compiles with -g -O0. With Write interpreter inputs to files for debuggers (experimental) CppInterOp#1156, each C++ input goes to a file, so gdb shows the source and binds file:line breakpoints, also in headers that cppjit.include loads.
  • cppjit.debugger adds Python line breakpoints and steps through sys.monitoring.
  • A gdb extension (cppjit/debugger/gdb) shows the Python frames between the C++ frames, with the variables of both. It reads CPython's _Py_DebugOffsets, so CPython needs no debug information (3.13 or later).
  • examples/vscode-gdb uses it from VS Code through gdb's DAP server (gdb 16 or later). A small DAP proxy adds Python breakpoints, and steps between Python and C++.

Notes for review:

  • The C++ changes are behind CPPJIT_WITH_DEBUGGER_SUPPORT: a hook pair around each JIT call in WrapperCall, a few exported variables for the debugger, and the import of cppjit.debugger in the module init.
  • The gdb extension also reads a few layouts that the debug offsets do not give: frame globals, dict entries, and the CPPInstance fields. I think that is acceptable for an experimental feature, but a change of these layouts needs an update here.
  • The debugger CI job builds with the option and runs test/test_debugging.py with gdb. It fails if a test skips. The other cells skip the gdb tests, but run the two decoder tests from the source tree. Locally (Linux, gdb 16.3, CPython 3.13), all 21 tests pass with the option.
  • The second commit points the CppInterOp pin at Write interpreter inputs to files for debuggers (experimental) CppInterOp#1156 for CI. I will drop it before merge.
  • file:line breakpoints need the CppInterOp change and a pin that has it. Breakpoints by function name work without it.

🤖 Done with the help of Claude Code (Opus 5.5, human in the loop)

@aaronj0

aaronj0 commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

Nice! So that your changes are tested on the CI, can you add a commit like cc7edf7 pointing to your CppInterOp remote and branch? That would stack your changes and any iterations on compiler-research/CppInterOp#1156 will automatically reflect here

@Vipul-Cariappa
Vipul-Cariappa self-requested a review October 23, 2026 10:43

@Vipul-Cariappa Vipul-Cariappa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I have only looked through some of the READMEs. I wanted to know why GDB? I believe LLDB provides better API support than GDB. With GDB, you are working with pipes and parsing the output as though you are running it in a shell.

@@ -0,0 +1,26 @@
"""gdb extension for processes that run cppjit.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can we have type hints for all the Python files in python/cppjit-debugger folder?
Will make it a bit easier to maintain and review the code.

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.

Done: all the files in python/cppjit-debugger have type hints now, and mypy finds no issues. The files use from __future__ import annotations, because gdb can embed an older Python.

@conrade-ctc
conrade-ctc force-pushed the upstreaming/jit-debugging branch 2 times, most recently from c0b4377 to 4031ac2 Compare October 23, 2026 13:09
@conrade-ctc

conrade-ctc commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the review! The extension does not use pipes. gdb embeds Python, so the API calls run in the gdb process: a frame filter shows the Python frames, and the commands and pretty printers read the memory of the inferior. One place parses text, the output of info types, to find the C++ type of a proxy. VS Code talks to gdb's built-in DAP server (gdb 16 and later) through stdin and stdout, with JSON messages. Only the tests run gdb in batch mode and read its output.

I chose gdb because it is the default debugger on Linux, and it has the JIT interface and DAP built in. I agree that LLDB has a good API. The CPython decoder (gdb/cpython.py) needs only a function that reads memory, so I think an LLDB front end can use it too. Maybe we can do that as a follow up.

@conrade-ctc
conrade-ctc force-pushed the upstreaming/jit-debugging branch from 4031ac2 to 4b14d6c Compare October 23, 2026 13:32
Emery Conrad added 2 commits October 23, 2026 14:17
The support is off by default. Build with
-DCPPJIT_ENABLE_DEBUGGER_SUPPORT=ON to get it. Without the option, the
build has no new code, symbols or files.

In a build with the support, CPPJIT_DEBUGGER_SUPPORT=1 starts a debug
session:

- The JIT compiles with -g -O0. CppInterOp writes each C++ input to a
  file in CPPINTEROP_JIT_SOURCE_DIR, so gdb shows the source and binds
  FILE:LINE breakpoints.
- cppjit.debugger copies in-memory Python source to files, and adds
  Python line breakpoints and steps through sys.monitoring.
- A gdb extension shows the Python frames between the C++ frames, with
  the variables of both. It reads CPython's _Py_DebugOffsets, so CPython
  needs no debug information.
- examples/vscode-gdb uses it from VS Code through gdb's DAP server. A
  DAP proxy adds Python breakpoints and steps between Python and C++.

Co-developed-with-the-help-of: Claude Code (Opus 5.5, human in the loop)
Co-developed-with-the-help-of: Claude Code (Opus 5.5, human in the loop)
@conrade-ctc
conrade-ctc force-pushed the upstreaming/jit-debugging branch from 4b14d6c to 4e894a9 Compare October 23, 2026 19:19

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants