Skip to content

Implement ExceptionHandling.SetFatalErrorHandler#129543

Draft
AaronRobinsonMSFT wants to merge 12 commits into
dotnet:mainfrom
AaronRobinsonMSFT:set-fatal-error-handler
Draft

Implement ExceptionHandling.SetFatalErrorHandler#129543
AaronRobinsonMSFT wants to merge 12 commits into
dotnet:mainfrom
AaronRobinsonMSFT:set-fatal-error-handler

Conversation

@AaronRobinsonMSFT

@AaronRobinsonMSFT AaronRobinsonMSFT commented Jun 17, 2026

Copy link
Copy Markdown
Member

Implements the ExceptionHandling.SetFatalErrorHandler API (#101560) for both NativeAOT and CoreCLR. Mono throws PlatformNotSupportedException.

Changes

Managed API (ExceptionHandling.cs)

  • Enable SetFatalErrorHandler and s_fatalErrorHandler for CoreCLR (Mono still throws PNSE)

NativeAOT (RuntimeExceptionHelpers.cs)

  • Invoke handler from FailFast after crash info is written to stderr
  • Capture crash log alongside stderr writes in a pre-allocated 8KB buffer
  • pfnGetFatalErrorLog implemented via [UnmanagedCallersOnly] callback
  • SkipDefaultHandler exits via _Exit/ExitProcess without crash dump

CoreCLR (eepolicy.cpp)

  • Read s_fatalErrorHandler from managed static via CoreLibBinder
  • Invoke handler after LogFatalError in both HandleFatalError and HandleFatalStackOverflow
  • Crash log captured by tee-ing PrintToStdErrA into a static buffer
  • SkipDefaultHandler calls _exit() to bypass crash dump

Public native header (src/native/public/FatalErrorHandling.h)

  • Defines FatalErrorHandlerResult enum, FatalErrorInfo struct, callback typedefs

Tests (src/tests/baseservices/exceptions/FatalErrorHandler/)

  • Subprocess-based tests validating: SkipHandler, RunHandler, LogHandler, SetNull, SetTwice
  • Works on both CoreCLR and NativeAOT

Fixes #101560

AaronRobinsonMSFT and others added 2 commits June 17, 2026 15:05
Implement the ExceptionHandling.SetFatalErrorHandler API for NativeAOT.
The handler is invoked from RuntimeExceptionHelpers.FailFast before the
runtime performs its default crash handling (crash dump + abort).

- Add src/native/public/FatalErrorHandling.h defining the native
  FatalErrorInfo struct and FatalErrorHandlerResult enum
- Wire RegisterFatalErrorHandler as a no-op for NativeAOT (handler
  pointer stored in managed s_fatalErrorHandler field)
- Add crash log capture in FailFast alongside existing stderr output
- Implement pfnGetFatalErrorLog callback via UnmanagedCallersOnly
- SkipDefaultHandler exits via _Exit/ExitProcess instead of crash dump
- Consolidate ExceptionHandling partials: MONO||CORECLR throws PNSE
  inline, eliminating per-runtime partial files
- Add subprocess-based smoke tests validating handler invocation,
  SkipDefaultHandler/RunDefaultHandler, pfnGetFatalErrorLog callback,
  and API contract (null/double-set)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wire up the user-registered fatal error handler in the CoreCLR runtime.
Read the managed ExceptionHandling.s_fatalErrorHandler static field via
CoreLibBinder and invoke the handler after LogFatalError completes in
both HandleFatalError and HandleFatalStackOverflow. If the handler
returns SkipDefaultHandler, exit without crash dump.

- Add ExceptionHandling class/field bindings to corelib.h
- Enable s_fatalErrorHandler field and SetFatalErrorHandler for CoreCLR
- Add crash log capture in PrintToStdErrA for pfnGetFatalErrorLog
- Include public/FatalErrorHandling.h for shared type definitions
- Fix test subprocess launch for CoreCLR (pass DLL path to corerun)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 17, 2026 22:24
@github-actions github-actions Bot added the area-ExceptionHandling-coreclr only use for closed issues label Jun 17, 2026
@AaronRobinsonMSFT AaronRobinsonMSFT changed the title Implement ExceptionHandling.SetFatalErrorHandler Implement ExceptionHandling.SetFatalErrorHandler Jun 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 introduces the public System.Runtime.ExceptionServices.ExceptionHandling.SetFatalErrorHandler API and wires it up so CoreCLR and NativeAOT invoke a user-provided unmanaged callback during fatal-error paths, with a mechanism to retrieve the fatal-error log text.

Changes:

  • Adds ExceptionHandling.SetFatalErrorHandler(delegate* unmanaged<int, void*, int>) to the public surface and implements registration in System.Private.CoreLib.
  • Implements fatal-error handler invocation + crash-log capture in both CoreCLR (VM) and NativeAOT fail-fast paths.
  • Adds a new native public header (FatalErrorHandling.h) and a new subprocess-based test covering handler behaviors.
Show a summary per file
File Description
src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.csproj Adds new standalone test project for fatal error handler scenarios.
src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Subprocess-based validation of handler invocation, skip/run default behavior, and log retrieval.
src/native/public/FatalErrorHandling.h Defines native ABI structs/enums/callback types for fatal error handling and log retrieval.
src/libraries/System.Runtime/ref/System.Runtime.cs Adds the new public ref-assembly API for SetFatalErrorHandler.
src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs Implements handler registration and stores function pointer for runtimes to read.
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx Adds resource string for duplicate fatal handler registration.
src/coreclr/vm/util.hpp Declares crash-log capture helpers used by fatal-error handler plumbing.
src/coreclr/vm/util.cpp Implements stderr “tee” into a fixed crash-log buffer.
src/coreclr/vm/eepolicy.cpp Invokes the fatal handler after logging fatal errors / stack overflow and provides log callback.
src/coreclr/vm/corelib.h Adds CoreLibBinder field binding for ExceptionHandling.s_fatalErrorHandler.
src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs Captures crash output into a buffer and invokes the fatal handler before default crash processing.

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 5

Comment thread src/coreclr/vm/eepolicy.cpp Outdated
Comment thread src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 17, 2026 22:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 6

Comment thread src/coreclr/vm/util.cpp
Comment thread src/coreclr/vm/eepolicy.cpp
Comment thread src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Outdated
Comment thread src/libraries/System.Runtime/ref/System.Runtime.cs
AaronRobinsonMSFT and others added 2 commits June 17, 2026 15:58
The SkipDefaultHandler path should terminate immediately without running
atexit handlers, which can deadlock in a corrupted process. Replace the
call to exit() (via Interop.Sys.Exit) with _exit() (via a new
Interop.Sys._Exit P/Invoke) in the NativeAOT FailFast path, matching
CoreCLR's native _exit() semantics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Outdated
Comment thread src/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 17, 2026 23:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 17/17 changed files
  • Comments generated: 4

Comment thread src/coreclr/vm/eepolicy.cpp
Comment thread src/native/libs/System.Native/pal_threading.c
Comment thread src/native/libs/System.Native/pal_threading_wasi.c
- Use C99 _Exit() instead of _exit() to avoid unistd.h dependency
- Add COR_E_FAILFAST to IsCrashExitCode for Windows CoreCLR
- Suppress unused parameter warning in GetFatalErrorLogCallback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On Windows, WatsonLastChance calls RaiseFailFastException which
terminates the process before InvokeFatalErrorHandler is reached.
Move the handler invocation before the Watson/debugger code path
in both HandleFatalError and HandleFatalStackOverflow.

In HandleFatalError, call LogInfoForFatalError directly first to
populate the crash log buffer for the handler, then invoke the
handler, then proceed with LogFatalError for ETW and Watson.

Exclude FatalErrorHandlerTest from Mono runs since
SetFatalErrorHandler throws PlatformNotSupportedException on Mono.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 18, 2026 19:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 17/17 changed files
  • Comments generated: 6

Comment thread src/native/public/FatalErrorHandling.h
Comment thread src/native/public/FatalErrorHandling.h
Comment thread src/coreclr/vm/util.cpp
AaronRobinsonMSFT and others added 3 commits June 18, 2026 14:27
- Split PrintToStdErrW to write crash log as UTF-8 regardless of console
  codepage, skip re-conversion when console is already CP_UTF8
- Extract AppendToCrashLog helper for crash log buffer management
- Fix NativeAOT AppendToCrashLog to use TryGetBytes with truncation
  fallback instead of throwing on buffer overflow
- Relax TestRunHandler exit code assertion to non-zero check
- Remove COR_E_FAILFAST from IsCrashExitCode

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move fatal error handler implementations from managed [UnmanagedCallersOnly]
methods to a native C++ shared library. This avoids managed allocations
during fatal errors (which fail on Windows) and validates that the public
FatalErrorHandling.h header is usable from third-party C++ code.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 17:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 1

Comment thread src/coreclr/vm/util.cpp Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 19, 2026 18:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot's findings

  • Files reviewed: 19/19 changed files
  • Comments generated: 7

Comment on lines +92 to +96
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string arguments = !string.IsNullOrEmpty(assemblyLocation)
? $"\"{assemblyLocation}\" {scenario}"
: scenario;

Comment on lines +719 to +723
if (pExceptionInfo != NULL)
{
errorInfo.info = pExceptionInfo->ExceptionRecord;
errorInfo.context = pExceptionInfo->ContextRecord;
}
Comment on lines +1014 to +1020
STRESS_LOG0(LF_CORDB,LL_INFO100, "D::HFE: About to call LogInfoForFatalError\n");

// Log exception to StdErr — this populates the crash log buffer that the
// user's fatal error handler can retrieve via pfnGetFatalErrorLog.
// Pass fIntentionalReentry = true since LogFatalError will call this again.
LogInfoForFatalError(exitCode, pszMessage, pExceptionInfo, errorSource, argExceptionString, /* fIntentionalReentry */ true);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-ExceptionHandling-coreclr only use for closed issues

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[API Proposal]: Overriding the default behavior in case of unhandled exceptions and fatal errors.

3 participants