Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 5, 2026

Description

Proposes declarative debug data for stubs to simplify debugger stepping. Current approach requires each StubManager to implement bespoke prediction logic that must understand stub internals and can fail in restricted contexts (locks, GC no-trigger regions).

Changes

  • Design document at docs/design/coreclr/stub-target-debug-data-design.md

    • Surveys 11 existing StubManager types (ThePreStubManager, ILStubManager, VirtualCallStubManager, etc.)
    • Documents 3 stub generation mechanisms: ILStubs (IL-based), StubLinker (assembly), VSD (dynamic dispatch)
    • Categorizes stubs by feasibility: high (StubLinker delegates, precodes), moderate (VSD, prestub), low (multicast delegates)
  • Proposed solution: Store instruction offset of branch/call to target

    • Hybrid approach: Extend Stub class for StubLinker stubs, leverage JIT debug info for ILStubs
    • New UniversalStubManager reads debug data and sets breakpoint at recorded instruction
    • 5-phase implementation plan (weeks 1-10)
  • Open questions (13 total):

    • VSD performance impact on hot path
    • Handling multiple call sites per stub
    • Cross-platform debug data format
    • DAC marshaling and out-of-process debugger API

Example of proposed Stub class extension:

class Stub {
    enum { DEBUG_DATA_BIT = 0x08000000 };
    
    bool HasTargetDebugData() const {
        return (m_numCodeBytesAndFlags & DEBUG_DATA_BIT) != 0;
    }
    
    uint32_t GetTargetCallOffset() const {
        uint32_t* pData = (uint32_t*)(GetBlob() + GetNumCodeBytes());
        return *pData;
    }
};

Ready for team review and feedback before prototype implementation.

Original prompt

This section details on the original issue you should resolve

<issue_title>Add stub target debug data</issue_title>
<issue_description>### Background

In the runtime we have a variety of 'stubs' which tend to be little adapter methods that do some setup work and then branch/call to some other target method. Some of these code bodies are generated from IL using ILStubs or TransientIL. Others are generated directly in assembly code using the StubLinker. When a developer is doing managed debugging and stepping through code they may need to traverse some of these stubs. Unlike normal managed methods where we'd show the debugger user the code after each step and let them decide whether to step in/over/out stubs are considered runtime implementation details that we want to step through automatically as if they weren't present. For example C# source code might show that method A calls method B but in the generated assembly method A calls Stub X and Stub X calls B. When a user has a debugging session stopped on the source line where A is about to call B and steps in we want the debugger to automatically step through Stub X and arrive at B before breaking in the debugger once again.

In order to implement this automatic stub stepping behavior we have a set of components called StubManagers that recognize different kinds of stubs and predict the code path they will take when executed. This allows the debugger to step through a stub repeatedly asking for a prediction, setting a breakpoint at the predicted location, and then executing until the breakpoint is hit. Eventually one of the predictions will be the address of a non-stub method where the stepping operation can complete.

The challenge

These stub managers need particularized understanding of each type of stub in order to recognize them and anticipate their future execution behavior. There is a complexity and maintainance cost to keep them in sync with the stub code generators. The logic that runs within the StubManagers to predict forward execution can also be non-trivial which is a problem if the breakpoints occur inside locks or in GC no-trigger regions where the logic may not be legal to run. Last we'd like to make progress towards making stepping (and all execution control) more compatible with out-of-process debuggers that would benefit from being able to follow simple declarative stepping patterns.

A potential solution to explore

Rather than have each StubManager implement bespoke stub-prediction logic we'd instead like to store a piece of debug data for each stub that records the address of the stub instruction which branches/calls to the target method. Then a single general purpose StubManager could recognize all stubs which implement this pattern. Prediction becomes easy, the StubManager reads the debug data and predicts execution will go to the recorded address. Once execution reaches that address the debugger can single-step to either arrive at the final method or arrive at another stub where the process repeats.

At this point the proposal is just a general idea that needs to be explored to determine how many of our stubs could accomodate this pattern and how difficult is it to generate the debug data.</issue_description>

<agent_instructions>As a first step I want you to survey the existing stubs, consider options for how the debug data could be generated + stored, and write your findings in an initial draft of a design document. If you have questions please include them in the design document as well. We will iterate on a design before writing the code.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: noahfalk <6243776+noahfalk@users.noreply.github.com>
Copilot AI changed the title [WIP] Add stub target debug data for managed debugging Add design document for stub target debug data Feb 5, 2026
Copilot AI requested a review from noahfalk February 5, 2026 10:01
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 6, 2026
@noahfalk
Copy link
Member

noahfalk commented Feb 7, 2026

@jkotas - I iterated a little offline with copilot. The design is still incomplete/rough but I think it points in a better direction than the original one did. Getting to a final design that we'd want to implement still seems like more work is required, especially since doing a nice job on the inlining increases the complexity. What I'd like to do is just bank the progress thus far and put this on the shelf until we have a more urgent need to move it forward. I acknowledge right at the top of the doc that the design isn't done to try and avoid anyone else trying to run with it as-is.


## 1. Executive Summary

This document proposes a rough design for adding debug data to runtime stubs, with an initial focus on stubs compiled from IL. The goal is to enable a simplified stub stepping mechanism in the debugger, reducing complexity and maintenance burden of the current bespoke StubManager prediction logic, while supporting future inlining scenarios where stubs can be inlined into user methods or other stubs.
Copy link
Member

Choose a reason for hiding this comment

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

where stubs can be inlined into user methods

And vice versa - user code inlined into stubs?

Copy link
Member

Choose a reason for hiding this comment

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

It could be done. At the moment to doc describes control flow from stub to user code using a call instruction that needs to be located. As long as every method in the inlining tree has debug data to annotate 'stub' or 'not stub' + the corresponding native code ranges and parent methods then the debugger should be able model the right stepping behavior even when there is no call instruction.

@noahfalk noahfalk marked this pull request as ready for review February 10, 2026 04:29
Copilot AI review requested due to automatic review settings February 10, 2026 04:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a CoreCLR design document proposing “stub target debug data” as a declarative mechanism to simplify debugger stepping through runtime stubs, aiming to reduce per-stub bespoke prediction logic in StubManager implementations.

Changes:

  • Introduces a draft design describing stub target callsite annotation and a proposed compact debug-info chunk (StubDebugInfo).
  • Surveys existing stub generation mechanisms and current StubManager implementations, and outlines feasibility by stub type.
  • Documents a proposed IL-offset-to-native-offset resolution approach during debug info compression.


When serializing debug info, the IL offsets recorded by stub generators must be resolved to the native call-instruction offsets already emitted by the JIT.

**Proposed resolution point:** `CompressDebugInfo::Compress` in [src/coreclr/vm/debuginfostore.cpp](src/coreclr/vm/debuginfostore.cpp).
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The markdown link to debuginfostore.cpp is relative to this document’s directory, so it currently points to a non-existent path (it would resolve under docs/design/coreclr/...). Use a repo-root absolute link (e.g. /src/...) or fix the relative path so it resolves correctly on GitHub.

Suggested change
**Proposed resolution point:** `CompressDebugInfo::Compress` in [src/coreclr/vm/debuginfostore.cpp](src/coreclr/vm/debuginfostore.cpp).
**Proposed resolution point:** `CompressDebugInfo::Compress` in [src/coreclr/vm/debuginfostore.cpp](/src/coreclr/vm/debuginfostore.cpp).

Copilot uses AI. Check for mistakes.
Comment on lines +318 to +319
- [Virtual Stub Dispatch Design](../botr/virtual-stub-dispatch.md)
- [Book of the Runtime (BOTR)](../botr/README.md)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The BOTR reference links use ../botr/..., but this document already lives under docs/design/coreclr/, so the BOTR folder is ./botr/ (or botr/). As written, these links likely won’t resolve.

Suggested change
- [Virtual Stub Dispatch Design](../botr/virtual-stub-dispatch.md)
- [Book of the Runtime (BOTR)](../botr/README.md)
- [Virtual Stub Dispatch Design](botr/virtual-stub-dispatch.md)
- [Book of the Runtime (BOTR)](botr/README.md)

Copilot uses AI. Check for mistakes.
Comment on lines +249 to +253
| Stub Type | Generation | Manager | Call Pattern | Feasibility |
|-----------|-----------|---------|--------------|-------------|
| PreStub | Assembly | ThePreStubManager | Call to init | Moderate |
| Precode | Assembly | PrecodeStubManager | Jump to target | High |
| Delegate Invoke | StubLinker | StubLinkStubManager | Call to target | High |
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Appendix B’s table uses double leading/trailing pipe markers (|| ... ||), which doesn’t render as a standard Markdown table in most viewers. Switch to the conventional single-pipe table syntax so the summary displays correctly.

Copilot uses AI. Check for mistakes.
At a high level, the stub stepping flow becomes:
1. At stub code generation time extra debug data is produced and stored in a similar manner as our existing method debug data.
2. At debugging time the stepping code encounters an IP that might belong to one of these stubs.
3. The new StubManager looks up the debug data corresponding that IP. The StubManager claims the stub if the appropriate debug data exists and the debug data indicates the IP is inside the range of a stub. For non-inlined cases the range is the whole method.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Grammar: “corresponding that IP” should be “corresponding to that IP” (or similar).

Suggested change
3. The new StubManager looks up the debug data corresponding that IP. The StubManager claims the stub if the appropriate debug data exists and the debug data indicates the IP is inside the range of a stub. For non-inlined cases the range is the whole method.
3. The new StubManager looks up the debug data corresponding to that IP. The StubManager claims the stub if the appropriate debug data exists and the debug data indicates the IP is inside the range of a stub. For non-inlined cases the range is the whole method.

Copilot uses AI. Check for mistakes.

This document proposes a rough design for adding debug data to runtime stubs, with an initial focus on stubs compiled from IL. The goal is to enable a simplified stub stepping mechanism in the debugger, reducing complexity and maintenance burden of the current bespoke StubManager prediction logic, while supporting future inlining scenarios where stubs can be inlined into user methods or other stubs.

This is an incomplete sketch I drafted quickly with copilot. Nothing here has been implemented in the runtime. I wanted to save the work-in-progress assuming that it will be useful to resume at some point, but it isn't urgent to implement right now and doing a good job on the design needs additional time. Folks are welcome to comment on the PR with suggestions but I'm not planning to spend much time revising the doc further for now. Instead what I'd like to do is check it in as-is with the expectation that anyone who resumes this work in the future should review the PR comments before moving forward with it.
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Product name capitalization: “copilot” should be “Copilot”.

Suggested change
This is an incomplete sketch I drafted quickly with copilot. Nothing here has been implemented in the runtime. I wanted to save the work-in-progress assuming that it will be useful to resume at some point, but it isn't urgent to implement right now and doing a good job on the design needs additional time. Folks are welcome to comment on the PR with suggestions but I'm not planning to spend much time revising the doc further for now. Instead what I'd like to do is check it in as-is with the expectation that anyone who resumes this work in the future should review the PR comments before moving forward with it.
This is an incomplete sketch I drafted quickly with Copilot. Nothing here has been implemented in the runtime. I wanted to save the work-in-progress assuming that it will be useful to resume at some point, but it isn't urgent to implement right now and doing a good job on the design needs additional time. Folks are welcome to comment on the PR with suggestions but I'm not planning to spend much time revising the doc further for now. Instead what I'd like to do is check it in as-is with the expectation that anyone who resumes this work in the future should review the PR comments before moving forward with it.

Copilot uses AI. Check for mistakes.
@jkotas
Copy link
Member

jkotas commented Feb 10, 2026

marked this pull request as ready for review

FWIW, I do not think we have any uncommitted draft designs like this checked in into main. They typically live as an issue or draft PR. Earlier example: #97084

@noahfalk
Copy link
Member

FWIW, I do not think we have any uncommitted draft designs like this checked in into main. They typically live as an issue or draft PR. Earlier example: #97084

If you think this type of half-baked doc creates too much noise in our docs (lets say a trend of such docs, not just one in isolation) I'd also be fine closing the PR and letting the PR be the record.

@noahfalk
Copy link
Member

I'll just close it.

@noahfalk noahfalk closed this Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add stub target debug data

3 participants