Skip to content

Notify attached debugger of NAOT exception #115678

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 27, 2025
Merged

Conversation

agocke
Copy link
Member

@agocke agocke commented May 17, 2025

Native AOT does not use C++ or SEH exceptions, meaning that debuggers do not recognize the exception throw/catch sequence and cannot break on first-chance exceptions. On Windows, we can use OS functionality to throw and catch an SEH exception when a Native AOT exception is thrown and a debugger is attached, effectively acting as a notification to debuggers that an exception is in flight. The exception code used here is the same one as CoreCLR, so the WinDbg command sxe clr would also be sufficient to catch Native AOT exceptions.

Fixes #115514

Native AOT does not use C++ or SEH exceptions, meaning that debuggers do
not recognize the exception throw/catch sequence and cannot break on
first-chance exceptions. On Windows, we can use OS functionality to
throw and catch an SEH exception when a Native AOT exception is thrown
and a debugger is attached, effectively acting as a notification to
debuggers that an exception is in flight. The exception code used here
is the same one as CoreCLR, so the WinDbg command `sxe clr` would also
be sufficient to catch Native AOT exceptions.

Fixes dotnet#115514
@Copilot Copilot AI review requested due to automatic review settings May 17, 2025 03:53
@agocke agocke requested a review from MichalStrehovsky as a code owner May 17, 2025 03:53
@@ -1557,19 +1557,21 @@ BOOL HandleHardwareException(PAL_SEHException* ex)

void FirstChanceExceptionNotification()
{
#ifndef TARGET_UNIX
#ifdef TARGET_WINDOWS
// Thow an SEH exception and immediately catch it. This is used to notify debuggers and other tools
Copy link
Member Author

Choose a reason for hiding this comment

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

I copied this code back over to keep the two copies identical.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a notification mechanism for attached debuggers when a Native AOT exception is thrown by throwing and catching an SEH exception on Windows.

  • Replace PAL_TRY/PAL_EXCEPT with __try/__except for Windows-specific exception notifications.
  • Introduce RhpFirstChanceException in EHHelpers.cpp and expose it in InternalCalls.cs and ExceptionHandling.cs.
  • Add exception code definitions in corexcep.h to ensure consistency with CoreCLR.

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/coreclr/vm/exceptionhandling.cpp Updated exception notification logic for Windows.
src/coreclr/nativeaot/Runtime/corexcep.h Added exception code definitions used by the CLR.
src/coreclr/nativeaot/Runtime/EHHelpers.cpp Introduced RhpFirstChanceException function with Windows-specific logic.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/InternalCalls.cs Exposed the new RhpFirstChanceException for internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs Trigger debugger notification before throwing exceptions on Windows.

Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

@@ -592,7 +592,7 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord,
// The 3rd argument passes to PopExplicitFrame is normally the parent SP to correctly handle InlinedCallFrame embbeded
// in parent managed frame. But at this point there are no further managed frames are on the stack, so we can pass NULL.
// Also don't pop the GC frames, their destructor will pop them as the exception propagates.
// NOTE: this needs to be popped in the 2nd pass to ensure that crash dumps and Watson get the dump with these still
// NOTE: this needs to be popped in the 2nd pass to ensure that crash dumps and Watson get the dump with these still
Copy link
Member Author

@agocke agocke May 17, 2025

Choose a reason for hiding this comment

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

My editor stripped trailing whitespace from the file. Feel free to look at this diff with ws-only changes disabled.

agocke and others added 2 commits May 16, 2025 20:55
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

Thanks!

@MichalStrehovsky
Copy link
Member

Regular CoreCLR does this notification from SfiInit - the native AOT equivalent would be RhpSfiInit. Admittedly, SfiInit is an odd place but it leads to a couple differences - rethrows also trigger this notification, and so do hardware exceptions. Do we want this for those? It feels like at least rethrow should trigger the notification.

@agocke
Copy link
Member Author

agocke commented May 19, 2025

For hardware exceptions, will this interfere with the native debuggers handling for those? That is, if someone already sets break-on-exception, will they break twice?

@MichalStrehovsky
Copy link
Member

For hardware exceptions, will this interfere with the native debuggers handling for those? That is, if someone already sets break-on-exception, will they break twice?

Native debuggers offer granularity for which exceptions to break on. By default access violation tends to be enabled by default (VS for sure, I think WinDBG too), so if we raise another exception it would break twice. On the other hand division by zero tends to not be enabled (again, VS for sure, I just checked), so if we don't raise it then sxe clr would probably not break for those.

I don't have a strong opinion on the HW exceptions, my comment was mostly about the rethrows - if we're fixing this, we might as well fix it fully.

Cc @tommcdon for opinion.

@tommcdon
Copy link
Member

I don't have a strong opinion on the HW exceptions, my comment was mostly about the rethrows - if we're fixing this, we might as well fix it fully.

Cc @tommcdon for opinion.

I am viewing this as incremental progress towards a better debug experience with native debuggers, as the goal was to enable the debugger to stop on managed first chance exceptions without the need of setting a bp on RhpThrowEx2 (per https://learn.microsoft.com/dotnet/core/deploying/native-aot/diagnostics#native-debugging). Perhaps we should create a user story with a checklist of the remaining work to provide a full experience, and we can address those in future PR's.

@agocke
Copy link
Member Author

agocke commented May 21, 2025

OK, I'll keep this as is for now -- it will exactly replace our docs to put a bp on RhThrowEx. If we want to do more, that can be follow-up.

@agocke agocke enabled auto-merge (squash) May 27, 2025 20:22
@agocke agocke merged commit 4fea27c into dotnet:main May 27, 2025
94 of 96 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[NativeAOT] Fire First Chance Exceptions if Debugger is Attached
4 participants