From f7813448423a02f143384381d15aa39a8a79a205 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 13 Jun 2024 17:51:44 +0200 Subject: [PATCH 1/2] Fix VS issue with unhandled exception on secondary threads When "just my code" is disabled, unhandled exceptions on secondary threads cause the VS to stop without any stack trace shown. This is similar to the recently fixed problem that was happening with user unhandled exception treatment. The exception is rethrown as native exception after leaving the last managed frame and it propagates to the `ManagedThreadBase_DispatchOuter` where it is caught. The debugger gets notified from there, but all of the managed stack frames are gone at that point, so the debugger cannot show them. The fix is to report exception on a secondary thread as unhandled earlier, right in the SfiNext where we report it for the primary threads. The secondary thread is different in having the DebuggerU2MCatchHandlerFrame on stack while in the primary thread case, there is no explicit frame. Close #103385 --- src/coreclr/vm/exceptionhandling.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index c7cc10a94de743..9a0fc79247fa80 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -8544,7 +8544,9 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla _ASSERTE(retVal != SWA_FAILED); _ASSERTE(pThis->GetFrameState() != StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION); - if (pThis->m_crawl.GetFrame() == FRAME_TOP) + pFrame = pThis->m_crawl.GetFrame(); + // Check if there are any further managed frames on the stack, if not, the exception is unhandled. + if ((pFrame == FRAME_TOP) || (pFrame->GetVTablePtr() == DebuggerU2MCatchHandlerFrame::GetMethodFrameVPtr())) { if (pTopExInfo->m_passNumber == 1) { From ecc2f8dda39015c6833dea6746a1fc6688a7f255 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Sat, 15 Jun 2024 14:31:26 +0200 Subject: [PATCH 2/2] Modify the check for DebuggerU2MCatchHandlerFrame We need to check that it is also the topmost frame --- src/coreclr/vm/exceptionhandling.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 9a0fc79247fa80..a0a1eae44842a0 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -8213,6 +8213,11 @@ void FailFastIfCorruptingStateException(ExInfo *pExInfo) } } +static bool IsTopmostDebuggerU2MCatchHandlerFrame(Frame *pFrame) +{ + return (pFrame->GetVTablePtr() == DebuggerU2MCatchHandlerFrame::GetMethodFrameVPtr()) && (pFrame->PtrNextFrame() == FRAME_TOP); +} + static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThread, ExInfo *pExInfo) { if (pExInfo->m_passNumber == 1) @@ -8289,7 +8294,7 @@ static void NotifyExceptionPassStarted(StackFrameIterator *pThis, Thread *pThrea pFrame = pFrame->PtrNextFrame(); _ASSERTE(pFrame != FRAME_TOP); } - if ((pFrame->GetVTablePtr() == FuncEvalFrame::GetMethodFrameVPtr()) || (pFrame->GetVTablePtr() == DebuggerU2MCatchHandlerFrame::GetMethodFrameVPtr())) + if ((pFrame->GetVTablePtr() == FuncEvalFrame::GetMethodFrameVPtr()) || IsTopmostDebuggerU2MCatchHandlerFrame(pFrame)) { EEToDebuggerExceptionInterfaceWrapper::NotifyOfCHFFilter((EXCEPTION_POINTERS *)&pExInfo->m_ptrs, pFrame); } @@ -8518,9 +8523,6 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla } else { - // TODO-NewEH: Currently there are two other cases of internal VM->managed transitions. The FastCallFinalize and COMToCLRDispatchHelperWithStack - // Either add handling those here as well or rewrite all these perf critical places in C#, so that CallDescrWorker is the only path that - // needs to be handled here. size_t CallDescrWorkerInternalReturnAddress = (size_t)CallDescrWorkerInternal + CallDescrWorkerInternalReturnAddressOffset; if (GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext) == CallDescrWorkerInternalReturnAddress) { @@ -8546,7 +8548,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla pFrame = pThis->m_crawl.GetFrame(); // Check if there are any further managed frames on the stack, if not, the exception is unhandled. - if ((pFrame == FRAME_TOP) || (pFrame->GetVTablePtr() == DebuggerU2MCatchHandlerFrame::GetMethodFrameVPtr())) + if ((pFrame == FRAME_TOP) || IsTopmostDebuggerU2MCatchHandlerFrame(pFrame)) { if (pTopExInfo->m_passNumber == 1) {