Fix x86 runtime async frame pointer mismatch in GetSpForDiagnosticReporting#126717
Fix x86 runtime async frame pointer mismatch in GetSpForDiagnosticReporting#126717tommcdon wants to merge 3 commits intodotnet:mainfrom
Conversation
…orting Adjust GetSpForDiagnosticReporting to correctly handle runtime async variant method stack layout on x86. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull request overview
Adjusts the stack pointer reported to the debugger on x86 for runtime “async call” frames so that ICorDebugManagedCallback2::Exception callbacks (notably DEBUG_EXCEPTION_CATCH_HANDLER_FOUND) can resolve a non-null ICorDebugFrame.
Changes:
- Extends
GetSpForDiagnosticReportingto optionally accept aMethodDesc*and apply an extra x86 adjustment for runtime async methods (IsAsyncMethod()). - Updates exception/debugger callback sites to pass the current
MethodDesc*intoGetSpForDiagnosticReporting.
Comments suppressed due to low confidence (1)
src/coreclr/vm/exceptionhandling.cpp:2968
pMDis only referenced underESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP+TARGET_X86. In other builds (e.g., x64 Unix where-Wallis enabled, often with-Werror), this new parameter can become unused and may trigger an unused-parameter warning. Consider addingUNREFERENCED_PARAMETER(pMD);in the#elsepath and/or in the non-x86 path to keep all configurations warning-free.
static TADDR GetSpForDiagnosticReporting(REGDISPLAY *pRD, MethodDesc *pMD = NULL)
{
#ifdef ESTABLISHER_FRAME_ADDRESS_IS_CALLER_SP
TADDR sp = CallerStackFrame::FromRegDisplay(pRD).SP;
#if defined(TARGET_X86)
sp -= sizeof(TADDR);
// On x86, runtime async methods have stack parameters that cause CallerSP
// to sit above the parameter area. The DBI uses PCTAddr as the frame
// pointer, which is at the return address (below the parameters).
// Subtract an extra sizeof(TADDR) to account for the stack parameter.
if (pMD != NULL && pMD->IsAsyncMethod())
{
sp -= sizeof(TADDR);
}
#endif
return sp;
#else
return GetSP(pRD->pCurrentContext);
#endif
src/coreclr/vm/exceptionhandling.cpp
Outdated
| // On x86, runtime async methods have stack parameters that cause CallerSP | ||
| // to sit above the parameter area. The DBI uses PCTAddr as the frame | ||
| // pointer, which is at the return address (below the parameters). | ||
| // Subtract an extra sizeof(TADDR) to account for the stack parameter. | ||
| if (pMD != NULL && pMD->IsAsyncMethod()) | ||
| { | ||
| sp -= sizeof(TADDR); | ||
| } |
There was a problem hiding this comment.
Is this because of the continuation? Or what exactly is the difference here?
There was a problem hiding this comment.
I am confused here because the continuation is not the only possible extra argument we can have -- we can also have generic context (and vararg cookie), but those do not seem to require handling here.
I am wondering if we instead need a fix on the JIT side in the GC information. Perhaps something like
runtime/src/coreclr/vm/gc_unwind_x86.inl
Lines 1902 to 1923 in a181406
is not working properly for the continuation.
There was a problem hiding this comment.
Is this because of the continuation? Or what exactly is the difference here?
Correct - this is due to the continuation parameter in the async calling convention being passed on the stack
I am wondering if we instead need a fix on the JIT side in the GC information
Good suggestion- taking a look
There was a problem hiding this comment.
Correct - this is due to the continuation parameter in the async calling convention being passed on the stack
The continuation parameter is not always passed on the stack. It is just a regular argument that follows the regular calling convention, which may or may not put it on the stack.
There was a problem hiding this comment.
yeah, I would hope that this can be addressed as part of GC or unwind information attached to the method rather than custom post-processing for async frames. I'd worry that any discrepancy we try to address here means we've got a leaky abstraction and this probably isn't the only place it would be leaking.
There was a problem hiding this comment.
The GC and unwind info looks correct. Since exception processing simply uses the context chain's stack pointer, it doesn't take into account of any stack arguments passed to the method. Fixing it on the DBI side is straightforward as we can offset the pointer by the parameter stack size.
|
@tommcdon I wonder - was this always a problem? I am asking since there was a change in the ifdef in this code in January (#122833) from I wonder if the ifdef really meant to be for Linux x86 only (which was previously the only one with defined(FEATURE_EH_FUNCLETS)) and was unrelated to the funclets per se. |
…osticReporting" This reverts commit 909edec.
On x86, the runtime sends CallerSP - sizeof(TADDR) as the frame pointer for exception notifications (see GetSpForDiagnosticReporting). Since this does not account for the stack parameter size, the DBI's IsContainedInFrame now queries the DAC for stackParamSize and adjusts the frame pointer before matching. Also switches FindFrame to use TARGET_X86 instead of HOST_64BIT for the ifdef guard, using exact FramePointer comparison on all non-x86 platforms. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
For certain runtime async frames this resulted in the
ICorDebugManagedCallback2::Exceptionto return a nullICorDebugFrameforDEBUG_EXCEPTION_CATCH_HANDLER_FOUNDnotifications. The fix addresses this by adjustingGetSpForDiagnosticReportingto account for runtime async variant method stack layout differences on x86.