You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Today the runtime's crash dump generation (createdump) can only be controlled with environment variables (DOTNET_DbgEnableMiniDump, DOTNET_DbgMiniDumpType, DOTNET_DbgMiniDumpName) set before the process starts — the createdump command line is prebuilt at startup so the crash handler does no work. This leaves several scenarios uncovered:
A user-launched application cannot arm crash dumps for itself (Enable minidump creation directly from application #56135): a GUI app started from the shell has no cooperative parent to set the variables, and SetEnvironmentVariable at runtime is a no-op for this purpose.
An application cannot write a dump of itself on demand without the diagnostics IPC. The IPC route (DiagnosticsClient.WriteDump) requires a second process and a healthy diagnostics server in the target — the very thing that may be broken during a crash — and the resulting dump has no crash context (no exception stream on Windows, no crashing-thread information on Linux).
After the dump, nothing user-visible happens. For interactive apps the crashed process is dead (and if it is a GUI app, cannot render); showing a crash-report UI or uploading the dump requires a fresh process, which today means shipping a separate pre-spawned monitor.
In the discussion on #101560, @noahfalk suggested splitting this into two complementary APIs (comment):
New BCL CreateDump() style API - flexible programatic control for dump creation at any point
New BCL ConfigureDumpOnCrash() style API - more limited options but covers more failure types
This proposal is exactly that pair, on the class that already hosts the crash-handling APIs (ExceptionHandling.SetUnhandledExceptionHandler / the upcoming SetFatalErrorHandler, #129543):
WriteCrashDump — write a dump of the current process on demand, by launching createdump directly (no IPC). When the calling thread has an exception in flight (e.g. inside an AppDomain.UnhandledException handler), the dump records that exception and thread as the crash, so debuggers and analysis tools open it at the failure point.
EnableDumpOnCrash / DisableDumpOnCrash — enable/disable/reconfigure the runtime's own on-crash dump at any time from managed code, overriding the environment. Because no managed code needs to run after the failure, this covers crash types no managed handler can (native threads, stack overflow, runtime corruption). An overload also specifies a program (e.g. a crash reporter) that createdump launches once the dump has been written, from a fresh process, closing the "show UI / upload from a dead process" gap.
API Proposal
namespaceSystem.Runtime.ExceptionServices;publicstaticpartialclassExceptionHandling{// existing: SetUnhandledExceptionHandler, RaiseAppDomainUnhandledExceptionEvent/// Writes a dump of the current process by launching createdump directly (no diagnostics IPC);/// blocks until the dump is written. When the calling thread has an exception in flight, the/// dump records it as the crash (exception stream on Windows).publicstaticvoidWriteCrashDump(stringpath,CrashDumpTypetype=CrashDumpType.WithHeap);/// Enables/changes the dump the runtime writes when the process crashes, overriding/// the DOTNET_DbgEnableMiniDump* environment variables. Callable at any time; last call wins.publicstaticvoidEnableDumpOnCrash(CrashDumpTypetype=CrashDumpType.WithHeap,string?pathTemplate=null);/// Same, with a program createdump launches once the crash dump has been written. %f in the/// arguments is replaced with the dump path (plus the usual %p/%e/%h/%t specifiers).publicstaticvoidEnableDumpOnCrash(CrashDumpTypetype,string?pathTemplate,stringlaunchProgram,paramsstring[]?launchArguments);/// Disables dump on process crashpublicstaticvoidDisableDumpOnCrash();}publicenumCrashDumpType{Normal=1,// same values as the existing DumpType inWithHeap=2,// Microsoft.Diagnostics.NETCore.Client and DOTNET_DbgMiniDumpTypeTriage=3,Full=4,}
Note: I hesitated between a single ConfigureDumpOnCrash and EnableDumpOnCrash with overloads plus DisableDumpOnCrash. I didn't like the former much because it needs an enabled flag (or a nullable CrashDumpType? with null meaning disabled), which makes contradictory calls representable (e.g. disabled but with a launch program) and gives disabling an unclear call site (ConfigureDumpOnCrash(false, ...) / ConfigureDumpOnCrash(null)), while EnableDumpOnCrash/DisableDumpOnCrash are self-describing.
Semantics:
pathTemplate/path support the existing dump name specifiers (%p pid, %e executable name, %h hostname, %t timestamp).
EnableDumpOnCrash does all its work at call time, in a healthy context; nothing is added to what the crash handler does today.
The launch program can only be configured through the API, never from the environment — with the environment variables alone, a crash still only ever runs createdump itself. This avoids turning the knob into arbitrary-code-execution-on-crash for anyone who can set environment variables (the concern that applied to the env-var shape of Register a custom crash handler #95461). Precedents for the mechanism: Linux core_pattern pipes (but system-global, root-only) and Windows WerRegisterRuntimeExceptionModule (but native-DLL-only, Windows-only); this is the portable, per-process, in-app-registered equivalent.
The launched program only starts once the dump has been fully written, runs in a fresh process, and is not waited on.
Errors: WriteCrashDump throws IOException if the dump could not be written; all of these methods throw PlatformNotSupportedException where unsupported (NativeAOT, Mono, iOS/tvOS/wasm initially).
API Usage
// Game engine editor (user-launched GUI app, cannot use DOTNET_DbgEnableMiniDump):// arm the runtime dumper and have it start our crash reporter after writing the dump.ExceptionHandling.EnableDumpOnCrash(CrashDumpType.WithHeap,Path.Combine(crashDir,"editor-%p-%t.dmp"),"crashreporter.exe","--dump","%f","--pid","%p");// On-demand dump with the in-flight exception recorded, e.g. before showing an error dialog:AppDomain.CurrentDomain.UnhandledException+=(s,e)=>{ExceptionHandling.WriteCrashDump(Path.Combine(crashDir,"managed-%p-%t.dmp"),CrashDumpType.Normal);// ... report/upload, then let the process die};// CI/tools can turn dumps off for intentionally-crashing child scenarios:ExceptionHandling.DisableDumpOnCrash();
Composition with SetFatalErrorHandler (#129543), once it ships: for fatal errors (native-thread faults, FailFast, runtime errors), EnableDumpOnCrash alone already produces the dump and launches the reporter with no managed code running after the failure — no handler needed. A fatal error handler adds value when the app wants to write the dump at a moment of its choosing or capture app-specific state first:
// Illustrative only — #129543 is in progress, and whether managed code may run in the fatal// error handler is one of the open questions below (the alternative is a native function// pointer exposing the same operation, see Open questions).[UnmanagedCallersOnly]staticintOnFatalError(nintcontext){ExceptionHandling.WriteCrashDump(fatalDumpPath,CrashDumpType.WithHeap);// ... spawn/notify the reporter with fatalDumpPath ...returnRunDefaultHandler;}ExceptionHandling.SetFatalErrorHandler(&OnFatalError);
Alternative Designs
runtimeconfig.json knobs for the arming half (the direction once suggested on Enable minidump creation directly from application #56135): solves the build-time case but not per-user dump paths (%LOCALAPPDATA%-style) or runtime decisions; could still be added later on top of the same internals.
A parameter object for the launch pair (CrashDumpLaunch { Program, Arguments }) instead of an overload: more extensible but more verbose/heavier; ProcessStartInfo is not reusable (wrong assembly, and most of its surface (environment, redirects) would not be honored).
Background and motivation
Today the runtime's crash dump generation (createdump) can only be controlled with environment variables (
DOTNET_DbgEnableMiniDump,DOTNET_DbgMiniDumpType,DOTNET_DbgMiniDumpName) set before the process starts — the createdump command line is prebuilt at startup so the crash handler does no work. This leaves several scenarios uncovered:SetEnvironmentVariableat runtime is a no-op for this purpose.DiagnosticsClient.WriteDump) requires a second process and a healthy diagnostics server in the target — the very thing that may be broken during a crash — and the resulting dump has no crash context (no exception stream on Windows, no crashing-thread information on Linux).MiniDumpWriteDump— is explicitly unsupported ([API Proposal]: Calling UnmanagedCallersOnly method regardless of GC mode #119142) and now crashes under a debugger on .NET 10 (Regression in net10.0: vectored exception handler crashes under the debugger #133066).In the discussion on #101560, @noahfalk suggested splitting this into two complementary APIs (comment):
This proposal is exactly that pair, on the class that already hosts the crash-handling APIs (
ExceptionHandling.SetUnhandledExceptionHandler/ the upcomingSetFatalErrorHandler, #129543):WriteCrashDump— write a dump of the current process on demand, by launching createdump directly (no IPC). When the calling thread has an exception in flight (e.g. inside anAppDomain.UnhandledExceptionhandler), the dump records that exception and thread as the crash, so debuggers and analysis tools open it at the failure point.EnableDumpOnCrash/DisableDumpOnCrash— enable/disable/reconfigure the runtime's own on-crash dump at any time from managed code, overriding the environment. Because no managed code needs to run after the failure, this covers crash types no managed handler can (native threads, stack overflow, runtime corruption). An overload also specifies a program (e.g. a crash reporter) that createdump launches once the dump has been written, from a fresh process, closing the "show UI / upload from a dead process" gap.API Proposal
Note: I hesitated between a single
ConfigureDumpOnCrashandEnableDumpOnCrashwith overloads plusDisableDumpOnCrash. I didn't like the former much because it needs anenabledflag (or a nullableCrashDumpType?with null meaning disabled), which makes contradictory calls representable (e.g. disabled but with a launch program) and gives disabling an unclear call site (ConfigureDumpOnCrash(false, ...)/ConfigureDumpOnCrash(null)), whileEnableDumpOnCrash/DisableDumpOnCrashare self-describing.Semantics:
pathTemplate/pathsupport the existing dump name specifiers (%ppid,%eexecutable name,%hhostname,%ttimestamp).EnableDumpOnCrashdoes all its work at call time, in a healthy context; nothing is added to what the crash handler does today.core_patternpipes (but system-global, root-only) and WindowsWerRegisterRuntimeExceptionModule(but native-DLL-only, Windows-only); this is the portable, per-process, in-app-registered equivalent.WriteCrashDumpthrowsIOExceptionif the dump could not be written; all of these methods throwPlatformNotSupportedExceptionwhere unsupported (NativeAOT, Mono, iOS/tvOS/wasm initially).API Usage
Composition with
SetFatalErrorHandler(#129543), once it ships: for fatal errors (native-thread faults, FailFast, runtime errors),EnableDumpOnCrashalone already produces the dump and launches the reporter with no managed code running after the failure — no handler needed. A fatal error handler adds value when the app wants to write the dump at a moment of its choosing or capture app-specific state first:Alternative Designs
runtimeconfig.jsonknobs for the arming half (the direction once suggested on Enable minidump creation directly from application #56135): solves the build-time case but not per-user dump paths (%LOCALAPPDATA%-style) or runtime decisions; could still be added later on top of the same internals.CrashDumpLaunch { Program, Arguments }) instead of an overload: more extensible but more verbose/heavier;ProcessStartInfois not reusable (wrong assembly, and most of its surface (environment, redirects) would not be honored).Open Questions
WriteCrashDumpfrom aSetFatalErrorHandlercallback (native, possibly corrupted process) supported? Otherwise another option is to expose a native function pointer (as in [WIP]: Wire on-demand in-proc crash report generation into the fatal error handler API- #131414 #131959 for crash log function).dotnet-dump collect) attach the stored crash context? (Offered in [API Proposal]: Overriding the default behavior in case of unhandled exceptions and fatal errors. #101560; natural follow-up.)Risks