[clr-ios] Fix JMC stepping and CallEntryPoint frame filtering#126954
[clr-ios] Fix JMC stepping and CallEntryPoint frame filtering#126954kotlarmilos wants to merge 10 commits intodotnet:mainfrom
Conversation
…ogic for clarity in DebuggerEval handling
…r-funceval # Conflicts: # src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/DebuggerEval.cs
…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>
- Skip CALL_FINALLY instructions during stepping (internal EH machinery) - Filter CallEntryPoint frames from debugger stack walks (in-process and DAC) - Add pre-callback bypass check to avoid redundant debugger notifications - Improve bypass save/restore with per-eval granularity and exception handling - Add IsCallFinally() to InterpreterWalker Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Fixes interpreter-specific debugging behaviors around JMC stepping, stack walking, and func-eval/breakpoint handling so that interpreter behavior matches JIT expectations and avoids redundant debugger callbacks.
Changes:
- Add interpreter-aware stepping logic to skip internal
INTOP_CALL_FINALLYboundaries during JMC stepping. - Filter
Environment.CallEntryPointfrom debugger stack walks (in-proc and DAC) to match JIT visibility behavior. - Refine interpreter breakpoint + func-eval handling (pre-callback bypass checks, bypass save/restore around func-eval, and allow SetIP to change interpreter IP).
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Frames/DebuggerEval.cs | Rename contract flag to EvalUsesHijack for interpreting func-eval behavior in stack walking. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/BaseFrameHandler.cs | Use EvalUsesHijack to decide whether to update context from func-eval frame. |
| src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/X86FrameHandler.cs | Same as BaseFrameHandler, with x86-specific context copying. |
| src/coreclr/vm/interpexec.cpp | Interpreter breakpoint handler updates (pre/post bypass checks, pending interpreter func-eval execution, SetIP support). |
| src/coreclr/vm/dbginterface.h | Add ExecutePendingInterpreterFuncEval hook for interpreter breakpoint handler. |
| src/coreclr/vm/datadescriptor/datadescriptor.inc | CDAC descriptor rename: EvalDuringException → EvalUsesHijack. |
| src/coreclr/debug/ee/interpreterwalker.h | Add InterpreterWalker::IsCallFinally() helper. |
| src/coreclr/debug/ee/interpreterwalker.cpp | Implement IsCallFinally(). |
| src/coreclr/debug/ee/funceval.cpp | Replace m_evalDuringException checks with m_evalUsesHijack. |
| src/coreclr/debug/ee/frameinfo.cpp | Filter Environment.CallEntryPoint in debugger stack walk for interpreter mode. |
| src/coreclr/debug/ee/debugger.h | Rename state flag and skip bp-segment executability check when not using hijack. |
| src/coreclr/debug/ee/debugger.cpp | Interpreter-aware func-eval setup (skip executable heap allocation for interpreter) and expose ExecutePendingInterpreterFuncEval. |
| src/coreclr/debug/ee/controller.cpp | Stepper logic to skip INTOP_CALL_FINALLY boundaries when stepping interpreted code. |
| src/coreclr/debug/daccess/dacdbiimplstackwalk.cpp | DAC stack walk filtering for Environment.CallEntryPoint under interpreter. |
Comments suppressed due to low confidence (1)
src/coreclr/debug/ee/debugger.cpp:14436
- In the interpreter func-eval path, bpInfoSegmentRX is NULL, but DebuggerEval::Init() is called before m_evalUsesHijack is flipped to false. Because the ctor currently initializes m_evalUsesHijack to
!fInException(true for non-exception interpreter evals), Init() will dereferencem_bpInfoSegmentand can crash. Setm_evalUsesHijackcorrectly before calling Init (e.g., initialize it in the ctor from bothfInExceptionand whetherbpInfoSegmentRXis non-null, or delay Init() until after the interpreter-specific flag update), and consider adding an assert/guard in Init to ensurem_bpInfoSegmentis non-null whenm_evalUsesHijackis true.
// Create a DebuggerEval to hold info about this eval while its in progress. Constructor copies the thread's
// CONTEXT.
DebuggerEval *pDE = new (interopsafe, nothrow) DebuggerEval(filterContext, pEvalInfo, fInException, bpInfoSegmentRX);
if (pDE == NULL)
{
return E_OUTOFMEMORY;
}
else if (!pDE->Init())
{
// We fail to change the m_breakpointInstruction field to PAGE_EXECUTE_READWRITE permission.
return E_FAIL;
}
|
Tagging subscribers to 'os-ios': @vitek-karas, @kotlarmilos, @steveisok, @akoeplinger |
|
@kotlarmilos would it make sense to drop the The comment "to match JIT behavior" suggests the JIT is expected to hide |
Description
Fix JMC stepping and debugger stack walk filtering for interpreter frames: skip
INTOP_CALL_FINALLYduring stepping (internal EH machinery), filterCallEntryPointfrom debugger stack walks in both the in-process and DAC frame iterators, add a pre-callback bypass check to avoid redundant debugger notifications, and improve bypass save/restore around func-eval execution.Follow-up to #126576
This fixes the following interpreter debugger test failures:
JMC.jmcClassesJMC.jmcFuncevalJMC.jmcChangeJMC.jmcDelegatesJMC.jmcModules