From 0947cc226fc32a0ab65fcfa8e245ca11fcf61546 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 31 Aug 2023 21:22:22 +0200 Subject: [PATCH 1/4] Fix NativeAOT unhandled exception stack trace The recent change to add a new exception handling mechanism to coreclr has broken stack traces on unhandled exceptions in nativeaot. This change fixes it by reverting the part of the change that caused the problem and that turned out to not to be needed for the new exception handling either. Close #91298 --- .../Runtime.Base/src/System/Runtime/ExceptionHandling.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 251d6c0ae7093d..6110b801d747e7 100644 --- a/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs +++ b/src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -708,14 +708,14 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn uint startIdx = MaxTryRegionIdx; for (; isValid; isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke)) { - prevControlPC = frameIter.ControlPC; - prevOriginalPC = frameIter.OriginalControlPC; - // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we // disallow dispatching exceptions across native code. if (unwoundReversePInvoke) break; + prevControlPC = frameIter.ControlPC; + prevOriginalPC = frameIter.OriginalControlPC; + DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); UpdateStackTrace(exceptionObj, exInfo._frameIter.FramePointer, (IntPtr)frameIter.OriginalControlPC, frameIter.SP, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame, ref exInfo); From 3313937f38354c1143a9b82c739d0af26bff9892 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Fri, 1 Sep 2023 21:29:16 +0200 Subject: [PATCH 2/4] Add unhandled exception test --- .../exceptions/unhandled/unhandled.cs | 78 +++++++++++++++++++ .../exceptions/unhandled/unhandled.csproj | 12 +++ 2 files changed, 90 insertions(+) create mode 100644 src/tests/baseservices/exceptions/unhandled/unhandled.cs create mode 100644 src/tests/baseservices/exceptions/unhandled/unhandled.csproj diff --git a/src/tests/baseservices/exceptions/unhandled/unhandled.cs b/src/tests/baseservices/exceptions/unhandled/unhandled.cs new file mode 100644 index 00000000000000..0dc7967764f300 --- /dev/null +++ b/src/tests/baseservices/exceptions/unhandled/unhandled.cs @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace TestUnhandledException +{ + class Program + { + static int Main(string[] args) + { + if (args.Length != 0) + { + throw new Exception("Test"); + } + + List lines = new List(); + + Process testProcess = new Process(); + + testProcess.StartInfo.FileName = Environment.ProcessPath; + testProcess.StartInfo.Arguments = Environment.CommandLine + " throw"; + testProcess.StartInfo.UseShellExecute = false; + testProcess.StartInfo.RedirectStandardError = true; + testProcess.ErrorDataReceived += (sender, line) => + { + Console.WriteLine($"\"{line.Data}\""); + if (!string.IsNullOrEmpty(line.Data)) + { + lines.Add(line.Data); + } + }; + + testProcess.Start(); + testProcess.BeginErrorReadLine(); + testProcess.WaitForExit(); + testProcess.CancelErrorRead(); + + int expectedExitCode; + if ((Environment.OSVersion.Platform == PlatformID.Unix) || (Environment.OSVersion.Platform == PlatformID.MacOSX)) + { + expectedExitCode = 128 + 6; + } + else if (TestLibrary.Utilities.IsNativeAot) + { + expectedExitCode = unchecked((int)0xC0000409); + } + else + { + expectedExitCode = unchecked((int)0xE0434352); + } + + if (expectedExitCode != testProcess.ExitCode) + { + Console.WriteLine($"Wrong exit code 0x{testProcess.ExitCode:X8}"); + return 101; + } + + if (Regex.Match(lines[0], @"Unhandled exception[.:] System\.Exception\: Test", RegexOptions.IgnoreCase) == Match.Empty) + { + Console.WriteLine("Missing Unhandled exception header"); + return 102; + } + + if (!lines[1].TrimStart().StartsWith("at TestUnhandledException.Program.Main")) + { + Console.WriteLine("Missing exception source frame"); + return 103; + } + + return 100; + } + } +} diff --git a/src/tests/baseservices/exceptions/unhandled/unhandled.csproj b/src/tests/baseservices/exceptions/unhandled/unhandled.csproj new file mode 100644 index 00000000000000..047f77d45e41cb --- /dev/null +++ b/src/tests/baseservices/exceptions/unhandled/unhandled.csproj @@ -0,0 +1,12 @@ + + + Exe + false + + + + + + + + From 3b6cdc2c2e0971df4add64fa68a08a47058bbe0c Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 4 Sep 2023 14:58:01 +0200 Subject: [PATCH 3/4] Reflect PR feedback --- .../src/System/RuntimeExceptionHelpers.cs | 2 +- src/tests/baseservices/exceptions/unhandled/unhandled.cs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) 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 d156c034bdb247..fbe17e69ef95c2 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/RuntimeExceptionHelpers.cs @@ -221,7 +221,7 @@ internal static unsafe void FailFast(string? message, Exception? exception, RhFa string outputMessage; if (exception != null) { - prefix = "Unhandled Exception: "; + prefix = "Unhandled exception. "; outputMessage = exception.ToString(); } else diff --git a/src/tests/baseservices/exceptions/unhandled/unhandled.cs b/src/tests/baseservices/exceptions/unhandled/unhandled.cs index 0dc7967764f300..3cf0075e5333ef 100644 --- a/src/tests/baseservices/exceptions/unhandled/unhandled.cs +++ b/src/tests/baseservices/exceptions/unhandled/unhandled.cs @@ -24,7 +24,6 @@ static int Main(string[] args) testProcess.StartInfo.FileName = Environment.ProcessPath; testProcess.StartInfo.Arguments = Environment.CommandLine + " throw"; - testProcess.StartInfo.UseShellExecute = false; testProcess.StartInfo.RedirectStandardError = true; testProcess.ErrorDataReceived += (sender, line) => { @@ -41,7 +40,7 @@ static int Main(string[] args) testProcess.CancelErrorRead(); int expectedExitCode; - if ((Environment.OSVersion.Platform == PlatformID.Unix) || (Environment.OSVersion.Platform == PlatformID.MacOSX)) + if (!OperatingSystem.IsWindows()) { expectedExitCode = 128 + 6; } @@ -60,7 +59,7 @@ static int Main(string[] args) return 101; } - if (Regex.Match(lines[0], @"Unhandled exception[.:] System\.Exception\: Test", RegexOptions.IgnoreCase) == Match.Empty) + if (lines[0] != "Unhandled exception. System.Exception: Test") { Console.WriteLine("Missing Unhandled exception header"); return 102; From f148f79bca73968174e69aa0a38b272d4ad44a3f Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Mon, 4 Sep 2023 23:07:04 +0200 Subject: [PATCH 4/4] Fix Mono console log parsing --- .../exceptions/unhandled/unhandled.cs | 34 ++++++++++++++++--- src/tests/issues.targets | 3 ++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/tests/baseservices/exceptions/unhandled/unhandled.cs b/src/tests/baseservices/exceptions/unhandled/unhandled.cs index 3cf0075e5333ef..0fa3a4798f2c61 100644 --- a/src/tests/baseservices/exceptions/unhandled/unhandled.cs +++ b/src/tests/baseservices/exceptions/unhandled/unhandled.cs @@ -40,7 +40,11 @@ static int Main(string[] args) testProcess.CancelErrorRead(); int expectedExitCode; - if (!OperatingSystem.IsWindows()) + if (TestLibrary.Utilities.IsMonoRuntime) + { + expectedExitCode = 1; + } + else if (!OperatingSystem.IsWindows()) { expectedExitCode = 128 + 6; } @@ -59,13 +63,33 @@ static int Main(string[] args) return 101; } - if (lines[0] != "Unhandled exception. System.Exception: Test") + int exceptionStackFrameLine = 1; + if (TestLibrary.Utilities.IsMonoRuntime) { - Console.WriteLine("Missing Unhandled exception header"); - return 102; + if (lines[0] != "Unhandled Exception:") + { + Console.WriteLine("Missing Unhandled exception header"); + return 102; + } + if (lines[1] != "System.Exception: Test") + { + Console.WriteLine("Missing exception type and message"); + return 103; + } + + exceptionStackFrameLine = 2; + } + else + { + if (lines[0] != "Unhandled exception. System.Exception: Test") + { + Console.WriteLine("Missing Unhandled exception header"); + return 102; + } + } - if (!lines[1].TrimStart().StartsWith("at TestUnhandledException.Program.Main")) + if (!lines[exceptionStackFrameLine].TrimStart().StartsWith("at TestUnhandledException.Program.Main")) { Console.WriteLine("Missing exception source frame"); return 103; diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 88ff8034743849..2d24f9707abc3e 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -3230,6 +3230,9 @@ + + System.Diagnostics.Process is not supported + System.Threading.Thread.UnsafeStart not supported