Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Port NativeAOT exception handling to CoreCLR - #88034

Merged
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2
Aug 24, 2023
Merged

Port NativeAOT exception handling to CoreCLR#88034
janvorli merged 5 commits into
dotnet:mainfrom
janvorli:port-nativeaot-eh-to-coreclr-final-2

Conversation

@janvorli

Copy link
Copy Markdown
Member

This change ports NativeAOT exception handling to CoreCLR, resulting in 3.5..4 times speedup in exception handling. Due to time constraints and various complexities, thread abort and debugger support is not completed yet, so this change is enabled only when DOTNET_EnableNewExceptionHandling env variable is set. By default, the old way is used.
This change supports all OSes and targets we support except of x86 Windows. That may be doable too in the future, but the difference in exception handling on x86 Windows adds quite a lot of complexity into the picture.

Notes for the PR:

  • I have left the ExceptionHandling.cs and StackFrameIterator.cs in the nativeaot folder to simplify the review. I can move it to some common location after the change is reviewed. Also it was not clear to me where that should be, so advise would be welcome here.
  • Naming of the native helpers like RhpCallCatchFunclet was left the same as in the NativeAOT for now.
  • There are still some little things I'd like to eventually clean up, like ExInfo encapsulation and possibly moving REGDISPLAY and CONTEXT it uses into the ExInfo itself or moving debug members of StackFrameIterator and REGDISPLAY to the end of those structures so that the AsmOffsets.cs can be simplified. It also may be possible to unify the exception handling callback that's used for ObjectiveC to use the managed version. I've tried and there were some ugly complications, so I've left it separated.
  • There are two bug fixes for bugs unrelated to this PR and a removal of unused parameter in existing code that could be made as separate PRs before this PR.
    • ProfilerEnter and ProfilerLeave for the case of UnmanagedCallersOnly method were being called in preemptive mode.
    • NativeAOT code for rethrowing exception was incorrectly calling DispatchEx with last argument set to activeExInfo._idxCurClause to start at the last clause processed when the rethrown exception was originally thrown instead of starting from the first one again. I have a accidentally came with a simple test that discovered this bug and causes failures in the original NativeAOT too.
  • Changes in the stackwalk.cpp add support for
    • Usage of ExInfo instead of ExceptionTracker
    • Handling of case when GC runs while finally funclet is on the stack and then again when the code is back in the new exception handling code in managed code before other finally or catch funclet is called. The NativeAOT solves that by disabling GC for the 2nd pass of EH, for this change it would not be reasonable.
    • Handling the GC reporting when funclet is found while walking the stack. It needs to scan frames of the managed code that handles the exception too, since it contains live references. The old EH way doesn't have this case.
  • I needed to add GCFrame::Remove method that can remove the GCFrame from any location in the chain. There is a managed runtime method that calls GCReporting::Unregister that was popping it with my changes out of order due to the exception handling code being managed.

Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
Comment threadsrc/coreclr/vm/frames.h Outdated
Comment threadsrc/coreclr/debug/daccess/dacdbiimplstackwalk.cpp Outdated
Comment threadsrc/coreclr/jit/codegencommon.cpp Outdated
Comment threadsrc/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Outdated
@danmoseley

Copy link
Copy Markdown
Contributor

How do the perf improvements compare between OS?

In a typical workload, would we expect this to only be noticeable in "exception storms" (e.g. due loss of connectivity)?

@jkotas

Copy link
Copy Markdown
Member

How do the perf improvements compare between OS?

#77568 (comment)

@janvorli

janvorli commented Jun 28, 2023

Copy link
Copy Markdown
MemberAuthor

I have found (by running a separate testing PR (#88113) just for the NativeAOT fix for rethrowing, that that fix is not correct in all cases. So clearly the last argument of the DispatchEx needs to be sometimes activeExInfo._idxCurClause and sometimes MaxTryRegionIdx. After debugging the issue that lead me to change this, I was convinced that it should always be MaxTryRegionIdx, which means to start at the first clause. So I'll need to debug the case failing in the PR to see why it is not the case here.
For the current PR, I am going to revert this change. The failure I have seen (exception going unhandled) was not happening in any of the tests we have, I was just lucky to create a test myself that was failing without the change for both NativeAOT and the new EH.

The test I have created is as simple as this. The rethrown exception is unhandled. When I comment out the throw; and uncomment the `throw new ArgumentException("aE");', it works.

classProgram{staticvoidMain(string[]args){try{thrownewException("boo");}catch(Exceptionex2){Console.WriteLine($"1: {ex2}");try{throw;//throw new ArgumentException("aE");}catch(Exceptionex3){Console.WriteLine($"2: {ex3}");}}}}
When running result of dotnet publish -c Release -p:PublishAot=true
1: System.Exception: boo
at Program.Main(String[]) + 0x4c
Unhandled Exception: System.Exception: boo
at Program.Main(String[]) + 0x151
at failingnativeaot!<BaseAddress>+0x16bc2b

@janvorli

Copy link
Copy Markdown
MemberAuthor

Fortunately, I have found what was causing the NativeAOT test suite to fail with my rethrow fix. It was just a problem of stack trace, the actual fix is correct.

@janvorli

Copy link
Copy Markdown
MemberAuthor

I have created a couple of PRs to separate bug fixes and cleanups unrelated to this PR.

Comment threadsrc/coreclr/vm/amd64/cgencpu.h Outdated
Comment threadsrc/coreclr/vm/eetwain.cpp Outdated
Comment threadsrc/coreclr/vm/frames.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.h Outdated
Comment threadsrc/coreclr/vm/exceptmacros.h Outdated
Comment threadsrc/coreclr/vm/exinfo.cpp Outdated
Comment threadsrc/coreclr/vm/proftoeeinterfaceimpl.cpp Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from a96c6a9 to 57a7e78CompareJune 28, 2023 21:36
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from 57a7e78 to 6db013dCompareJune 29, 2023 07:30
Comment threadsrc/coreclr/vm/fcall.h Outdated
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c77f189 to ee3f998CompareJuly 26, 2023 21:05
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this TODO about?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it be a TODO-NewEH

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I still plan to add a number of TODO-NewEHs to this PR. As for this TODO, the comment was related to the fact that the ExceptionTracker::MakeCallbacksRelatedToHandler, which is the old EH variant of this function, calls m_EHClauseInfo.ResetInfo() and IIRC, profiler stuff somehow relied on more stuff from the m_EHClauseInfo info than the stuff I've extracted from it and use in the new EH.

This change ports NativeAOT exception handling to CoreCLR, resulting in
3.5..4 times speedup in exception handling. Due to time constraints and
various complexities, thread abort and debugger support is not
completed yet, so this change is enabled only when
`DOTNET_EnableNewExceptionHandling` env variable is set. By default,
the old way is used.
This change supports all OSes and targets we support except of x86
Windows. That may be doable too in the future, but the difference in
exception handling on x86 Windows adds quite a lot of complexity into
the picture.
Notes for the PR:
* I have left the `ExceptionHandling.cs` and `StackFrameIterator.cs` in
the nativeaot folder to simplify the review. I can move it to some
common location after the change is reviewed. Also it was not clear to
me where that should be, so advise would be welcome here.
* Naming of the native helpers like `RhpCallCatchFunclet` was left the
same as in the NativeAOT for now.
* There are still some little things I'd like to eventually clean up,
like `ExInfo` encapsulation and possibly moving `REGDISPLAY` and
`CONTEXT` it uses into the `ExInfo` itself or moving debug members of
`StackFrameIterator` and `REGDISPLAY` to the end of those structures
so that the `AsmOffsets.cs` can be simplified. It also may be possible
to unify the exception handling callback that's used for ObjectiveC to
use the managed version. I've tried and there were some ugly
complications, so I've left it separated.
* There are two bug fixes for bugs unrelated to this PR and a removal of
unused parameter in existing code that could be made as separate PRs
before this PR.
* `ProfilerEnter` and `ProfilerLeave` for the case of
`UnmanagedCallersOnly` method were being called in preemptive mode.
* NativeAOT code for rethrowing exception was incorrectly calling
`DispatchEx` with last argument set to `activeExInfo._idxCurClause`
to start at the last clause processed when the rethrown exception
was originally thrown instead of starting from the first one again.
I have a accidentally came with a simple test that discovered this
bug and causes failures in the original NativeAOT too.
* Changes in the stackwalk.cpp add support for
* Usage of `ExInfo` instead of `ExceptionTracker`
* Handling of case when GC runs while finally funclet is on the stack
and then again when the code is back in the new exception handling
code in managed code before other finally or catch funclet is
called. The NativeAOT solves that by disabling GC for the 2nd pass
of EH, for this change it would not be reasonable.
* Handling the GC reporting when funclet is found while walking the
stack. It needs to scan frames of the managed code that handles the
exception too, since it contains live references. The old EH way
doesn't have this case.
* I needed to add `GCFrame::Remove` method that can remove the `GCFrame`
from any location in the chain. There is a managed runtime method that
calls `GCReporting::Unregister` that was popping it with my changes
out of order due to the exception handling code being managed.
Fix context initialization after rebase
The `UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` in the
`EE_TO_JIT_TRANSITION` needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.
This change adds parameter to the
`UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE` macro to select whether
to rethrow the exception as native or to invoke the new managed
exception handling.
This problem didn't show up until I ran the coreclr tests with tiered
compilation disabled.
@janvorli
janvorliforce-pushed the port-nativeaot-eh-to-coreclr-final-2 branch from c3824e1 to 28f603bCompareAugust 22, 2023 13:06
@janvorli

Copy link
Copy Markdown
MemberAuthor

@jkotas I believe I have addressed all of the feedback. Can you please take a look to see if you have any other comments or if it can be merged? The CI failures are unrelated.

@jkotas

Copy link
Copy Markdown
Member

The UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE in the
EE_TO_JIT_TRANSITION needs to rethrow an exception (if any) using native
exception handling mechanism instead of calling the new managed
exception handling, as the native exception needs to propagate through
some native code layers from there.

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too. For example, this one: https://github.com/dotnet/runtime/blob/main/src/coreclr/vm/excep.cpp#L6055-L6057

@janvorli

Copy link
Copy Markdown
MemberAuthor

Is JIT/EE interface really the only place that needs this change? I went over all UNINSTALL_UNWIND_AND_CONTINUE_HANDLER occurences and it seems that some of them need this fix too

That's a good point, I'll review the usage. The place that you've mentioned definitely needs it.

There were three places where the UNINSTALL_UNWIND_AND_CONTINUE_HANDLER
needed to be replaced by
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_NO_PROBE(true).
Comment threadsrc/coreclr/vm/excep.cpp Outdated
To INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX, as the old name is obsolete
@janvorli
janvorli merged commit f8d9b3c into dotnet:mainAug 24, 2023
@janvorli
janvorli deleted the port-nativeaot-eh-to-coreclr-final-2 branch August 24, 2023 20:50
LuckyXu-HF added a commit to LuckyXu-HF/runtime that referenced this pull request Aug 25, 2023
@ghostghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-ExceptionHandling-coreclronly use for closed issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@janvorli@danmoseley@jkotas@AustinWise@davidwrighton@MichalStrehovsky@AaronRobinsonMSFT