-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Move coreclr EH second pass to native code #119863
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
base: main
Are you sure you want to change the base?
Conversation
There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.
The current state is that it almost works. A catch handler is correctly invoked, but then something crashes. |
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.
Pull Request Overview
This PR moves the CoreCLR exception handling second pass from managed to native code to fix GC holes that can occur when GC is triggered between finally handlers and catch handlers during the second pass of exception handling.
- Splits existing
RhThrowEx
/RhThrowHwEx
methods into separate first-pass handler finding methods and second-pass execution - Moves the second pass execution logic (
DispatchExPass2
) from managed to native code - Removes QCALL exports for
CallCatchFunclet
andCallFinallyFunclet
as they're now called directly from native code
Reviewed Changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
File | Description |
---|---|
src/coreclr/vm/qcallentrypoints.cpp |
Removes QCALL exports for CallCatchFunclet and CallFinallyFunclet |
src/coreclr/vm/metasig.h |
Updates method signatures to include handlingFrameSP and pCatchHandler out parameters |
src/coreclr/vm/exinfo.h |
Adds ContainsCodeOffset helper method to RhEHClause |
src/coreclr/vm/exceptionhandlingqcalls.h |
Removes QCALL declarations for catch/finally funclets |
src/coreclr/vm/exceptionhandling.h |
Adds declaration for new native DispatchExPass2 function |
src/coreclr/vm/exceptionhandling.cpp |
Major refactoring: converts QCALL functions to native, adds new DispatchExPass2 implementation |
src/coreclr/vm/excep.cpp |
Updates managed fault handling to use new method signatures |
src/coreclr/vm/corelib.h |
Updates method name mappings for exception handling entry points |
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs |
Splits exception handling into separate first/second pass methods |
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs
Outdated
Show resolved
Hide resolved
Also, please note that the collided unwind detection is not working yet, as I cannot rely on the pinvokes from the managed EH code anymore. I am working on a fix. |
And one more thing not done yet is rethrow. |
* Reflect PR feedback * Implement rethrow * Implement new way of collided unwind detection now that the CallCatchFunclet is not called via pinvoke * Remove forced reporting of EH code from stack frame iterator, as we now cannot have that code on the stack during 2nd pass
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs
Show resolved
Hide resolved
I am considering reducing the number of changes in the exceptionhandling.cs by a slightly different approach - keeping only the methods we had and adding an extra argument "firstPassOnly" to them. Then if that argument is set to true, in DispatchEx store the handlingFrameSP/PC, _pReversePInvokePropagationCallback/Context and the pCatchHandler into the ExInfo and return; The current state is that, with some additional changes that I am going to commit after I fix the offsets in StackFrameIterator for some targets, it passes all diagnostics tests and coreclr tests (except ForeignThreadExceptionTest where it ends up messing GC mode when calling managed callback from native code catch) I also need to add proper contracts here and there. |
I would add some correlation comments so that we know that C++ function X is supposed to be the same as C# function Y. But otherwise, this actually looks good to me (assuming it works of course) |
@jkotas I've added some contracts. I also had to change contract on the |
There is one issue left that I am investigating - Linux arm64 failures in several libraries tests. |
/azp run runtime-coreclr outerloop |
Azure Pipelines successfully started running 1 pipeline(s). |
/azp run runtime-coreclr gcstress0x3-gcstress0xc |
Azure Pipelines successfully started running 1 pipeline(s). |
This is done to ensure that no GC is allowed between the scanned stack range is extended and a funclet for the current frame is called.
@jkotas, @davidwrighton the current state of this PR is final from my point of view. Can you please review it? |
|
||
#if TARGET_ARM64 | ||
[FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_handlingFramePC)] | ||
internal volatile byte* _handlingFramePC; |
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.
Should we have a comment somewhere that explains what's special about Arm64 that it needs the handlingFramePC ?
|
||
extern "C" CLR_BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause* pEHClause) | ||
// The doNotCalculateCatchType option makes the function skip calculation of the catch type. It is used in | ||
// the 2nd path of EH to avoid possible GC stemming from a call to ResolveEHClause. |
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.
nit: path -> pass?
MethodDesc *pMD = exInfo->m_frameIter.m_crawl.GetFunction(); | ||
// Profiler, debugger and ETW events | ||
TADDR spForDebugger = GetSpForDiagnosticReporting(pvRegDisplay); | ||
exInfo->MakeCallbacksRelatedToHandler(true, pThread, pMD, &exInfo->m_CurrentClause, (DWORD_PTR)pHandlerIP, spForDebugger); |
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 don't understand how your are protecting the callbacks here against triggering GCs. AFAIK BEGINFORBIDGC and ENDFORBIDGC are debug only macros. They have no effect in retail builds.
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.
The callbacks that are called here don't trigger GC, there is a subset of profiler callbacks that are documented here https://github.com/dotnet/runtime/blob/fc1dee66fac8ae088d01d07b4a2faaeef15a234a/docs/design/coreclr/botr/profiling.md#gc-safe-callouts
that runtime calls without switching to preemptive mode.
There were some GC holes discovered caused by the fact that GC can be triggered during 2nd pass of EH in-between calls to finally handlers and catch handler. After considering options, moving the 2nd pass to native code seems the most reasonable solution.