From 0d45438de099e08f140ff4a487d1daeced683873 Mon Sep 17 00:00:00 2001 From: yowl Date: Wed, 14 Oct 2020 19:05:51 -0500 Subject: [PATCH] Wasm: enable stack trace exceptions, simple exceptions test, upgrade emscripten (#8319) --- eng/install-emscripten.cmd | 6 +-- .../src/CodeGen/ILToWebAssemblyImporter.cs | 14 +++++- .../src/System.Private.CoreLib.csproj | 1 + .../Diagnostics/StackTrace.CoreRT.Wasm.cs | 3 +- .../src/System/Exception.CoreRT.Wasm.cs | 42 ++++++++++++++++ tests/src/Simple/Exceptions/Exceptions.cmd | 8 +++- tests/src/Simple/Exceptions/Exceptions.cs | 48 +++++++++++++++++++ tests/src/Simple/Exceptions/wasm | 0 8 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 src/System.Private.CoreLib/src/System/Exception.CoreRT.Wasm.cs create mode 100644 tests/src/Simple/Exceptions/wasm diff --git a/eng/install-emscripten.cmd b/eng/install-emscripten.cmd index bd8bc5f69..39af231e2 100644 --- a/eng/install-emscripten.cmd +++ b/eng/install-emscripten.cmd @@ -5,14 +5,14 @@ git clone https://github.com/emscripten-core/emsdk.git cd emsdk rem checkout a known good version to avoid a random break when emscripten changes the top of tree. -git checkout c1f0ad9 +git checkout def6e49 powershell -NoProfile -NoLogo -ExecutionPolicy ByPass -command "& """%~dp0update-machine-certs.ps1""" %*" rem Use the python that is downloaded to native-tools explicitly as its not on the path -call "%1"\..\native-tools\bin\python3 emsdk.py install 2.0.0 +call "%1"\..\native-tools\bin\python3 emsdk.py install 2.0.3 if %errorlevel% NEQ 0 goto fail -call emsdk activate 2.0.0 +call emsdk activate 2.0.3 if %errorlevel% NEQ 0 goto fail exit /b 0 diff --git a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs index 7c8d18f35..6adae81a0 100644 --- a/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs +++ b/src/ILCompiler.WebAssembly/src/CodeGen/ILToWebAssemblyImporter.cs @@ -4539,13 +4539,25 @@ namespace Internal.IL void ThrowOrRethrow(StackEntry exceptionObject) { + int offset = GetTotalParameterOffset() + GetTotalLocalOffset(); + LLVMValueRef shadowStack = _builder.BuildGEP(_currentFunclet.GetParam(0), + new LLVMValueRef[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (uint)offset, false) }, + String.Empty); + LLVMValueRef exSlot = _builder.BuildBitCast(shadowStack, LLVMTypeRef.CreatePointer(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), 0)); + _builder.BuildStore(exceptionObject.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder), exSlot); + LLVMValueRef[] llvmArgs = new LLVMValueRef[] { shadowStack }; + MetadataType helperType = _compilation.TypeSystemContext.SystemModule.GetKnownType("System", "Exception"); + MethodDesc helperMethod = helperType.GetKnownMethod("DispatchExWasm", null); + LLVMValueRef fn = LLVMFunctionForMethod(helperMethod, helperMethod, null, false, null, null, out bool hasHiddenParam, out LLVMValueRef dictPtrPtrStore, out LLVMValueRef fatFunctionPtr); + ExceptionRegion currentExceptionRegion = GetCurrentTryRegion(); + _builder.BuildCall(fn, llvmArgs, string.Empty); + if (RhpThrowEx.Handle.Equals(IntPtr.Zero)) { RhpThrowEx = Module.AddFunction("RhpThrowEx", LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, new LLVMTypeRef[] { LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0) }, false)); } LLVMValueRef[] args = new LLVMValueRef[] { exceptionObject.ValueAsType(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), _builder) }; - ExceptionRegion currentExceptionRegion = GetCurrentTryRegion(); if (currentExceptionRegion == null) { _builder.BuildCall(RhpThrowEx, args, ""); diff --git a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj index cada8b763..720ba5bdd 100644 --- a/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -239,6 +239,7 @@ + diff --git a/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs b/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs index 75106ffe7..e7c61d53b 100644 --- a/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs +++ b/src/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreRT.Wasm.cs @@ -22,10 +22,11 @@ namespace System.Diagnostics // at S_P_CoreLib_System_Diagnostics_StackTrace__InitializeForCurrentThread (wasm-function[12314]:275) // at S_P_CoreLib_System_Diagnostics_StackTrace___ctor_0(wasm-function[12724]:118) skipFrames += 2; // METHODS_TO_SKIP is a constant so just change here + // at S_P_CoreLib_System_Exception__DispatchExWasm(wasm-function[2360]:39) fixed (byte* curChar = backtraceBuffer) { - callstackLen = emscripten_get_callstack(8 /* original source stack if source maps available, not tested */, curChar, backtraceBuffer.Length); + callstackLen = emscripten_get_callstack(0, curChar, backtraceBuffer.Length); } int _numOfFrames = 1; int lineStartIx = 0; diff --git a/src/System.Private.CoreLib/src/System/Exception.CoreRT.Wasm.cs b/src/System.Private.CoreLib/src/System/Exception.CoreRT.Wasm.cs new file mode 100644 index 000000000..51ba580a4 --- /dev/null +++ b/src/System.Private.CoreLib/src/System/Exception.CoreRT.Wasm.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System +{ + public partial class Exception + { + [DllImport("*")] + internal static extern void RhpThrowEx(object exception); + + private static void DispatchExWasm(object exception) + { + AppendExceptionStackFrameWasm(exception, new StackTrace(1).ToString()); + //RhpThrowEx(exception); can't as not handling the transition unmanaged->managed in the landing pads. + } + + private static void AppendExceptionStackFrameWasm(object exceptionObj, string stackTraceString) + { + // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions + // back into the dispatcher. + try + { + Exception ex = exceptionObj as Exception; + if (ex == null) + Environment.FailFast("Exceptions must derive from the System.Exception class"); + + if (!RuntimeExceptionHelpers.SafeToPerformRichExceptionSupport) + return; + + ex._stackTraceString = stackTraceString.Replace("__", ".").Replace("_", "."); + } + catch + { + // We may end up with a confusing stack trace or a confusing ETW trace log, but at least we + // can continue to dispatch this exception. + } + } + } +} diff --git a/tests/src/Simple/Exceptions/Exceptions.cmd b/tests/src/Simple/Exceptions/Exceptions.cmd index 2167b8ecf..56440d683 100644 --- a/tests/src/Simple/Exceptions/Exceptions.cmd +++ b/tests/src/Simple/Exceptions/Exceptions.cmd @@ -1,6 +1,12 @@ @echo off setlocal -"%1\%2" + +IF /i "%__Mode%"=="wasm" ( + call %EMSDK_NODE% "%1\%2" +) ELSE ( + "%1\%2" +) + set ErrorCode=%ERRORLEVEL% IF "%ErrorCode%"=="100" ( echo %~n0: pass diff --git a/tests/src/Simple/Exceptions/Exceptions.cs b/tests/src/Simple/Exceptions/Exceptions.cs index ea8bc3f27..1ca7a7686 100644 --- a/tests/src/Simple/Exceptions/Exceptions.cs +++ b/tests/src/Simple/Exceptions/Exceptions.cs @@ -3,6 +3,10 @@ using System; using System.Text; +#if CODEGEN_WASM +using System.Runtime.InteropServices; +using Console=BringUpTest.Console; +#endif public class BringUpTest { @@ -53,7 +57,11 @@ public class BringUpTest } string stackTrace = e.StackTrace; +#if CODEGEN_WASM && !DEBUG //Wasm doesn't get useful names in release mode, e.g. it gets at wasm-function[10259]:0x4b5182 + if (!stackTrace.Contains("wasm-function")) +#else if (!stackTrace.Contains("BringUpTest.Main")) +#endif { Console.WriteLine("Unexpected stack trace: " + stackTrace); return Fail; @@ -198,5 +206,45 @@ public class BringUpTest CreateSomeGarbage(); return true; } + +#if CODEGEN_WASM + internal class Console + { + private static unsafe void PrintString(string s) + { + int length = s.Length; + fixed (char* curChar = s) + { + for (int i = 0; i < length; i++) + { + TwoByteStr curCharStr = new TwoByteStr(); + curCharStr.first = (byte)(*(curChar + i)); + printf((byte*)&curCharStr, null); + } + } + } + + internal static void WriteLine(string s) + { + PrintString(s); + PrintString("\n"); + } + + internal static void WriteLine(string format, string p) + { + PrintString(string.Format(format, p)); + PrintString("\n"); + } + } + + struct TwoByteStr + { + public byte first; + public byte second; + } + + [DllImport("*")] + private static unsafe extern int printf(byte* str, byte* unused); +#endif } diff --git a/tests/src/Simple/Exceptions/wasm b/tests/src/Simple/Exceptions/wasm new file mode 100644 index 000000000..e69de29bb