From 44b2b2fe29f9223ef99f0f4bb843311372cfb2a0 Mon Sep 17 00:00:00 2001 From: Max Charlamb <44248479+max-charlamb@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:35:52 -0500 Subject: [PATCH] ignore async thunks when iterating the async continuation chain --- src/coreclr/debug/daccess/dacdbiimpl.cpp | 20 +++--- src/coreclr/debug/daccess/dacdbiimpl.h | 4 +- src/coreclr/debug/di/rsstackwalk.cpp | 84 ++++++++++++++---------- src/coreclr/debug/di/rsthread.cpp | 42 ++++++------ src/coreclr/debug/inc/dacdbiinterface.h | 10 +-- 5 files changed, 88 insertions(+), 72 deletions(-) diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 48a62a3c2b5b24..facd30daac6bdf 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -5288,8 +5288,8 @@ GENERICS_TYPE_TOKEN DacDbiInterfaceImpl::ResolveExactGenericArgsToken(DWORD ThrowHR(CORDBG_E_TARGET_INCONSISTENT); } -// Check if the given method is an IL stub or an LCD method. -IDacDbiInterface::DynamicMethodType DacDbiInterfaceImpl::IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) +// Check if the given method is a DiagnosticHidden or an LCG method. +IDacDbiInterface::DynamicMethodType DacDbiInterfaceImpl::IsDiagnosticsHiddenOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) { DD_ENTER_MAY_THROW; @@ -5297,7 +5297,7 @@ IDacDbiInterface::DynamicMethodType DacDbiInterfaceImpl::IsILStubOrLCGMethod(VMP if (pMD->IsDiagnosticsHidden()) { - return kILStub; + return kDiagnosticHidden; } else if (pMD->IsLCGMethod()) { @@ -7261,7 +7261,7 @@ HRESULT DacDbiInterfaceImpl::AreOptimizationsDisabled(VMPTR_Module vmModule, mdM { return E_INVALIDARG; } -#ifdef FEATURE_REJIT +#ifdef FEATURE_REJIT { CodeVersionManager * pCodeVersionManager = pModule->GetCodeVersionManager(); ILCodeVersion activeILVersion = pCodeVersionManager->GetActiveILCodeVersion(pModule, methodTk); @@ -7451,12 +7451,12 @@ void DacDbiInterfaceImpl::GetAsyncLocals( ULONG32 cAsyncVars = 0; BOOL success = DebugInfoManager::GetAsyncDebugInfo( - request, - DebugInfoStoreNew, - nullptr, - &asyncInfo, - &asyncSuspensionPoints, - &asyncVars, + request, + DebugInfoStoreNew, + nullptr, + &asyncInfo, + &asyncSuspensionPoints, + &asyncVars, &cAsyncVars); if (!success) return; diff --git a/src/coreclr/debug/daccess/dacdbiimpl.h b/src/coreclr/debug/daccess/dacdbiimpl.h index 797bdee2ade0bb..99de4ffd483414 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.h +++ b/src/coreclr/debug/daccess/dacdbiimpl.h @@ -915,8 +915,8 @@ class DacDbiInterfaceImpl : DebuggerREGDISPLAY * pOutDRD, BOOL fActive); - // Check if the given method is an IL stub or an LCD method. - DynamicMethodType IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc); + // Check if the given method is a DiagnosticHidden or an LCG method. + DynamicMethodType IsDiagnosticsHiddenOrLCGMethod(VMPTR_MethodDesc vmMethodDesc); // Return a TargetBuffer for the raw vararg signature. TargetBuffer GetVarArgSig(CORDB_ADDRESS VASigCookieAddr, diff --git a/src/coreclr/debug/di/rsstackwalk.cpp b/src/coreclr/debug/di/rsstackwalk.cpp index 5f4b71c8a7f3fc..79038742cddc74 100644 --- a/src/coreclr/debug/di/rsstackwalk.cpp +++ b/src/coreclr/debug/di/rsstackwalk.cpp @@ -879,46 +879,62 @@ HRESULT CordbAsyncStackWalk::PopulateFrame() if (m_pCurrentFrame != NULL) return S_OK; - // If we have reached the end of the stack, we can't populate any frame - if (m_continuationAddress == 0) - return CORDBG_E_PAST_END_OF_STACK; - HRESULT hr; IDacDbiInterface* pDac = m_pProcess->GetDAC(); - PCODE diagnosticIP; - CORDB_ADDRESS nextContinuation; - UINT32 state; - IfFailRet(pDac->ParseContinuation( - m_continuationAddress, - &diagnosticIP, - &nextContinuation, - &state)); - - NativeCodeFunctionData codeData; - VMPTR_Module pModule; - mdMethodDef methodDef; - pDac->GetNativeCodeInfoForAddr( - diagnosticIP, - &codeData, - &pModule, - &methodDef); - CordbAsyncFrame * frame = new CordbAsyncFrame( - GetProcess(), - pModule, - methodDef, - codeData.vmNativeCodeMethodDescToken, - codeData.m_rgCodeRegions[kHot].pAddress, - diagnosticIP, - m_continuationAddress, - state); - - IfFailRet(frame->Init()); - m_pCurrentFrame.Assign(frame); + while (true) + { + PCODE diagnosticIP; + CORDB_ADDRESS nextContinuation; + UINT32 state; + + // If we have reached the end of the stack, we can't populate any frame + if (m_continuationAddress == 0) + return CORDBG_E_PAST_END_OF_STACK; + + IfFailRet(pDac->ParseContinuation( + m_continuationAddress, + &diagnosticIP, + &nextContinuation, + &state)); + + NativeCodeFunctionData codeData; + VMPTR_Module pModule; + mdMethodDef methodDef; + pDac->GetNativeCodeInfoForAddr( + diagnosticIP, + &codeData, + &pModule, + &methodDef); + + IDacDbiInterface::DynamicMethodType dynMethodType = pDac->IsDiagnosticsHiddenOrLCGMethod(codeData.vmNativeCodeMethodDescToken); + if (dynMethodType == IDacDbiInterface::kDiagnosticHidden) + { + // Skipping async frame creation for async thunks. These can not be converted to a CordbAsyncFrame as they lack DebugInfo + // Async Thunks appear on the continuation chain when runtime async calls library async code through a thunk. + // This is acceptable to skip as these frames will be represented in the stack by the library async method frames. + m_continuationAddress = nextContinuation; + continue; + } + + CordbAsyncFrame * frame = new CordbAsyncFrame( + GetProcess(), + pModule, + methodDef, + codeData.vmNativeCodeMethodDescToken, + codeData.m_rgCodeRegions[kHot].pAddress, + diagnosticIP, + m_continuationAddress, + state); + + IfFailRet(frame->Init()); + m_pCurrentFrame.Assign(frame); + + break; + } return S_OK; } - HRESULT CordbAsyncStackWalk::GetContext(ULONG32 contextFlags, ULONG32 contextBufSize, ULONG32 * pContextSize, diff --git a/src/coreclr/debug/di/rsthread.cpp b/src/coreclr/debug/di/rsthread.cpp index aee966e01cdc8c..2c5b73a510b5c4 100644 --- a/src/coreclr/debug/di/rsthread.cpp +++ b/src/coreclr/debug/di/rsthread.cpp @@ -5221,7 +5221,7 @@ BOOL CordbInternalFrame::ConvertInternalFrameForILMethodWithoutMetadata( } // Retrieve the type of the method associated with the STUBFRAME_JIT_COMPILATION. - IDacDbiInterface::DynamicMethodType type = GetProcess()->GetDAC()->IsILStubOrLCGMethod(m_vmMethodDesc); + IDacDbiInterface::DynamicMethodType type = GetProcess()->GetDAC()->IsDiagnosticsHiddenOrLCGMethod(m_vmMethodDesc); // Here are the conversion rules: // 1) For a normal managed method, we don't convert, and we return FALSE. @@ -5231,7 +5231,7 @@ BOOL CordbInternalFrame::ConvertInternalFrameForILMethodWithoutMetadata( { return FALSE; } - else if (type == IDacDbiInterface::kILStub) + else if (type == IDacDbiInterface::kDiagnosticHidden) { return TRUE; } @@ -7300,7 +7300,7 @@ BOOL CordbNativeFrame::ConvertNativeFrameForILMethodWithoutMetadata( IDacDbiInterface * pDAC = GetProcess()->GetDAC(); IDacDbiInterface::DynamicMethodType type = - pDAC->IsILStubOrLCGMethod(GetNativeCode()->GetVMNativeCodeMethodDescToken()); + pDAC->IsDiagnosticsHiddenOrLCGMethod(GetNativeCode()->GetVMNativeCodeMethodDescToken()); // Here are the conversion rules: // 1) For a normal managed method, we don't convert, and we return FALSE. @@ -7310,7 +7310,7 @@ BOOL CordbNativeFrame::ConvertNativeFrameForILMethodWithoutMetadata( { return FALSE; } - else if (type == IDacDbiInterface::kILStub) + else if (type == IDacDbiInterface::kDiagnosticHidden) { return TRUE; } @@ -8523,23 +8523,23 @@ HRESULT CordbJITILFrame::EnumerateArguments(ICorDebugValueEnum **ppValueEnum) // Get arg count ULONG argCount; IfFailThrow(GetFunction()->GetSig(NULL, &argCount, NULL)); - + // Handle varargs if (m_fVarArgFnx && !m_sigParserCached.IsNull()) { argCount = m_allArgsCount; } - + // Create the lambda that captures 'this' CordbJITILFrame* pThis = this; ValueGetter getter = [pThis](DWORD index, ICorDebugValue** ppValue) -> HRESULT { return pThis->GetArgument(index, ppValue); }; - + RSInitHolder cdVE( new CordbValueEnum( - GetProcess(), - argCount, + GetProcess(), + argCount, getter, m_nativeFrame->m_pThread->GetRefreshStackNeuterList())); @@ -8901,18 +8901,18 @@ HRESULT CordbJITILFrame::EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugVa IfFailThrow(m_ilCode->GetLocalVarSig(NULL, &localsCount)); } #endif // FEATURE_CODE_VERSIONING - + // Capture both 'this' and the flags CordbJITILFrame* pThis = this; ILCodeKind codeKind = flags; ValueGetter getter = [pThis, codeKind](DWORD index, ICorDebugValue** ppValue) -> HRESULT { return pThis->GetLocalVariableEx(codeKind, index, ppValue); }; - + RSInitHolder cdVE( new CordbValueEnum( - GetProcess(), - localsCount, + GetProcess(), + localsCount, getter, m_nativeFrame->m_pThread->GetRefreshStackNeuterList())); @@ -11189,17 +11189,17 @@ HRESULT CordbAsyncFrame::EnumerateArguments(ICorDebugValueEnum **ppValueEnum) { ULONG argCount; IfFailThrow(m_pFunction->GetSig(NULL, &argCount, NULL)); - + // Capture 'this' in the lambda CordbAsyncFrame* pThis = this; ValueGetter getter = [pThis](DWORD index, ICorDebugValue** ppValue) -> HRESULT { return pThis->GetArgument(index, ppValue); }; - + RSInitHolder cdVE( new CordbValueEnum( - GetProcess(), - argCount, + GetProcess(), + argCount, getter, GetProcess()->GetContinueNeuterList())); @@ -11346,17 +11346,17 @@ HRESULT CordbAsyncFrame::EnumerateLocalVariablesEx(ILCodeKind flags, ICorDebugVa IfFailThrow(m_pILCode->GetLocalVarSig(NULL, &localsCount)); } #endif // FEATURE_CODE_VERSIONING - + CordbAsyncFrame* pThis = this; ILCodeKind codeKind = flags; ValueGetter getter = [pThis, codeKind](DWORD index, ICorDebugValue** ppValue) -> HRESULT { return pThis->GetLocalVariableEx(codeKind, index, ppValue); }; - + RSInitHolder cdVE( new CordbValueEnum( - GetProcess(), - localsCount, + GetProcess(), + localsCount, getter, GetProcess()->GetContinueNeuterList())); cdVE.TransferOwnershipExternal(ppValueEnum); diff --git a/src/coreclr/debug/inc/dacdbiinterface.h b/src/coreclr/debug/inc/dacdbiinterface.h index f6bc066bad798a..9cf4c4d6172dc1 100644 --- a/src/coreclr/debug/inc/dacdbiinterface.h +++ b/src/coreclr/debug/inc/dacdbiinterface.h @@ -1571,25 +1571,25 @@ class IDacDbiInterface typedef enum { kNone, - kILStub, + kDiagnosticHidden, kLCGMethod, } DynamicMethodType; // - // Check whether the specified method is an IL stub or an LCG method. This answer determines if we + // Check whether the specified method is a DiagnosticHidden or an LCG method. This answer determines if we // need to expose the method in a V2-style stackwalk. // // Arguments: // vmMethodDesc - the method to be checked // // Return Value: - // Return kNone if the method is neither an IL stub or an LCG method. - // Return kILStub if the method is an IL stub. + // Return kNone if the method is neither a DiagnosticHidden or an LCG method. + // Return kDiagnosticHidden if the method is a DiagnosticHidden method. // Return kLCGMethod if the method is an LCG method. // virtual - DynamicMethodType IsILStubOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) = 0; + DynamicMethodType IsDiagnosticsHiddenOrLCGMethod(VMPTR_MethodDesc vmMethodDesc) = 0; // // Return a TargetBuffer for the raw vararg signature.