Skip to content

Add CPython 3.15 support to mixed-mode debugging - #1

Closed
rchiodo wants to merge 7 commits into
mainfrom
python-315-debug-offsets
Closed

Add CPython 3.15 support to mixed-mode debugging#1
rchiodo wants to merge 7 commits into
mainfrom
python-315-debug-offsets

Conversation

@rchiodo

@rchiodo rchiodo commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary

Adds CPython 3.15 support to the mixed-mode (Python/Native) debugger, building on the 3.14 work in microsoft#8586. It makes the _Py_DebugOffsets reader version-aware so it can parse both the 3.14 and 3.15 offset-table layouts, and adds the V315 language version so the 3.14+ code paths (_PyStackRef masking, in-process line-number decoding) fire for 3.15.

Background

CPython 3.15 keeps the self-describing _Py_DebugOffsets table introduced/formalized in 3.14, but the flat layout of the table grew, which breaks a parser that assumes the fixed 3.14 layout:

  • thread_state gained 7 fields (3 inserted after current_frame: base_frame, last_profiled_frame, last_profiled_frame_seq; 4 appended after status: holds_gil, gil_requested, current_exception, exc_state) — 9 → 16 fields.
  • A new err_stackitem group (exc_value) is inserted between thread_state and interpreter_frame.
  • type_object gained tp_basicsize / tp_dictoffset; a new heap_type_object group (size, ht_cached_keys) follows it.
  • unicode_object gained compactunicodeobject_size; gc gained frame, generation_stats_size, generation_stats.

Because groups after thread_state shift, the flat offsets of interpreter_frame / code_object in the table differ between 3.14 (table size 760) and 3.15 (table size 888). The parser must select the layout by version.

The interpreter-frame field offsets, _PyStackRef decode (low-bit tag mask), and the co_linetable format are unchanged in production 3.15 builds, so the existing 3.14+ masking and line-number code applies to 3.15 as-is.

What this does

Version-aware _Py_DebugOffsets reader:

  • PyDebugOffsets now parses the header/version first, then selects the matching layout (Layout314, Layout315) built from shared field arrays plus version-specific arrays, and reads that layout's table size. Unknown versions are rejected.
  • Is314 is joined by Is315, and callers gate on the new IsSupported (3.14 or 3.15) instead of Is314. TableSizeFor(major, minor) replaces the single static table size; TryRead reads the larger MaxTableSize.

Language-version wiring (required):

  • PythonLanguageVersion.V315 = 0x030f and GetPythonLanguageVersion handles "315". Without this, LanguageVersion would be None, the >= V314 localsplus / f_executable stackref masks would never fire, and Debug.Assert(version != None) would trip — producing empty locals / broken frames exactly like pre-3.14.

No hot-path map change:

  • DebugOffsetsFieldProvider needs no mapping change: every hot-path struct/field it reads (_PyInterpreterFrame, PyCodeObject, _ts) exists in both layouts; only its doc comment was refreshed.

Files

  • Proxies/Structs/PyDebugOffsets.cs — version-aware layout (Layout314 760B / Layout315 888B), GetLayout, Is315, IsSupported, TableSizeFor, version-selecting TryParse, TryRead reads MaxTableSize.
  • PythonRuntimeInfo.cscase "315"V315; StructFieldOffsetProvider gates on IsSupported.
  • Common/Parsing/PythonLanguageVersion.csV315 = 0x030f.
  • Proxies/Structs/DebugOffsetsFieldProvider.cs — doc comment only (map unchanged).
  • Tests/DebuggerTests/PyDebugOffsetsTests.cs, PyDebugOffsetsProviderTests.csTableSizeFor; synthetic 3.15 structural vector with ordinals derived independently from the CPython 3.15 header; 3.15 provider test; unknown-version rejection.

Testing

  • Unit tests pass (18/18 parser + provider tests, including the new 3.15 structural/provider tests and unknown-version rejection).

Notes

  • The 3.15 tests currently use a synthetic (structurally correct) offset vector. Adding a real recorded 3.15 _Py_DebugOffsets vector (standard and free-threaded 3.15t), captured in-process the same way as the 3.14 vectors, is a planned follow-up once broader end-to-end 3.15 validation is done.
  • As with 3.14, only the mixed-mode hot path is driven by the table; struct sizes and all non-hot-path fields still come from the PDB. Free-threaded (3.15t) layout shifts are covered by the table automatically.

StellaHuang95 and others added 7 commits August 5, 2026 10:19
* Restore Pylance document synchronization on Dev18

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f5679231-48fb-4e6f-ba92-12102f084440

* Address initialize response review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f5679231-48fb-4e6f-ba92-12102f084440

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f5679231-48fb-4e6f-ba92-12102f084440
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: f5679231-48fb-4e6f-ba92-12102f084440
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: f5679231-48fb-4e6f-ba92-12102f084440
…ze) (microsoft#8606)

The Debug property page hosts a launcher-options UserControl swapped in at runtime (DefaultPythonLauncherOptions for standard projects, PythonWebLauncherOptions for Web/Django/Flask). Fix microsoft#8567 applied the VS environment font only to the property-page frames via ThemeAwareUserControl, so these launcher controls still rendered at the fixed design-time font and did not resize with the OS Text size setting (MAS 1.4.4 Resize Text). This was visible on Flask projects (taller PythonWebLauncherOptions) but not standard projects.

Override OnHandleCreated in both launcher controls to call VsShellFontHelper.ApplyEnvironmentFont(this). Their existing AutoSize layout plus the Debug page's AutoScroll then reflow and scroll the enlarged content. Addresses work item 3026378.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: d8aa13d4-f66e-4a25-bb23-a57474a24bd3
* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14121 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=10082099

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14121 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=10088788

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=23949 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=10297120

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14121 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=10594459

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14121 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=11698099

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=23949 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=11754798

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14121 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=14066892

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=23949 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=14068750

* Updated for https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build?definitionId=14164 by using baselines generated in https://dev.azure.com/devdiv/0bdbc590-a062-4c3f-b0f6-9383f67865ee/_build/results?buildId=14871965

---------

Co-authored-by: microsoft-github-policy-service[bot] <77245923+microsoft-github-policy-service[bot]@users.noreply.github.com>
Extends the mixed-mode debugger's CPython 3.14 support to 3.15. The 3.15
_Py_DebugOffsets table grew (thread_state gained profiling/GIL/exception
fields, a new err_stackitem and heap_type_object group were inserted, and
type_object/unicode_object/gc gained fields), which shifts the byte positions
of the frame and code-object groups the mixed-mode stack walk relies on. A
3.14-only flat parser would misread a 3.15 table, so the reader is now
version-aware.

- PythonLanguageVersion: add V315 (0x030f).
- PythonRuntimeInfo: map python315(_d).dll -> V315; gate the debug-offsets
  field provider on IsSupported (3.14 or 3.15) instead of Is314.
- PyDebugOffsets: parse the version prefix first, then select the matching
  ordered layout (3.14 vs 3.15) and consume that layout's table size. Unknown
  versions now return false so callers fall back to the PDB. Replace the static
  TableSize with TableSizeFor(major, minor).
- DebugOffsetsFieldProvider: unchanged map (every hot-path group/field it reads
  exists in both layouts); refreshed doc.

The existing '>= V314' stackref masks and in-process line-number path already
fire for V315, and every version-gated struct proxy uses open-ended MinVersion
gates, so 3.15 is covered without further changes.

Tests: version-aware TableSizeFor; synthetic 3.15 structural tests asserting
each group/field lands at the ordinal the CPython 3.15 header dictates; a 3.15
provider test; unknown-version rejection. A real recorded 3.15 vector will be
added once a 3.15 build is available.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@rchiodo

rchiodo commented Aug 13, 2026

Copy link
Copy Markdown
Owner Author

Superseded by microsoft#8611 (this one accidentally targeted the fork's own main instead of upstream microsoft/PTVS).

@rchiodo rchiodo closed this Aug 13, 2026
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