-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix VS issue with unhandled exception on secondary threads #103425
Fix VS issue with unhandled exception on secondary threads #103425
Conversation
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 dotnet#103385
@janvorli I don't have a comment on the correctness of this fix, as I'm not familiar with the debugger interaction here, but I see that just a few lines before this fix is a TODO-NewEH comment about |
@davidwrighton I still need to investigate whether it is needed to do anything here. Thank you for noticing this / reminding me of this. None of the tests have ever hit a problem due to that and neither have customers reported any issues related to this. But it obviously doesn't mean it is fine as is, as a problem can be lurking waiting for someone to trigger it. |
Thanks @davidwrighton for the comment again. The finalizer case actually has similar problem as the one fixed by this PR, so I am going to add a fix to this PR too. |
I think it should be fine. runtime/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/__Finalizer.cs Lines 55 to 67 in f8f4509
I would get rid of the x86 special path as well. |
src/coreclr/vm/exceptionhandling.cpp
Outdated
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())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can DebuggerU2MCatchHandlerFrame
be in the middle of the stack, with a bunch more managed frame behind it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have believed when I've searched some time ago that the only place where it is used is on top of secondary threads. But I have searched for it again to be 100% sure now and I have actually missed on in DispatchInfo::InvokeMember
. So it seems that both this check and a similar one in the NotifyExceptionPassStarted
should also check if that frame is also the topmost one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas I have added check for the DebuggerU2MCatchHandlerFrame being the topmost one.
@jkotas I have added some instrumentation into the |
I think it is acceptable regression. We can open a tracking issue to make the perf better by porting the naot scheme for finalizers. |
Actually, it is a nice cleanup. I have just done it: #103501 |
We need to check that it is also the topmost frame
src/coreclr/vm/exceptionhandling.cpp
Outdated
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't propagate managed exceptions through the COMToCLRDispatchHelper
, they are caught and transformed into HRESULT, so after the FastCallFinalize was removed by another PR, this comment is now obsolete.
…on-secondary-thread
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