Skip to content

[clr-interp] Add initial CoreCLR interpreter func-eval support#126576

Merged
kotlarmilos merged 12 commits intodotnet:mainfrom
matouskozak:interpreter-debugger-funceval
Apr 29, 2026
Merged

[clr-interp] Add initial CoreCLR interpreter func-eval support#126576
kotlarmilos merged 12 commits intodotnet:mainfrom
matouskozak:interpreter-debugger-funceval

Conversation

@matouskozak
Copy link
Copy Markdown
Member

@matouskozak matouskozak commented Apr 6, 2026

Description

This adds initial func-eval support for threads stopped in CoreCLR interpreter code.

  • Queue interpreter func-eval requests in the existing pending evals hash table (ProcessAnyPendingEvals) instead of using native-context hijacking
  • Execute pending evals from InterpBreakpoint after the debugger callback returns, reusing the shared dispatch path for both interpreter and exception-time evals
  • Skip native-only setup for interpreter evals: executable breakpoint segment allocation, SP alignment validation, and register/context updates that rely on real native frames
  • Rename m_evalDuringException to m_evalUsesHijack (inverted logic) to accurately describe the flag's purpose now that it covers both exception and interpreter paths
  • Move Init() skip logic inside DebuggerEval so callers don't need interpreter-specific conditionals
  • Return CORDBG_E_FUNC_EVAL_BAD_START_POINT for func-eval requests on interpreter threads not stopped at a breakpoint
  • Reuse the direct completion path for interpreter evals, where completion is signaled without the native breakpoint trap mechanism

Testing

  • Locally on iOS simulator
  • DiagnosticTests: Func-eval tests passing under --clrinterpreter (osx-arm64, validated locally)
    • FuncEval.NestedFuncEvalTest
    • FuncEval.EscapeSecondPass

FuncEval.InfiniteLoopASyncAbortTest is Windows-only and FuncEval.AssemblyLoadFuncEval is a Mono configuration.

Fixes #125959

@matouskozak matouskozak self-assigned this Apr 6, 2026
Copilot AI review requested due to automatic review settings April 6, 2026 12:14
Copy link
Copy Markdown
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 initial debugger func-eval support when the target thread is stopped in CoreCLR interpreter code, by avoiding native-context hijacking and instead running queued evals from the interpreter breakpoint path.

Changes:

  • Adds a pending func-eval slot to the interpreter thread context and triggers execution after the breakpoint debugger callback returns.
  • Extends DebugInterface/Debugger with an interpreter-specific hook to execute pending evals.
  • Adjusts FuncEvalSetup / DebuggerEval initialization to skip native-only setup (e.g., executable breakpoint segment allocation, SP alignment checks) for interpreter evals.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/coreclr/vm/interpexec.h Adds InterpThreadContext::m_pPendingFuncEval storage for interpreter-thread func-evals.
src/coreclr/vm/interpexec.cpp Executes pending interpreter func-evals from the INTOP_BREAKPOINT handler using a synthetic filter context.
src/coreclr/vm/dbginterface.h Adds ExecutePendingInterpreterFuncEval(Thread*) to the debug interface under FEATURE_INTERPRETER.
src/coreclr/debug/ee/debugger.h Declares the Debugger implementation of ExecutePendingInterpreterFuncEval.
src/coreclr/debug/ee/debugger.cpp Implements pending-eval execution and updates FuncEvalSetup/DebuggerEval to support interpreter eval flow.

Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/vm/interpexec.cpp Outdated
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag
See info in area-owners.md if you want to be subscribed.

Comment thread src/coreclr/vm/interpexec.h Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Copilot AI review requested due to automatic review settings April 15, 2026 16:09
…r-funceval

# Conflicts:
#	src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/DebuggerEval.cs
Copy link
Copy Markdown
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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread src/coreclr/vm/interpexec.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
@kotlarmilos kotlarmilos marked this pull request as ready for review April 15, 2026 16:20
…ec.h include

- Restore m_bypassAddress/m_bypassOpcode in EX_CATCH before EX_RETHROW so
  bypass state is not corrupted if func-eval throws
- Remove unused interpexec.h include from debugger.cpp (GetInterpThreadContext
  is declared on Thread, not in interpexec.h)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/coreclr/vm/interpexec.cpp Outdated
Copilot AI review requested due to automatic review settings April 22, 2026 09:32
Copy link
Copy Markdown
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

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/vm/interpexec.cpp
Copy link
Copy Markdown
Member

@noahfalk noahfalk left a comment

Choose a reason for hiding this comment

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

Looks pretty good. I think there are a few places where a little more refactoring can simplify even further and potentially an issue about SetIP interacting with breakpoint skipping.

Comment thread src/coreclr/vm/interpexec.cpp Outdated
Comment thread src/coreclr/vm/interpexec.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
Comment thread src/coreclr/debug/ee/debugger.cpp Outdated
@kotlarmilos kotlarmilos requested a review from noahfalk April 27, 2026 13:24
kotlarmilos added a commit to kotlarmilos/runtime that referenced this pull request Apr 27, 2026
Includes the func-eval support from dotnet#126576 plus follow-up fixes:
- Skip CALL_FINALLY instructions during stepping (internal EH machinery)
- Filter CallEntryPoint frames from interpreter debugger stack walks (in-process and DAC)
- Add IsCallFinally() to InterpreterWalker

Co-authored-by: Matous Kozak <55735845+matouskozak@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Member

@noahfalk noahfalk left a comment

Choose a reason for hiding this comment

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

LGTM!

@am11
Copy link
Copy Markdown
Member

am11 commented Apr 28, 2026

DiagnosticTests: Func-eval tests passing under --clrinterpreter (osx-arm64, validated locally)

* FuncEval.NestedFuncEvalTest

* FuncEval.EscapeSecondPa

Are these local tests? Asking as I'd like to run them against #126809 (your and mine PR combined can be tested too; it has wasm changes in src/coreclr/vm/wasm/callhelpers-reverse.cpp and no merge-conflict is expected AFAICT).

@janvorli
Copy link
Copy Markdown
Member

Are these local tests?

@am11 These are the private diagnostic tests that I was running with your PR

janvorli added a commit that referenced this pull request Apr 28, 2026
When debugger breakpoint is hit and then debugger resumes the execution,
the context at which the breakpoint was hit can be modified. For
example, one of the exception interception tests does that to continue
from a different location after an exception is hit. The interpreter
ignored changes in the context and resumed execution at the original
breakpoint location instead of the modified context.

This change fixes it by throwing the `ResumeAfterCatchException` if the
context was modified. That ensures that we resume the exception
correctly even if both the stack frame and the IP was changed. The
`ResumeAfterCatchException` became a bit of a misnomer, but I don't want
to pollute this PR by renaming the `ResumeAfterCatchException` to
`ResumeException`, which would be a better name now. I'd prefer doing it
in a separate PR.

Follow-up to #126576

This fixes the following interpreter debugger test failure:

* Exceptions.InterceptTest
Copilot AI review requested due to automatic review settings April 28, 2026 13:39
@kotlarmilos
Copy link
Copy Markdown
Member

DiagnosticTests: Func-eval tests passing under --clrinterpreter (osx-arm64, validated locally)

* FuncEval.NestedFuncEvalTest

* FuncEval.EscapeSecondPa

Are these local tests? Asking as I'd like to run them against #126809 (your and mine PR combined can be tested too; it has wasm changes in src/coreclr/vm/wasm/callhelpers-reverse.cpp and no merge-conflict is expected AFAICT).

Merged latest main into the branch and resolved the conflicts. I verified locally that on osx-arm64 under --clrinterpreter: FuncEval.NestedFuncEvalTest, FuncEval.EscapeSecondPass, and Exceptions.InterceptTest all pass.

Copy link
Copy Markdown
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.

Copilot's findings

Comments suppressed due to low confidence (1)

src/coreclr/debug/ee/debugger.cpp:14266

  • If bpInfoSegmentRX was allocated for a hijacked eval and new DebuggerEval(...) returns NULL, this returns E_OUTOFMEMORY without freeing bpInfoSegmentRX, leaking executable-heap memory. Consider keeping the heap pointer and freeing bpInfoSegmentRX on this failure path (and any other early-return paths before pDE takes ownership).
    DebuggerEval *pDE = new (interopsafe, nothrow) DebuggerEval(filterContext, pEvalInfo, bpInfoSegmentRX);

    if (pDE == NULL)
    {
        return E_OUTOFMEMORY;
  • Files reviewed: 9/9 changed files
  • Comments generated: 2

Comment thread src/coreclr/debug/ee/debugger.cpp
Comment thread src/coreclr/debug/ee/debugger.cpp
@jkoritzinsky jkoritzinsky removed their request for review April 28, 2026 20:50
@kotlarmilos kotlarmilos merged commit 25c2c30 into dotnet:main Apr 29, 2026
110 of 120 checks passed
kotlarmilos added a commit to kotlarmilos/runtime that referenced this pull request Apr 29, 2026
- Skip INTOP_CALL_FINALLY during stepping (internal EH machinery)
- Add IsCallFinally() helper to InterpreterWalker
- Filter Environment.CallEntryPoint frames from debugger stack walks (frameinfo.cpp)

Follow-up to dotnet#126576.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kotlarmilos added a commit to kotlarmilos/runtime that referenced this pull request Apr 29, 2026
- Skip INTOP_CALL_FINALLY during stepping (internal EH machinery)
- Add IsCallFinally() helper to InterpreterWalker
- Filter Environment.CallEntryPoint frames from debugger stack walks (frameinfo.cpp)

Follow-up to dotnet#126576.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

[clr-ios] FuncEval does not work on iOS because executable memory allocation fails

9 participants