Skip to content

Implement ExceptionHandling.SetFatalErrorHandler#129543

Open
AaronRobinsonMSFT wants to merge 42 commits into
dotnet:mainfrom
AaronRobinsonMSFT:set-fatal-error-handler
Open

Implement ExceptionHandling.SetFatalErrorHandler#129543
AaronRobinsonMSFT wants to merge 42 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)

  • Mono throws PNSE

NativeAOT (RuntimeExceptionHelpers.cs)

  • Supported.

CoreCLR (eepolicy.cpp)

  • Supported.

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 Outdated
Comment thread src/coreclr/vm/eepolicy.cpp Outdated
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/tests/baseservices/exceptions/FatalErrorHandler/FatalErrorHandlerTest.cs Outdated
Comment thread src/coreclr/vm/eepolicy.cpp Outdated
Comment thread src/native/libs/System.Native/pal_threading.c Outdated
Comment thread src/native/libs/System.Native/pal_threading_wasi.c Outdated
- 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 review requested due to automatic review settings July 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

Comments suppressed due to low confidence (2)

src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs:82

  • On Mono, the method throws PlatformNotSupportedException before validating handler for null. The approved API comment on #101560 documents ArgumentNullException for a null handler; argument validation should generally happen before platform checks so callers get consistent exception behavior.
        public static unsafe void SetFatalErrorHandler(delegate* unmanaged<int, void*, int> handler)
        {
#if MONO
            throw new PlatformNotSupportedException();
#else
            ArgumentNullException.ThrowIfNull((void*)handler, nameof(handler));

            if (!TrySetFatalErrorHandler((IntPtr)handler))
            {
                throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondFatalErrorHandler);
            }
#endif

src/native/public/FatalErrorHandling.h:7

  • The PR description / linked issue text describes a FatalErrorInfo struct-based contract, but this header actually defines a property-getter callback model (no FatalErrorInfo struct). Please either update the PR description/docs to match the implemented contract, or adjust the header/managed docs if the struct-based design is still the intended API.
// This header defines the native types used by the
// ExceptionHandling.SetFatalErrorHandler API. A native fatal error handler
// receives an HRESULT and a property-getter callback through which it can
// request additional crash information on demand.
  • Files reviewed: 29/29 changed files
  • Comments generated: 0 new

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "1a608d4054e854663de18298a4dd61e6e098ea16",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "c9265d78e03e96c00b7ec062f2dbf2465bbb86d6",
  "last_reviewed_commit": "1a608d4054e854663de18298a4dd61e6e098ea16",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "c9265d78e03e96c00b7ec062f2dbf2465bbb86d6",
  "last_recorded_worker_run_id": "29772964837",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "3b168438f67eb0478f255d4a21a55bb4b41186fe",
      "review_id": 4730782393
    },
    {
      "commit": "1a608d4054e854663de18298a4dd61e6e098ea16",
      "review_id": 4738486867
    }
  ]
}

@github-actions github-actions Bot 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.

Holistic Review

Motivation: Strong and well-established. This implements the api-approved ExceptionHandling.SetFatalErrorHandler (issue #101560), a long-requested capability (crash reporters like breakpad/Sentry behind the .NET runtime) with clear demand from the linked issue's discussion. The problem is real and the direction was reviewed by API review.

Approach: Reasonable and consistent with the codebase. The public managed surface exactly matches the approved shape (namespace, SetFatalErrorHandler(delegate* unmanaged<int, void*, int>), [CLSCompliant(false)], ANE/IOE/PNSE exceptions). The shared native contract lives in a new public header (FatalErrorHandling.h), registration uses a lock-free InterlockedCompareExchange single-registration model, and crash output is refactored behind a CrashInfoWriter abstraction so the same text can go to stderr or the user's log callback. CoreCLR hooks HandleFatalError/HandleFatalStackOverflow; NativeAOT hooks the classlib FailFast plus the Unix signal handlers and a new Windows last-chance unhandled-exception filter. The GetRuntimeException classlib export signature change (adding faultingIP) is threaded consistently through all call sites and Test.CoreLib.

Summary: ⚠️ Needs Human Review. The API shape is approved and correctly implemented, and the change is thorough with a genuinely comprehensive subprocess-based test matrix (skip/run/log/native/nested/set-null/set-twice across CoreCLR and NativeAOT). My main reservations are around the fatal-path robustness of invoking arbitrary user code before the runtime's crashing-thread latch and the documented re-entrancy/concurrency contract — see the inline comments. These are behavioral/contract questions best judged by the exceptions/EH maintainers rather than blocking defects I can confirm. As a new public runtime API with platform-specific EH surgery, this warrants careful human review, and I could not run builds or tests in this environment.


Detailed Findings

✅ API Approval — Public surface matches the approved proposal

The ref/System.Runtime.cs addition and the src implementation match the api-approved comment on issue #101560 exactly: System.Runtime.ExceptionServices.ExceptionHandling.SetFatalErrorHandler(delegate* unmanaged<int, void*, int> handler), [CLSCompliant(false)], with ArgumentNullException/InvalidOperationException/PlatformNotSupportedException. No extra or missing public managed surface was found. The new src/native/public/FatalErrorHandling.h is a native (non-managed-ref) contract and is appropriately not part of the managed ref assembly.

⚠️ Fatal-path robustness / re-entrancy — see inline on eepolicy.cpp (InvokeFatalErrorHandler)

User code now runs on the crashing thread before the s_pCrashingThreadID latch (which moved into LogInfoForFatalError, only on the RunDefaultHandler path). This exposes concurrent and re-entrant handler invocation that the FatalErrorHandling.h contract does not document. Detailed inline comment provided; the same invocation pattern exists at the NativeAOT sites.

💡 Skip-path control flow — see inline on eepolicy.cpp (skip SafeExitProcess)

Minor defense-in-depth / consistency: the SkipDefaultHandler SafeExitProcess has no trailing UNREACHABLE() unlike the stack-overflow site, so a hypothetical return would fall through and emit the suppressed crash log.

✅ Cross-platform consistency — Windows filter vs. Unix signal handlers

The genuinely-unmanaged fatal path is handled symmetrically: Unix SIGSEGVHandler/SIGFPEHandler gate on ShouldSkipDefaultHandlingForNativeException and restore the previous disposition on skip; Windows adds a chained last-chance SetUnhandledExceptionFilter that preserves any previously-installed filter. IsFatalHardwareExceptionForFatalErrorHandler mirrors the same fault-code set on both CoreCLR/Windows and NativeAOT/Windows, and stack overflow is consistently and deliberately excluded (documented) because too little stack remains. One observation for the author/human reviewer: NativeAOT/Unix installs SIGSEGV and SIGFPE handlers but not SIGILL, while the fault-code lists include illegal/privileged-instruction codes — worth confirming that illegal-instruction native faults are intentionally out of scope on Unix.

✅ Test quality — comprehensive subprocess matrix

The test spawns child processes per scenario and asserts on stderr markers and exit codes: SkipHandler (crash log suppressed), RunHandler (default log emitted exactly once — with an explicit regression guard against the double-emission "Fatal error while logging another fatal error." path), LogHandler (pfnGetFatalErrorLog round-trip), native/native-code/nested-hardware-fault address+platform-record propagation, and the SetNull/SetTwice argument-validation paths. Platform/runtime applicability is gated correctly (e.g. native-code path skipped on CoreCLR/Unix, nested-fault path NativeAOT-only). RequiresProcessIsolation and the Mono CLRTestTargetUnsupported gate are set appropriately. This is a good, behavior-focused test suite.

💡 Minor naming/consistency observations (non-blocking)

  • The XML doc on SetFatalErrorHandler and the header comment reference pfnGetFatalErrorLog, but the managed doc refers to FatalErrorPropertyGetter; the naming across the doc comment, header, and code is mostly consistent but a human may want to confirm the doc-comment terminology (RunDefaultHandler/SkipDefaultHandler values, property getter) reads cleanly for the public API.
  • The NativeAOT FatalErrorProperty enum is duplicated in managed code with a "must be kept in sync" comment against the native header — acceptable, but a drift risk worth a human's awareness.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 412.1 AIC · ⌖ 12 AIC · ⊞ 10K

Comment thread src/coreclr/vm/eepolicy.cpp
Comment thread src/coreclr/vm/eepolicy.cpp
… memory efficiency and eliminating truncation risks
Copilot AI review requested due to automatic review settings July 20, 2026 19: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

Comments suppressed due to low confidence (2)

src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs:83

  • On Mono this method always throws PlatformNotSupportedException before validating the handler pointer. That means SetFatalErrorHandler(null) throws PNSE (not ArgumentNullException) and the documented exception behavior differs by runtime. In dotnet/runtime APIs, argument validation is typically performed before platform checks so exception behavior is consistent.
        public static unsafe void SetFatalErrorHandler(delegate* unmanaged<int, void*, int> handler)
        {
#if MONO
            throw new PlatformNotSupportedException();
#else
            ArgumentNullException.ThrowIfNull((void*)handler, nameof(handler));

            if (!TrySetFatalErrorHandler((IntPtr)handler))
            {
                throw new InvalidOperationException(SR.InvalidOperation_CannotRegisterSecondFatalErrorHandler);
            }
#endif
        }

src/native/public/FatalErrorHandling.h:79

  • Same C-compatibility concern as FatalErrorHandlerResult: enum FatalErrorProperty : int32_t is C++-only and makes this public header unusable from C. If C support is intended, prefer a typedef enum form here too.
enum FatalErrorProperty : int32_t
{
    // Value: FatalErrorLogFunc. Entry point for retrieving the crash log.
    FEP_FatalErrorLogFunc = 0x1,

    // Value: void*. Code location correlated with the failure (for example,
    // the address where FailFast was called). May be unavailable.
    FEP_Address = 0x2,

    // Value: PEXCEPTION_RECORD. Windows exception record for the failure.
    FEP_WindowsExceptionRecord = 0x3,

    // Value: PCONTEXT. Windows thread context at the point of failure.
    FEP_WindowsContextRecord = 0x4,

    // Value: ucontext_t*. Thread context on signal-based Unix platforms.
    FEP_UContext = 0x5,

    // Value: siginfo_t*. Signal information on signal-based Unix platforms.
    FEP_PosixSigInfo = 0x6,

    // Value: Mach thread state for the current architecture
    // (arm_thread_state64_t* on arm64, x86_thread_state64_t* on x64).
    FEP_MachExceptionInfo = 0x7,
};
  • Files reviewed: 29/29 changed files
  • Comments generated: 1

Comment thread src/native/public/FatalErrorHandling.h

@github-actions github-actions Bot 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.

Holistic Review

Motivation: Unchanged from the prior review. This implements the api-approved ExceptionHandling.SetFatalErrorHandler (issue #101560), a long-requested capability for hooking crash reporters (breakpad/Sentry-style) beneath the .NET runtime. The problem is real and API-review-approved.

Approach: Unchanged in shape and risk. The single new commit since the last review (1a608d4 — "Refactor crash log handling to use a single managed string") is a localized simplification of the NativeAOT crash-log storage in RuntimeExceptionHelpers.cs. It replaces the fixed-capacity InlineArray16<string?> fragment buffer (and its StoreCrashLogFragment/MaxCrashLogFragments machinery) with a single [ThreadStatic] string? t_crashLog composed via a StringBuilder on the crashing thread in FailFast, then replayed on demand by GetFatalErrorLog (handler callback) and WriteCrashLogToStdErr (default path). This eliminates the 16-fragment capacity limit and the associated Debug.Fail truncation risk that earlier reviews flagged, and it removes per-fragment iteration in favor of a single UTF-8 chunked encode. The overall design (public FatalErrorHandling.h contract, lock-free single registration, CoreCLR + NativeAOT + platform EH hooks) is otherwise identical.

Summary: ⚠️ Needs Human Review — unchanged verdict. The incremental refactor is a clean, self-contained improvement that correctly addresses the truncation concern: thread-affinity is preserved because both consumers run synchronously on the same crashing thread that sets t_crashLog (the non-crashing threads block in Thread.Sleep(int.MaxValue)), so making the storage [ThreadStatic] is safe and, if anything, more correct than the previous plain-static buffer. The minimal OOM path still avoids StringBuilder allocation by assigning a string literal directly. I found no new actionable defects in this commit. My prior reservations about fatal-path robustness / re-entrancy of invoking arbitrary user code and the documented concurrency contract still stand and remain the province of the exceptions/EH maintainers; they are unaffected by this commit. As a new public runtime API with platform-specific EH surgery, this continues to warrant careful human review, and I could not run builds or tests in this environment.


Assessment History

  • review 4730782393 reviewed commit 3b168438, verdict ⚠️ Needs Human Review. Current verdict ⚠️ Needs Human Review — unchanged. The only new commit is a localized crash-log storage refactor that resolves a previously-noted truncation risk without altering the API surface, registration model, or fatal-path invocation semantics; motivation, approach, and risk are all unchanged.

Detailed Findings

No new actionable findings in the incremental scope (3b168438..1a608d4). Observations on the refactor:

✅ Truncation risk resolved

The previous fragment-array design capped crash-log content at 16 fragments and would silently drop overflow (guarded by a Debug.Fail). The single t_crashLog string removes both the cap and the truncation path. GetFatalErrorLog still chunks the UTF-8 encode with the correct deferred flush handling to avoid splitting surrogate pairs across ChunkSize boundaries.

✅ ThreadStatic storage is thread-affinity-safe

Making the crash text [ThreadStatic] is correct here: it is written on the crashing thread in FailFast, and both readers (GetFatalErrorLog invoked synchronously through the user handler, and WriteCrashLogToStdErr on the default path) execute on that same thread. Other threads that enter FailFast block indefinitely, so there is no cross-thread read of t_crashLog.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 75 AIC · ⌖ 14.9 AIC · ⊞ 10K

@janvorli janvorli left a comment

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.

LGTM, thank you!

Comment thread src/coreclr/vm/eepolicy.cpp
lateralusX added a commit that referenced this pull request Jul 24, 2026
## Summary

Adds an on-demand entry point to the in-proc crash reporter so a report
can be generated programmatically while the runtime is still running,
independent of the fatal-signal path. This is the reporting mechanism
that a user-registered native
fatal-error handler (proposed in #129543) can call to produce a crash
report.

## Motivation

The in-proc crash reporter can currently only run from the PAL
fatal-signal dispatcher. The fatal-error-handler proposal (#129543)
needs a way to invoke just the reporting mechanism on demand, formatting
the report to a caller-supplied sink,
without the watchdog/lifecycle/file-management that the signal path
performs.

## What's in this PR

- **On-demand report API** —
`InProcCrashReportCreateReport(outputFormat, signal, context,
outputCallback, callbackContext)`. Emits the same report as the signal
path but streams the selected format (`Json` or `Log`) to a
caller-supplied callback, with no watchdog or file output. Re-runnable;
yields to any report already in flight.
- **Shared in-flight guard** — the signal-path and on-demand
`CreateReport` paths are made mutually exclusive over the shared
signal-safe writers, module table, and process-global thread-suspension
machinery via a single `m_reportInFlight` guard. The on-demand path
releases the guard on completion; the signal path never does (the
process is terminating).
- **Init / services split** — reporter bring-up is split into
`CrashReportInitialize` (VM callbacks only, so on-demand reports are
possible) and `InProcCrashReportInitializeServices` (env-gated watchdog
+ lifecycle + PAL dispatcher registration). This lets the reporter be
initialized from either the runtime startup path or a future
fatal-error-handler setup path, without arming the signal-path
crash-dump behavior.
- **Output sink refactor** — `SignalSafeConsoleWriter` and
`SignalSafeJsonWriter` gain a small copyable sink abstraction so a
single caller-supplied callback shape can drive either writer (platform
console / drop-all / caller callback).
- **Stack-overflow classification fix** — the in-proc SO stack trace is
now captured only on the real stack-overflow path. Previously a
`FailFast` (which funnels through the same fatal-error callstack logger)
populated the SO-trace latch, causing an on-demand report to misclassify
a `FailFast` as a `StackOverflow`. Gating capture to the real-SO path
makes the latch authoritative for classification.

## Behavior notes

- No behavior changes when crash reporting is disabled: startup
allocates nothing.
- No behavior changes when crash reporting is enabled: allocates and
configure signal-based in-proc crash reporter.

## Testing

- In-proc crash reporter lacks automatic tests, on-demand can be tested
through #129543 tests when integrated.
- Manually ran FailFast and StackOverflow paths on Android arm64
emulator.
- All tests in
https://github.com/mdh1418/runtime/blob/inproc_crashreport_tests/src/tests/FunctionalTests/Android/Device_Emulator/InProcCrashReport/OVERVIEW.md
pass on Android arm64 emulator.

## Follow-ups

- Wiring the on-demand entry point into the fatal-error handler once
#129543 lands.
…iling thread invokes the handler, preventing reentrancy and blocking other threads during process termination.
Copilot AI review requested due to automatic review settings July 24, 2026 22:56

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: 29/29 changed files
  • Comments generated: 2

Comment thread src/coreclr/vm/eepolicy.cpp
…dler

# Conflicts:
#	src/coreclr/vm/eepolicy.cpp
Copilot AI review requested due to automatic review settings July 24, 2026 23:15

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

Comments suppressed due to low confidence (2)

src/coreclr/vm/eepolicy.cpp:458

  • #include <public/FatalErrorHandling.h> is currently placed mid-file, after code, which is unusual for CoreCLR sources and makes it easy to miss compile dependencies (and can interact poorly with precompiled-header/include-order assumptions). Please move this include to the main include block at the top of the file, and keep this section focused on the fatal-error logging implementation.
//
// Uses the public FatalErrorHandling.h header for shared type definitions.
//

#include <public/FatalErrorHandling.h>

src/native/public/FatalErrorHandling.h:8

  • The PR description says FatalErrorHandling.h defines FatalErrorInfo / FatalErrorHandlerResult / callback typedefs. The header added here defines FatalErrorHandlerResult but does not define a FatalErrorInfo struct; it instead exposes a property-getter based model (FatalErrorProperty + FatalErrorPropertyGetter). Please align the PR description (and any related docs) with the actual header/API shape to avoid misleading native consumers.
// This header defines the native types used by the
// ExceptionHandling.SetFatalErrorHandler API. A native fatal error handler
// receives an HRESULT and a property-getter callback through which it can
// request additional crash information on demand.
//
  • Files reviewed: 29/29 changed files
  • Comments generated: 0 new

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.

Isn't this directory meant for mono embedded API? I think minipal is the better place for it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a public header file. It is there by design. See the new public native description at #101560.

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.

This looks odd because:

  1. Docs:

    # Public .NET APIs #
    The contents of this directory include headers for various native APIs exposed
    by the .NET runtimes.
    These include:
    - The Mono Embedding API
    - ...
    ## The Mono Embedding API ##

  2. First C++ header in this dir

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.

minipal is the better place for it.

I do not think minipal would be a better place for it. minipal is meant as an abstraction of the underlying platform.

We do not have a central location for CoreCLR/NAOT public headers. Some examples of where public headers live currently:

src\native\corehost\hostfxr.h
src\coreclr\inc\corprof.idl
src\coreclr\src\coreclr\pal\prebuilt\inc\corprof.h

We place some of the public headers into published packages, and some public headers we just tell people to copy out manually.

If we do not like src\native\public, src\native\inc or src\coreclr\inc are potential alternatives.

How are people expected to get FatalErrorHandling.h?

/// A pointer to an unmanaged callback invoked when the runtime encounters a fatal
/// error. The callback receives the HRESULT associated with the failure and a
/// property-getter callback (<c>FatalErrorPropertyGetter</c>, declared in
/// <c>FatalErrorHandling.h</c>) through which it can request additional crash

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.

Consider somebody reading this doc at https://learn.microsoft.com/dotnet/api/system.runtime.exceptionservices.exceptionhandling - how are they going to figure out where to acquire FatalErrorHandling.h?

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

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.

7 participants