diff --git a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs index 6110b801d747e7..0b8b0ac953a5a2 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -28,6 +28,7 @@ enum RhFailFastReason UnhandledException = 2, // "unhandled exception" UnhandledExceptionFromPInvoke = 3, // "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition." EnvironmentFailFast = 4, + AssertionFailure = 5, } internal static unsafe partial class EH diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs index 5fd8c51adb8e06..231761bb6a7d3e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/DeveloperExperience/DeveloperExperience.cs @@ -101,14 +101,6 @@ public virtual void TryGetMethodBase(IntPtr methodStartAddress, out MethodBase m } } - public virtual bool OnContractFailure(string? stackTrace, ContractFailureKind contractFailureKind, string? displayMessage, string userMessage, string conditionText, Exception innerException) - { - Debug.WriteLine("Assertion failed: " + (displayMessage ?? "")); - if (Debugger.IsAttached) - Debugger.Break(); - return false; - } - public static DeveloperExperience Default { get diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/CrashInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/CrashInfo.cs index 8a2f33c93f1f8c..b2598eec37b7e1 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/CrashInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/CrashInfo.cs @@ -111,6 +111,7 @@ private bool WriteHeader(RhFailFastReason reason, ulong crashingThreadId, string CrashReason crashReason = reason switch { + RhFailFastReason.AssertionFailure or RhFailFastReason.EnvironmentFailFast => CrashReason.EnvironmentFailFast, RhFailFastReason.InternalError => CrashReason.InternalFailFast, RhFailFastReason.UnhandledException or diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.cs index 10312e4473b902..857d69d67e296d 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Environment.NativeAot.cs @@ -35,23 +35,16 @@ public static void Exit(int exitCode) // to assign blame for crashes. Don't mess with this, such as by making it call // another managed helper method, unless you consult with some CLR Watson experts. [DoesNotReturn] - public static void FailFast(string message) => + public static void FailFast(string? message) => RuntimeExceptionHelpers.FailFast(message); [DoesNotReturn] - public static void FailFast(string message, Exception exception) => + public static void FailFast(string? message, Exception? exception) => RuntimeExceptionHelpers.FailFast(message, exception); - internal static void FailFast(string message, Exception exception, string _ /*errorSource*/) - { - // TODO: errorSource originates from CoreCLR (See: https://github.com/dotnet/coreclr/pull/15895) - // For now, we ignore errorSource but we should distinguish the way FailFast prints exception message using errorSource - bool result = DeveloperExperience.Default.OnContractFailure(exception.StackTrace, ContractFailureKind.Assert, message, null, null, null); - if (!result) - { - RuntimeExceptionHelpers.FailFast(message, exception); - } - } + // Used by System.Diagnostics.Debug.Assert/Fail + internal static void FailFast(string? message, Exception? exception, string errorSource) => + RuntimeExceptionHelpers.FailFast(message, exception, errorSource: errorSource, reason: RhFailFastReason.AssertionFailure); private static int GetProcessorCount() => Runtime.RuntimeImports.RhGetProcessCpuCount(); diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs index fbe17e69ef95c2..2ce94084149a89 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -118,40 +118,21 @@ public class RuntimeExceptionHelpers } } - private static string GetStringForFailFastReason(RhFailFastReason reason) - { - switch (reason) + private static string GetStringForFailFastReason(RhFailFastReason reason) => reason switch { - case RhFailFastReason.InternalError: - return "Runtime internal error"; - case RhFailFastReason.UnhandledException: - return "Unhandled exception: a managed exception was not handled before reaching unmanaged code"; - case RhFailFastReason.UnhandledExceptionFromPInvoke: - return "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition"; - case RhFailFastReason.EnvironmentFailFast: - return "Environment.FailFast was called"; - default: - return "Unknown reason."; - } - } - - [DoesNotReturn] - public static void FailFast(string message) - { - FailFast(message, null, RhFailFastReason.EnvironmentFailFast, IntPtr.Zero, IntPtr.Zero); - } - - [DoesNotReturn] - public static void FailFast(string message, Exception? exception) - { - FailFast(message, exception, RhFailFastReason.EnvironmentFailFast, IntPtr.Zero, IntPtr.Zero); - } + RhFailFastReason.InternalError => "Runtime internal error", + RhFailFastReason.UnhandledException => "Unhandled exception: a managed exception was not handled before reaching unmanaged code", + RhFailFastReason.UnhandledExceptionFromPInvoke => "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition", + RhFailFastReason.EnvironmentFailFast => "Environment.FailFast was called", + RhFailFastReason.AssertionFailure => "Assertion failure", + _ => "Unknown reason." + }; // Used to report exceptions that *logically* go unhandled in the Fx code. For example, an // exception that escapes from a ThreadPool workitem, or from a void-returning async method. public static void ReportUnhandledException(Exception exception) { - FailFast(GetStringForFailFastReason(RhFailFastReason.UnhandledException), exception, RhFailFastReason.UnhandledException, IntPtr.Zero, IntPtr.Zero); + FailFast(exception: exception, reason: RhFailFastReason.UnhandledException); } // This is the classlib-provided fail-fast function that will be invoked whenever the runtime @@ -167,7 +148,7 @@ public static void RuntimeFailFast(RhFailFastReason reason, Exception? exception // back into the dispatcher. try { - FailFast(message: null, exception, reason, pExAddress, pExContext); + FailFast(exception: exception, reason: reason, pExAddress: pExAddress, pExContext: pExContext); } catch { @@ -199,7 +180,9 @@ internal unsafe struct EXCEPTION_RECORD private static ulong s_crashingThreadId; [DoesNotReturn] - internal static unsafe void FailFast(string? message, Exception? exception, RhFailFastReason reason, IntPtr pExAddress, IntPtr pExContext) + internal static unsafe void FailFast(string? message = null, Exception? exception = null, string? errorSource = null, + RhFailFastReason reason = RhFailFastReason.EnvironmentFailFast, + IntPtr pExAddress = 0, IntPtr pExContext = 0) { IntPtr triageBufferAddress = IntPtr.Zero; int triageBufferSize = 0; @@ -209,33 +192,43 @@ internal static unsafe void FailFast(string? message, Exception? exception, RhFa ulong previousThreadId = Interlocked.CompareExchange(ref s_crashingThreadId, currentThreadId, 0); if (previousThreadId == 0) { - message ??= GetStringForFailFastReason(reason); - CrashInfo crashInfo = new(); - crashInfo.Open(reason, s_crashingThreadId, message); + crashInfo.Open(reason, s_crashingThreadId, message ?? GetStringForFailFastReason(reason)); bool minimalFailFast = (exception == PreallocatedOutOfMemoryException.Instance); if (!minimalFailFast) { - string prefix; - string outputMessage; - if (exception != null) + Internal.Console.Error.Write(((exception == null) || (reason is RhFailFastReason.EnvironmentFailFast or RhFailFastReason.AssertionFailure)) ? + "Process terminated. " : "Unhandled exception. "); + + if (errorSource != null) + { + Internal.Console.Error.Write(errorSource); + Internal.Console.Error.WriteLine(); + } + + if (message != null) { - prefix = "Unhandled exception. "; - outputMessage = exception.ToString(); + Internal.Console.Error.Write(message); + Internal.Console.Error.WriteLine(); } - else + + if (errorSource == null && message == null && (exception == null || reason is RhFailFastReason.EnvironmentFailFast)) + { + Internal.Console.Error.Write(GetStringForFailFastReason(reason)); + Internal.Console.Error.WriteLine(); + } + + if (reason is RhFailFastReason.EnvironmentFailFast) { - prefix = "Process terminated. "; - outputMessage = message; + Internal.Console.Error.Write(new StackTrace().ToString()); } - Internal.Console.Error.Write(prefix); - if (outputMessage != null) + if ((exception != null) && (reason is not RhFailFastReason.AssertionFailure)) { - Internal.Console.Error.Write(outputMessage); + Internal.Console.Error.Write(exception.ToString()); + Internal.Console.Error.WriteLine(); } - Internal.Console.Error.Write(Environment.NewLine); if (exception != null) { diff --git a/src/libraries/System.Private.CoreLib/src/Internal/Console.cs b/src/libraries/System.Private.CoreLib/src/Internal/Console.cs index 5034a2be59b333..c6ce19fae3bdee 100644 --- a/src/libraries/System.Private.CoreLib/src/Internal/Console.cs +++ b/src/libraries/System.Private.CoreLib/src/Internal/Console.cs @@ -17,5 +17,11 @@ public static void WriteLine(string? s) => public static void WriteLine() => Write(Environment.NewLineConst); + + public static partial class Error + { + public static void WriteLine() => + Write(Environment.NewLineConst); + } } }