diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 08d58deb0ab7bc..aaede3419780fd 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -858,6 +858,7 @@ enum ReadyToRunHelper READYTORUN_HELPER_FailFast = 0x24, READYTORUN_HELPER_ThrowNullRef = 0x25, READYTORUN_HELPER_ThrowDivZero = 0x26, + READYTORUN_HELPER_ThrowExact = 0x27, // Write barriers READYTORUN_HELPER_WriteBarrier = 0x30, diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs index e77eb6409cce51..24378e6a408534 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs @@ -229,6 +229,14 @@ public void CaptureContexts() [ThreadStatic] private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState; +#if !NATIVEAOT + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AsyncHelpers_AddContinuationToExInternal")] + private static unsafe partial void AddContinuationToExInternal(void* diagnosticIP, ObjectHandleOnStack ex); + + internal static unsafe void AddContinuationToExInternal(void* diagnosticIP, Exception e) + => AddContinuationToExInternal(diagnosticIP, ObjectHandleOnStack.Create(ref e)); +#endif + private static unsafe Continuation AllocContinuation(Continuation prevContinuation, MethodTable* contMT) { #if NATIVEAOT @@ -436,6 +444,7 @@ internal void HandleSuspended() } } + [StackTraceHidden] private unsafe void DispatchContinuations() { ExecutionAndSyncBlockStore contexts = default; @@ -469,7 +478,7 @@ private unsafe void DispatchContinuations() } catch (Exception ex) { - Continuation? handlerContinuation = UnwindToPossibleHandler(asyncDispatcherInfo.NextContinuation); + Continuation? handlerContinuation = UnwindToPossibleHandler(asyncDispatcherInfo.NextContinuation, ex); if (handlerContinuation == null) { // Tail of AsyncTaskMethodBuilderT.SetException @@ -520,10 +529,19 @@ private unsafe void DispatchContinuations() private ref byte GetResultStorage() => ref Unsafe.As(ref m_result); - private static Continuation? UnwindToPossibleHandler(Continuation? continuation) + private static unsafe Continuation? UnwindToPossibleHandler(Continuation? continuation, Exception ex) { while (true) { + if (continuation != null && continuation.ResumeInfo != null && continuation.ResumeInfo->DiagnosticIP != null) + { +#if !NATIVEAOT + AddContinuationToExInternal(continuation.ResumeInfo->DiagnosticIP, ex); +#else + IntPtr ip = (IntPtr)continuation.ResumeInfo->DiagnosticIP; + System.Exception.AppendExceptionStackFrame(ex, ip, 0); +#endif + } if (continuation == null || (continuation.Flags & ContinuationFlags.HasException) != 0) return continuation; @@ -769,12 +787,14 @@ private static void CaptureContinuationContext(ref object continuationContext, r flags |= ContinuationFlags.ContinueOnThreadPool; } + [StackTraceHidden] internal static T CompletedTaskResult(Task task) { TaskAwaiter.ValidateEnd(task); return task.ResultOnSuccess; } + [StackTraceHidden] internal static void CompletedTask(Task task) { TaskAwaiter.ValidateEnd(task); diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 7057132b460fac..8599bbdd55a2f4 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -20,7 +20,7 @@ // If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION` // and handle pending work. #define READYTORUN_MAJOR_VERSION 18 -#define READYTORUN_MINOR_VERSION 0x0000 +#define READYTORUN_MINOR_VERSION 0x0001 #define MINIMUM_READYTORUN_MAJOR_VERSION 18 @@ -337,6 +337,7 @@ enum ReadyToRunHelper READYTORUN_HELPER_FailFast = 0x24, READYTORUN_HELPER_ThrowNullRef = 0x25, READYTORUN_HELPER_ThrowDivZero = 0x26, + READYTORUN_HELPER_ThrowExact = 0x27, // Write barriers READYTORUN_HELPER_WriteBarrier = 0x30, diff --git a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S index d2bce874cecaca..f47a2d64fa3b14 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S @@ -78,6 +78,24 @@ ALTERNATE_ENTRY RhpThrowHwEx2 NESTED_END RhpThrowHwEx, _TEXT +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowExact +// +// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +// +// INPUT: RDI: exception object +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LEAF_ENTRY RhpThrowExact, _TEXT + + mov r8d, 4 // r8d = ExKind.RethrowFlag + jmp RhpThrowExImpl + +LEAF_END RhpThrowExact, _TEXT + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RhpThrowEx @@ -89,6 +107,10 @@ NESTED_END RhpThrowHwEx, _TEXT ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + mov r8d, 1 // r8d = ExKind.Throw + +ALTERNATE_ENTRY RhpThrowExImpl + STACKSIZEOF_ExInfo = ((SIZEOF__ExInfo + 15) & (~ 15)) rsp_offsetof_Context = STACKSIZEOF_ExInfo @@ -136,7 +158,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler mov [rsi + OFFSETOF__ExInfo__m_exception], rdx // init the exception object to null mov byte ptr [rsi + OFFSETOF__ExInfo__m_passNumber], 1 // init to the first pass mov dword ptr [rsi + OFFSETOF__ExInfo__m_idxCurClause], 0xFFFFFFFF - mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], 1 // ExKind.Throw + mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], r8b // ExKind (from r8b) // link the ExInfo into the thread's ExInfo chain mov rdx, [rax + OFFSETOF__Thread__m_pExInfoStackHead] diff --git a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm index 0e63c8808439fd..1c89226ff461d4 100644 --- a/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm @@ -105,6 +105,24 @@ ALTERNATE_ENTRY RhpThrowHwEx2 NESTED_END RhpThrowHwEx, _TEXT +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowExact +;; +;; SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +;; +;; INPUT: RCX: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +LEAF_ENTRY RhpThrowExact, _TEXT + + mov r9d, 4 ;; r9d = ExKind.RethrowFlag + jmp RhpThrowExImpl + +LEAF_END RhpThrowExact, _TEXT + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpThrowEx @@ -116,6 +134,10 @@ NESTED_END RhpThrowHwEx, _TEXT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NESTED_ENTRY RhpThrowEx, _TEXT + mov r9d, 1 ;; r9d = ExKind.Throw + +RhpThrowExImpl:: + SIZEOF_XmmSaves equ SIZEOF__PAL_LIMITED_CONTEXT - OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6 STACKSIZEOF_ExInfo equ ((SIZEOF__ExInfo + 15) AND (NOT 15)) @@ -172,7 +194,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT ;; address could have been hijacked when we were in that C# code and we must remove the hijack and ;; reflect the correct return address in our exception context record. The other throw helpers don't ;; need this because they cannot be tail-called from C#. - INLINE_THREAD_UNHIJACK rax, r9, rdx ;; trashes R9, RDX + INLINE_THREAD_UNHIJACK rax, r10, rdx ;; trashes R10, RDX mov rdx, [rbx] ;; rdx <- return address mov [rsp + rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], rdx ;; set 'faulting' IP after unhijack @@ -181,7 +203,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT mov [rdx + OFFSETOF__ExInfo__m_exception], r8 ;; init the exception object to null mov byte ptr [rdx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass mov dword ptr [rdx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh - mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], 1 ;; ExKind.Throw + mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], r9b ;; ExKind (from r9b) ;; link the ExInfo into the thread's ExInfo chain mov r8, [rax + OFFSETOF__Thread__m_pExInfoStackHead] diff --git a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S index 9be4b24b7a2305..df23a3cde87c59 100644 --- a/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S @@ -79,6 +79,24 @@ GLOBAL_LABEL RhpThrowHwEx2 NESTED_END RhpThrowHwEx +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// RhpThrowExact +// +// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +// +// INPUT: R0: exception object +// +// OUTPUT: +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LEAF_ENTRY RhpThrowExact, _TEXT + + mov r2, #4 // r2 = ExKind.RethrowFlag + b RhpThrowExImpl + +LEAF_END RhpThrowExact, _TEXT + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RhpThrowEx @@ -90,6 +108,10 @@ NESTED_END RhpThrowHwEx ////////////////////////////////////////////////////////////////////////////////////////////////////////////// NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + mov r2, #1 // r2 = ExKind.Throw + +ALTERNATE_ENTRY RhpThrowExImpl + // Setup a PAL_LIMITED_CONTEXT on the stack { PROLOG_VPUSH {d8-d15} PROLOG_PUSH "{r0,lr}" // Reserve space for SP and store LR @@ -106,8 +128,6 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler // r0 = GetThread() INLINE_GETTHREAD - add r2, sp, #(rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8) // r2 <- addr of return address - // There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return // address could have been hijacked when we were in that C# code and we must remove the hijack and // reflect the correct return address in our exception context record. The other throw helpers don't @@ -158,9 +178,9 @@ LOCAL_LABEL(NotHiJacked): str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null mov r3, #1 strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // init to the first pass - strb r3, [r1, #OFFSETOF__ExInfo__m_kind] + strb r2, [r1, #OFFSETOF__ExInfo__m_kind] // ExKind (from r2) mov r3, #0xFFFFFFFF - str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // ExKind.Throw + str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // link the ExInfo into the thread's ExInfo chain ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead] diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S index 5c992615f66637..4bf1cdf6a7230b 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S @@ -252,6 +252,23 @@ NESTED_END RhpThrowHwEx, _TEXT +// +// RhpThrowExact +// +// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +// +// INPUT: X0: exception object +// +// OUTPUT: +// + + LEAF_ENTRY RhpThrowExact, _TEXT + + mov w4, #4 // w4 = ExKind.RethrowFlag + b RhpThrowExImpl + + LEAF_END RhpThrowExact, _TEXT + // // RhpThrowEx // @@ -262,6 +279,10 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + mov w4, #1 // w4 = ExKind.Throw + +ALTERNATE_ENTRY RhpThrowExImpl + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION GetThreadX2 @@ -316,8 +337,7 @@ LOCAL_LABEL(NotHijacked): strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1 mov w3, #0xFFFFFFFF str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx - mov w3, #1 - strb w3, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.Throw + strb w4, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind (from w4) // link the ExInfo into the thread's ExInfo chain ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] @@ -338,7 +358,6 @@ LOCAL_LABEL(NotHijacked): EMIT_BREAKPOINT NESTED_END RhpThrowEx, _TEXT - // // void FASTCALL RhpRethrow() // diff --git a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm index 15fe87b22fc358..6bc32d7869f93e 100644 --- a/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.asm @@ -250,6 +250,24 @@ NESTED_END RhpThrowHwEx +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowExact +;; +;; SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +;; +;; INPUT: X0: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + LEAF_ENTRY RhpThrowExact + + mov w4, #4 ;; w4 = ExKind.RethrowFlag + b RhpThrowExImpl + + LEAF_END RhpThrowExact + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpThrowEx @@ -261,6 +279,10 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; NESTED_ENTRY RhpThrowEx + mov w4, #1 ;; w4 = ExKind.Throw + +RhpThrowExImpl + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION ;; x2 = GetThread(), TRASHES x1 @@ -317,8 +339,7 @@ NotHijacked strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] ;; pExInfo->m_passNumber = 1 mov w3, #0xFFFFFFFF str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] ;; pExInfo->m_idxCurClause = MaxTryRegionIdx - mov w3, #1 - strb w3, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind.Throw + strb w4, [x1, #OFFSETOF__ExInfo__m_kind] ;; pExInfo->m_kind = ExKind (from w4) ;; link the ExInfo into the thread's ExInfo chain ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead] diff --git a/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm index 810058235b7d32..11416fd0971376 100644 --- a/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm +++ b/src/coreclr/nativeaot/Runtime/i386/ExceptionHandling.asm @@ -89,6 +89,24 @@ ALTERNATE_ENTRY _RhpThrowHwEx2 FASTCALL_ENDFUNC +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; RhpThrowExact +;; +;; SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +;; +;; INPUT: ECX: exception object +;; +;; OUTPUT: +;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +FASTCALL_FUNC RhpThrowExact, 4 + + mov edx, 4 ;; edx = ExKind.RethrowFlag + jmp RhpThrowExImpl + +FASTCALL_ENDFUNC + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; RhpThrowEx @@ -100,6 +118,9 @@ FASTCALL_ENDFUNC ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; FASTCALL_FUNC RhpThrowEx, 4 + mov edx, 1 ;; edx = ExKind.Throw + +RhpThrowExImpl:: esp_offsetof_ExInfo textequ %0 esp_offsetof_Context textequ %SIZEOF__ExInfo @@ -107,7 +128,6 @@ FASTCALL_FUNC RhpThrowEx, 4 mov ebp, esp lea eax, [esp+8] ;; calculate the RSP of the throw site - mov edx, [esp+4] ;; get the throw site IP via the return address ;; struct PAL_LIMITED_CONTEXT ;; { @@ -118,42 +138,44 @@ FASTCALL_FUNC RhpThrowEx, 4 mov ebx, [ebp] push ebx ;; 'faulting' Rbp push eax ;; 'faulting' Rsp - push edx ;; 'faulting' IP + mov eax, [ebp+4] ;; get the throw site IP via the return address + push eax ;; 'faulting' IP ;; }; sub esp, SIZEOF__ExInfo ;; ------------------------- - lea ebx, [eax-4] ;; ebx <- addr of return address - INLINE_GETTHREAD eax, edx ;; eax <- thread, edx <- trashed + lea ebx, [ebp+4] ;; ebx <- addr of return address + INLINE_GETTHREAD eax, edi ;; eax <- thread, edi <- trashed ;; There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return ;; address could have been hijacked when we were in that C# code and we must remove the hijack and ;; reflect the correct return address in our exception context record. The other throw helpers don't ;; need this because they cannot be tail-called from C#. - INLINE_THREAD_UNHIJACK eax, esi, edx ;; trashes esi, edx + INLINE_THREAD_UNHIJACK eax, esi, edi ;; trashes esi, edi - mov edx, [ebx] ;; edx <- return address - mov [esp + esp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], edx ;; set 'faulting' IP after unhijack + mov edi, [ebx] ;; edi <- return address + mov [esp + esp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], edi ;; set 'faulting' IP after unhijack - lea edx, [esp + esp_offsetof_ExInfo] ;; edx <- ExInfo* + lea edi, [esp + esp_offsetof_ExInfo] ;; edi <- ExInfo* xor esi, esi - mov [edx + OFFSETOF__ExInfo__m_exception], esi ;; init the exception object to null - mov byte ptr [edx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass - mov dword ptr [edx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh - mov byte ptr [edx + OFFSETOF__ExInfo__m_kind], 1 ;; ExKind.Throw + mov [edi + OFFSETOF__ExInfo__m_exception], esi ;; init the exception object to null + mov byte ptr [edi + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass + mov dword ptr [edi + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh + mov byte ptr [edi + OFFSETOF__ExInfo__m_kind], dl ;; ExKind ;; link the ExInfo into the thread's ExInfo chain mov ebx, [eax + OFFSETOF__Thread__m_pExInfoStackHead] - mov [edx + OFFSETOF__ExInfo__m_pPrevExInfo], ebx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead - mov [eax + OFFSETOF__Thread__m_pExInfoStackHead], edx ;; m_pExInfoStackHead = pExInfo + mov [edi + OFFSETOF__ExInfo__m_pPrevExInfo], ebx ;; pExInfo->m_pPrevExInfo = m_pExInfoStackHead + mov [eax + OFFSETOF__Thread__m_pExInfoStackHead], edi ;; m_pExInfoStackHead = pExInfo ;; set the exception context field on the ExInfo lea ebx, [esp + esp_offsetof_Context] ;; ebx <- PAL_LIMITED_CONTEXT* - mov [edx + OFFSETOF__ExInfo__m_pExContext], ebx ;; init ExInfo.m_pExContext + mov [edi + OFFSETOF__ExInfo__m_pExContext], ebx ;; init ExInfo.m_pExContext + mov edx, edi ;; ecx still contains the exception object ;; edx contains the address of the ExInfo diff --git a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h index fdfb525c675b14..13eeb69f898ec5 100644 --- a/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h +++ b/src/coreclr/nativeaot/Runtime/inc/ModuleHeaders.h @@ -12,7 +12,7 @@ struct ReadyToRunHeaderConstants static const uint32_t Signature = 0x00525452; // 'RTR' static const uint32_t CurrentMajorVersion = 18; - static const uint32_t CurrentMinorVersion = 0; + static const uint32_t CurrentMinorVersion = 1; }; struct ReadyToRunHeader diff --git a/src/coreclr/nativeaot/Runtime/loongarch64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/loongarch64/ExceptionHandling.S index 93f2afd3ea786b..571287253939b8 100644 --- a/src/coreclr/nativeaot/Runtime/loongarch64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/loongarch64/ExceptionHandling.S @@ -268,6 +268,23 @@ NESTED_END RhpThrowHwEx, _TEXT +// +// RhpThrowExact +// +// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +// +// INPUT: a0: exception object +// +// OUTPUT: +// + + LEAF_ENTRY RhpThrowExact, _TEXT + + ori $a4, $zero, 4 // a4 = ExKind.RethrowFlag + b RhpThrowExImpl + + LEAF_END RhpThrowExact, _TEXT + // // RhpThrowEx // @@ -278,6 +295,10 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + ori $a4, $zero, 1 // a4 = ExKind.Throw + +ALTERNATE_ENTRY RhpThrowExImpl + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION GetThreadA2 @@ -331,8 +352,7 @@ LOCAL_LABEL(NotHijacked): st.b $a3, $a1, OFFSETOF__ExInfo__m_passNumber // pExInfo->m_passNumber = 1 addi.w $a3, $zero, -1 st.w $a3, $a1, OFFSETOF__ExInfo__m_idxCurClause // pExInfo->m_idxCurClause = MaxTryRegionIdx - ori $a3, $zero, 1 - st.b $a3, $a1, OFFSETOF__ExInfo__m_kind // pExInfo->m_kind = ExKind.Throw + st.b $a4, $a1, OFFSETOF__ExInfo__m_kind // pExInfo->m_kind = ExKind (from a4) // link the ExInfo into the thread's ExInfo chain ld.d $a3, $a2, OFFSETOF__Thread__m_pExInfoStackHead @@ -353,7 +373,6 @@ LOCAL_LABEL(NotHijacked): EMIT_BREAKPOINT NESTED_END RhpThrowEx, _TEXT - // // void FASTCALL RhpRethrow() // diff --git a/src/coreclr/nativeaot/Runtime/riscv64/ExceptionHandling.S b/src/coreclr/nativeaot/Runtime/riscv64/ExceptionHandling.S index 15aed3af521ea4..934c97fb088600 100644 --- a/src/coreclr/nativeaot/Runtime/riscv64/ExceptionHandling.S +++ b/src/coreclr/nativeaot/Runtime/riscv64/ExceptionHandling.S @@ -303,6 +303,23 @@ NESTED_END RhpThrowHwEx, _TEXT +// +// RhpThrowExact +// +// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag +// +// INPUT: a0: exception object +// +// OUTPUT: +// + + LEAF_ENTRY RhpThrowExact, _TEXT + + li a4, 4 // a4 = ExKind.RethrowFlag + j RhpThrowExImpl + + LEAF_END RhpThrowExact, _TEXT + // // RhpThrowEx // @@ -313,6 +330,10 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler + li a4, 1 // a4 = ExKind.Throw + +ALTERNATE_ENTRY RhpThrowExImpl + ALLOC_THROW_FRAME SOFTWARE_EXCEPTION GetThreadA2 @@ -328,7 +349,7 @@ // Normal case where a valid return address location is hijacked sd a1, 0(a3) - tail LOCAL_LABEL(ClearThreadState) + j LOCAL_LABEL(ClearThreadState) LOCAL_LABEL(TailCallWasHijacked): @@ -357,8 +378,7 @@ LOCAL_LABEL(NotHijacked): sb a3, OFFSETOF__ExInfo__m_passNumber(a1) // pExInfo->m_passNumber = 1 addiw a3, zero, -1 sw a3, OFFSETOF__ExInfo__m_idxCurClause(a1) // pExInfo->m_idxCurClause = MaxTryRegionIdx - li a3, 1 - sb a3, OFFSETOF__ExInfo__m_kind(a1) // pExInfo->m_kind = ExKind.Throw + sb a4, OFFSETOF__ExInfo__m_kind(a1) // pExInfo->m_kind = ExKind (from a4) // Link the ExInfo into the thread's ExInfo chain ld a3, OFFSETOF__Thread__m_pExInfoStackHead(a2) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.NativeAot.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.NativeAot.cs index 9fbfcc9302c562..584866ccae4908 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.NativeAot.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Exception.NativeAot.cs @@ -102,7 +102,7 @@ private enum RhEHFrameType internal static uint GetExceptionCount() => s_exceptionCount; [RuntimeExport("AppendExceptionStackFrame")] - private static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags) + internal static void AppendExceptionStackFrame(object exceptionObj, IntPtr IP, int flags) { // This method is called by the runtime's EH dispatch code and is not allowed to leak exceptions // back into the dispatcher. diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index fe971370ac1a57..0e849fb950ae3e 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -16,7 +16,7 @@ internal struct ReadyToRunHeaderConstants public const uint Signature = 0x00525452; // 'RTR' public const ushort CurrentMajorVersion = 18; - public const ushort CurrentMinorVersion = 0; + public const ushort CurrentMinorVersion = 1; } #if READYTORUN #pragma warning disable 0169 diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 250e316e1d37ce..da985df738d319 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -229,6 +229,7 @@ public enum ReadyToRunHelper FailFast = 0x24, ThrowNullRef = 0x25, ThrowDivZero = 0x26, + ThrowExact = 0x27, // Write barriers WriteBarrier = 0x30, diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs index 3da700e9cd5234..ca46321c4759ef 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/JitHelper.cs @@ -28,6 +28,9 @@ public static void GetEntryPoint(TypeSystemContext context, ReadyToRunHelper id, case ReadyToRunHelper.Rethrow: mangledName = "RhpRethrow"; break; + case ReadyToRunHelper.ThrowExact: + mangledName = "RhpThrowExact"; + break; case ReadyToRunHelper.Overflow: methodDesc = context.GetHelperEntryPoint("ThrowHelpers"u8, "ThrowOverflowException"u8); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs index 256d9a81c53230..0b61b624b25236 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/StackTraceEmissionPolicy.cs @@ -40,12 +40,14 @@ public override MethodStackTraceVisibilityFlags GetMethodVisibility(MethodDesc m MethodStackTraceVisibilityFlags result = _flags; if (method.HasCustomAttribute("System.Diagnostics", "StackTraceHiddenAttribute") - || (method.OwningType is MetadataType mdType && mdType.HasCustomAttribute("System.Diagnostics", "StackTraceHiddenAttribute"))) + || (method.OwningType is MetadataType mdType && mdType.HasCustomAttribute("System.Diagnostics", "StackTraceHiddenAttribute")) + || (method is Internal.IL.Stubs.ILStubMethod) + || method.IsAsyncThunk()) // see MethodDesc::IsDiagnosticsHidden() in src/coreclr/vm/method.inl { result |= MethodStackTraceVisibilityFlags.IsHidden; } - return method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod + return (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod || (method.IsAsync && method.IsAsyncCall())) ? result | MethodStackTraceVisibilityFlags.HasMetadata : result; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index ad04e7f202c1be..addb3a67c196f2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -987,6 +987,9 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) case CorInfoHelpFunc.CORINFO_HELP_RETHROW: id = ReadyToRunHelper.Rethrow; break; + case CorInfoHelpFunc.CORINFO_HELP_THROWEXACT: + id = ReadyToRunHelper.ThrowExact; + break; case CorInfoHelpFunc.CORINFO_HELP_OVERFLOW: id = ReadyToRunHelper.Overflow; break; diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index 6cf54e7854c1d5..1b5eb5d6fc0bf7 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -1639,6 +1639,10 @@ private void ParseHelper(StringBuilder builder) builder.Append("RETHROW"); break; + case ReadyToRunHelper.ThrowExact: + builder.Append("THROW_EXACT"); + break; + case ReadyToRunHelper.Overflow: builder.Append("OVERFLOW"); break; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index c64ff0f1b49699..c9dfa56bff3846 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -467,12 +467,14 @@ private ISymbolNode GetHelperFtnUncached(CorInfoHelpFunc ftnNum) switch (ftnNum) { case CorInfoHelpFunc.CORINFO_HELP_THROW: - case CorInfoHelpFunc.CORINFO_HELP_THROWEXACT: // TODO: (async): THROWEXACT id = ReadyToRunHelper.Throw; break; case CorInfoHelpFunc.CORINFO_HELP_RETHROW: id = ReadyToRunHelper.Rethrow; break; + case CorInfoHelpFunc.CORINFO_HELP_THROWEXACT: + id = ReadyToRunHelper.ThrowExact; + break; case CorInfoHelpFunc.CORINFO_HELP_USER_BREAKPOINT: id = ReadyToRunHelper.DebugBreak; break; diff --git a/src/coreclr/vm/clrex.h b/src/coreclr/vm/clrex.h index 353804cb71d196..01fbce5df1a1c7 100644 --- a/src/coreclr/vm/clrex.h +++ b/src/coreclr/vm/clrex.h @@ -30,6 +30,7 @@ enum StackTraceElementFlags // Set if the element references a method that needs a keep alive object STEF_KEEPALIVE = 0x0004, + STEF_CONTINUATION = 0x0008, }; // This struct is used by SOS in the diagnostic repo. @@ -69,8 +70,10 @@ class StackTraceInfo static OBJECTREF GetKeepAliveObject(MethodDesc* pMethod); static void EnsureStackTraceArray(StackTraceArrayProtect *pStackTraceArrayProtected, size_t neededSize); static void EnsureKeepAliveArray(PTRARRAYREF *ppKeepAliveArray, size_t neededSize); + static void AppendElementImpl(OBJECTREF pThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf, Thread* pThread, BOOL fRaisingForeignException); public: static void AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf); + static void AppendElement(OBJECTREF pThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf); }; diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index 1ca995a36eec40..bfce067a851101 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -68,6 +68,8 @@ #include #include #include +#include +#include #include diff --git a/src/coreclr/vm/debugdebugger.cpp b/src/coreclr/vm/debugdebugger.cpp index 1d09d0d1a6b797..cd33e4f168871f 100644 --- a/src/coreclr/vm/debugdebugger.cpp +++ b/src/coreclr/vm/debugdebugger.cpp @@ -284,6 +284,32 @@ static void GetStackFrames(DebugStackTrace::GetStackFramesData *pData) } } +extern "C" void QCALLTYPE AsyncHelpers_AddContinuationToExInternal( + void* diagnosticIP, + QCall::ObjectHandleOnStack exception) +{ + QCALL_CONTRACT; + + BEGIN_QCALL; + + GCX_COOP(); + + OBJECTREF pException = (OBJECTREF)exception.Get(); + _ASSERTE(pException != NULL); + // populate exception with information from the continuation object + EECodeInfo codeInfo((PCODE)diagnosticIP); + _ASSERTE(codeInfo.IsValid()); + MethodDesc* methodDesc = codeInfo.GetMethodDesc(); + StackTraceInfo::AppendElement( + pException, + (UINT_PTR)diagnosticIP, + 0, + methodDesc, + NULL); + + END_QCALL; +} + extern "C" void QCALLTYPE StackTrace_GetStackFramesInternal( QCall::ObjectHandleOnStack stackFrameHelper, BOOL fNeedFileInfo, @@ -951,25 +977,37 @@ void DebugStackTrace::GetStackFramesFromException(OBJECTREF * e, // push frames and the method body is therefore non-contiguous. // Currently such methods always return an IP of 0, so they're easy // to spot. - DWORD dwNativeOffset; - + DWORD dwNativeOffset = 0; UINT_PTR ip = cur.ip; -#if defined(DACCESS_COMPILE) && defined(TARGET_AMD64) - // Compensate for a bug in the old EH that for a frame that faulted - // has the ip pointing to an address before the faulting instruction - if ((i == 0) && ((cur.flags & STEF_IP_ADJUSTED) == 0)) + if (cur.flags & STEF_CONTINUATION) { - ip -= 1; - } -#endif // DACCESS_COMPILE && TARGET_AMD64 - if (ip) - { - EECodeInfo codeInfo(ip); - dwNativeOffset = codeInfo.GetRelOffset(); + EECodeInfo codeInfo((PCODE)ip); + if (codeInfo.IsValid()) + { + PCODE startAddress = codeInfo.GetStartAddress(); + dwNativeOffset = (DWORD)(ip - startAddress); + } } + else { - dwNativeOffset = 0; +#if defined(DACCESS_COMPILE) && defined(TARGET_AMD64) + // Compensate for a bug in the old EH that for a frame that faulted + // has the ip pointing to an address before the faulting instruction + if ((i == 0) && ((cur.flags & STEF_IP_ADJUSTED) == 0)) + { + ip -= 1; + } +#endif // DACCESS_COMPILE && TARGET_AMD64 + if (ip) + { + EECodeInfo codeInfo(ip); + dwNativeOffset = codeInfo.GetRelOffset(); + } + else + { + dwNativeOffset = 0; + } } pData->pElements[i].InitPass1( @@ -1554,7 +1592,7 @@ void DebugStackTrace::Element::InitPass2() bool bRes = false; - bool fAdjustOffset = (this->flags & STEF_IP_ADJUSTED) == 0 && this->dwOffset > 0; + bool fAdjustOffset = (this->flags & STEF_IP_ADJUSTED) == 0 && this->dwOffset > 0 && !(this->flags & STEF_CONTINUATION); // Check the cache! uint32_t dwILOffsetFromCache; diff --git a/src/coreclr/vm/debugdebugger.h b/src/coreclr/vm/debugdebugger.h index d2bdb47c070f19..8cd17439db17cc 100644 --- a/src/coreclr/vm/debugdebugger.h +++ b/src/coreclr/vm/debugdebugger.h @@ -135,6 +135,10 @@ extern "C" void QCALLTYPE StackTrace_GetStackFramesInternal( BOOL fNeedFileInfo, QCall::ObjectHandleOnStack exception); +extern "C" void QCALLTYPE AsyncHelpers_AddContinuationToExInternal( + void* diagnosticIP, + QCall::ObjectHandleOnStack exception); + extern "C" MethodDesc* QCALLTYPE StackFrame_GetMethodDescFromNativeIP(LPVOID ip); diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index a324e4d83041e8..11e28f890bb693 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -46,6 +46,7 @@ #endif // HAVE_GCCOVER #include "exinfo.h" +#include "exkind.h" //---------------------------------------------------------------------------- // @@ -2617,7 +2618,7 @@ OBJECTREF StackTraceInfo::GetKeepAliveObject(MethodDesc* pMethod) } // -// Append stack frame to an exception stack trace. +// Append stack frame to an exception stack trace - handle version. // void StackTraceInfo::AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf) { @@ -2642,20 +2643,63 @@ void StackTraceInfo::AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP, LOG((LF_EH, LL_INFO10000, "StackTraceInfo::AppendElement IP = %p, SP = %p, %s::%s\n", currentIP, currentSP, pFunc ? pFunc->m_pszDebugClassName : "", pFunc ? pFunc->m_pszDebugMethodName : "" )); - if ((pFunc != NULL && pFunc->IsDiagnosticsHidden())) - return; - // Do not save stacktrace to preallocated exception. These are shared. if (CLRException::IsPreallocatedExceptionHandle(hThrowable)) { - // Preallocated exceptions will never have this flag set. However, its possible - // that after this flag is set for a regular exception but before we throw, we have an async - // exception like a RudeThreadAbort, which will replace the exception - // containing the restored stack trace. - return; } + AppendElementImpl(ObjectFromHandle(hThrowable), currentIP, currentSP, pFunc, pCf, pThread, fRaisingForeignException); +} + +// +// Append stack frame to an exception stack trace - objectref version for runtime-async stack frames. +// +void StackTraceInfo::AppendElement(OBJECTREF pThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf) +{ + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + MODE_COOPERATIVE; + } + CONTRACTL_END + + Thread *pThread = GetThread(); + MethodTable* pMT = pThrowable->GetMethodTable(); + _ASSERTE(IsException(pMT)); + + LOG((LF_EH, LL_INFO10000, "StackTraceInfo::AppendElement IP = %p, SP = %p, %s::%s\n", currentIP, currentSP, pFunc ? pFunc->m_pszDebugClassName : "", pFunc ? pFunc->m_pszDebugMethodName : "" )); + AppendElementImpl(pThrowable, currentIP, currentSP, pFunc, pCf, pThread, FALSE /* fRaisingForeignException */); +} + +// +// Append stack frame to an exception stack trace. +// +void StackTraceInfo::AppendElementImpl(OBJECTREF pThrowable, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf, Thread* pThread, BOOL fRaisingForeignException) +{ + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + MODE_COOPERATIVE; + } + CONTRACTL_END + + if ((pFunc != NULL && pFunc->IsDiagnosticsHidden())) + return; + + struct + { + StackTraceArrayProtect stackTrace; + PTRARRAYREF pKeepAliveArray = NULL; // Object array of Managed Resolvers / Loader Allocators of methods that can be collected + OBJECTREF keepAliveObject = NULL; + EXCEPTIONREF pThrowable = NULL; + } gc; + + GCPROTECT_BEGIN_THREAD(pThread, gc); + gc.pThrowable = (EXCEPTIONREF)pThrowable; + StackTraceElement stackTraceElem; stackTraceElem.pFunc = pFunc; @@ -2683,25 +2727,21 @@ void StackTraceInfo::AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP, stackTraceElem.flags |= STEF_IP_ADJUSTED; } } + else + { + stackTraceElem.flags |= STEF_CONTINUATION; + } #ifndef TARGET_UNIX // Watson is supported on Windows only - SetupWatsonBucket(currentIP, pCf); + if (pCf != NULL) + SetupWatsonBucket(currentIP, pCf); #endif // !TARGET_UNIX EX_TRY { - struct - { - StackTraceArrayProtect stackTrace; - PTRARRAYREF pKeepAliveArray = NULL; // Object array of Managed Resolvers / Loader Allocators of methods that can be collected - OBJECTREF keepAliveObject = NULL; - } gc; - - GCPROTECT_BEGIN_THREAD(pThread, gc); - // Fetch the stacktrace and the keepAlive array from the exception object. It returns clones of those arrays in case the // stack trace was created by a different thread. - ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->GetStackTrace(gc.stackTrace.m_pStackTraceArray, &gc.pKeepAliveArray, pThread); + gc.pThrowable->GetStackTrace(gc.stackTrace.m_pStackTraceArray, &gc.pKeepAliveArray, pThread); // The stack trace returned by the GetStackTrace has to be created by the current thread or be NULL. _ASSERTE((gc.stackTrace.m_pStackTraceArray.Get() == NULL) || (gc.stackTrace.m_pStackTraceArray.GetObjectThread() == pThread)); @@ -2759,23 +2799,23 @@ void StackTraceInfo::AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP, { _ASSERTE(keepAliveItemsCount > 0); gc.pKeepAliveArray->SetAt(0, gc.stackTrace.m_pStackTraceArray.Get()); - ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTrace(dac_cast(gc.pKeepAliveArray)); + gc.pThrowable->SetStackTrace(dac_cast(gc.pKeepAliveArray)); } else { _ASSERTE(keepAliveItemsCount == 0); - ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTrace(dac_cast(gc.stackTrace.m_pStackTraceArray.Get())); + gc.pThrowable->SetStackTrace(dac_cast(gc.stackTrace.m_pStackTraceArray.Get())); } // Clear the _stackTraceString field as it no longer matches the stack trace - ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTraceString(NULL); - - GCPROTECT_END(); // gc + gc.pThrowable->SetStackTraceString(NULL); } EX_CATCH { } EX_END_CATCH + + GCPROTECT_END(); } void UnwindFrameChain(Thread* pThread, LPVOID pvLimitSP) diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index 595589a8c55d2f..014788848d086b 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -1562,7 +1562,7 @@ void NormalizeThrownObject(OBJECTREF *ppThrowable) } } -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pExceptionContext, EXCEPTION_RECORD* pExceptionRecord) +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pExceptionContext, EXCEPTION_RECORD* pExceptionRecord, ExKind exKind /* = ExKind::None */) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -1585,12 +1585,12 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE { newExceptionRecord.ExceptionCode = EXCEPTION_COMPLUS; newExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE; - newExceptionRecord.ExceptionAddress = (void *)(void (*)(OBJECTREF))&DispatchManagedException; + newExceptionRecord.ExceptionAddress = (void *)(void (*)(OBJECTREF, ExKind))&DispatchManagedException; newExceptionRecord.NumberParameters = MarkAsThrownByUs(newExceptionRecord.ExceptionInformation, hr); newExceptionRecord.ExceptionRecord = NULL; } - ExInfo exInfo(pThread, &newExceptionRecord, pExceptionContext, ExKind::Throw); + ExInfo exInfo(pThread, &newExceptionRecord, pExceptionContext, (ExKind)((uint8_t)ExKind::Throw | (uint8_t)exKind)); #ifdef HOST_WINDOWS // On Windows, this enables the possibility to propagate a longjmp across managed frames. Longjmp @@ -1642,7 +1642,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE UNREACHABLE(); } -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable) +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, ExKind exKind /*= ExKind::None*/) { STATIC_CONTRACT_THROWS; STATIC_CONTRACT_GC_TRIGGERS; @@ -1651,7 +1651,7 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable) CONTEXT exceptionContext; ClrCaptureContext(&exceptionContext); - DispatchManagedException(throwable, &exceptionContext); + DispatchManagedException(throwable, &exceptionContext, NULL, exKind); UNREACHABLE(); } diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 9cf69fae0342a3..3abd52c3458b4f 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -9,6 +9,7 @@ #include "eexcp.h" #include "exstatecommon.h" +#include "exkind.h" // This address lies in the NULL pointer partition of the process memory. // Accessing it will result in AV. @@ -28,8 +29,8 @@ CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRe void NormalizeThrownObject(OBJECTREF *ppThrowable); -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, EXCEPTION_RECORD *pExceptionRecord = NULL); -VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable); +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, EXCEPTION_RECORD *pExceptionRecord = NULL, ExKind exKind = ExKind::None); +VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, ExKind exKind = ExKind::None); VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(); VOID DECLSPEC_NORETURN DispatchRethrownManagedException(CONTEXT* pExceptionContext); diff --git a/src/coreclr/vm/exinfo.cpp b/src/coreclr/vm/exinfo.cpp index 88a14de1aee29b..202ec8bf22b4c0 100644 --- a/src/coreclr/vm/exinfo.cpp +++ b/src/coreclr/vm/exinfo.cpp @@ -3,6 +3,7 @@ #include "common.h" #include "exinfo.h" +#include "exkind.h" #include "dbginterface.h" #include "eetoprofinterfacewrapper.inl" diff --git a/src/coreclr/vm/exinfo.h b/src/coreclr/vm/exinfo.h index f6773f023dffff..682e4afee2da8f 100644 --- a/src/coreclr/vm/exinfo.h +++ b/src/coreclr/vm/exinfo.h @@ -37,20 +37,6 @@ struct RhEHClause } }; -enum class ExKind : uint8_t -{ - None = 0, - Throw = 1, - HardwareFault = 2, - KindMask = 3, - - RethrowFlag = 4, - - SupersededFlag = 8, - - InstructionFaultFlag = 0x10 -}; - struct PAL_SEHException; struct LastReportedFuncletInfo diff --git a/src/coreclr/vm/exkind.h b/src/coreclr/vm/exkind.h new file mode 100644 index 00000000000000..6d189d81be3e46 --- /dev/null +++ b/src/coreclr/vm/exkind.h @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// exkind.h +// + +#ifndef HAVE_EXKIND_H +#define HAVE_EXKIND_H + +#include + +//========================================================================== +// Identifies exception kinds. +//========================================================================== +enum class ExKind : uint8_t +{ + None = 0, + Throw = 1, + HardwareFault = 2, + KindMask = 3, + RethrowFlag = 4, + SupersededFlag = 8, + InstructionFaultFlag = 0x10 +}; + +#endif // HAVE_EXKIND_H diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 8672d3b3ca2765..11a455af1d62e5 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -4150,9 +4150,8 @@ do \ if (exception != NULL) { - GetThread()->GetExceptionState()->SetRaisingForeignException(); pInterpreterFrame->SetIsFaulting(true); - DispatchManagedException(exception); + DispatchManagedException(exception, ExKind::RethrowFlag); UNREACHABLE(); } } diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 6861a44f10f366..5ae9402e493f07 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -55,6 +55,7 @@ #include "patchpointinfo.h" #include "exinfo.h" +#include "exkind.h" #include "arraynative.inl" using std::isfinite; @@ -822,7 +823,6 @@ EXTERN_C HCIMPL2(void, IL_ThrowExact_Impl, Object* obj, TransitionBlock* transi ResetCurrentContext(); OBJECTREF oref = ObjectToOBJECTREF(obj); - GetThread()->GetExceptionState()->SetRaisingForeignException(); Thread *pThread = GetThread(); @@ -832,7 +832,7 @@ EXTERN_C HCIMPL2(void, IL_ThrowExact_Impl, Object* obj, TransitionBlock* transi FC_CAN_TRIGGER_GC(); - DispatchManagedException(oref, exceptionFrame.GetContext()); + DispatchManagedException(oref, exceptionFrame.GetContext(), NULL, ExKind::RethrowFlag); FC_CAN_TRIGGER_GC_END(); UNREACHABLE(); diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 3783b73e9ed465..0a03fbce6b82a2 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -188,6 +188,7 @@ static const Entry s_QCall[] = DllImportEntry(RuntimeFieldHandle_GetFieldDataReference) DllImportEntry(UnsafeAccessors_ResolveGenericParamToTypeHandle) DllImportEntry(StackTrace_GetStackFramesInternal) + DllImportEntry(AsyncHelpers_AddContinuationToExInternal) DllImportEntry(StackFrame_GetMethodDescFromNativeIP) DllImportEntry(ModuleBuilder_GetStringConstant) DllImportEntry(ModuleBuilder_GetTypeRef) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 30c1cd022a9d9c..5fe0df416d1952 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -446,6 +446,8 @@ public static bool IsSubstAvailable } } + public static bool IsRuntimeAsyncSupported => !IsMonoRuntime; + private static Version GetICUVersion() { int version = 0; diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs index 1b2c9fb7274efd..e1dd761502f68c 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs +++ b/src/libraries/System.Diagnostics.StackTrace/tests/StackTraceTests.cs @@ -11,6 +11,7 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.DotNet.RemoteExecutor; +using System.Threading.Tasks; using Xunit; namespace System.Diagnostics @@ -30,7 +31,183 @@ public static StackTrace MethodWithException() } } } + + public static class V1Methods + { + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)] +#line 1 "Test0.cs" + public static async Task Test0(Func method) + { + await Test1(method); + await Task.Yield(); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)] +#line 1 "Test1.cs" + public static async Task Test1(Func method) + { + try + { + await method(3); + } + catch (Exception ex) when (ex.Message.Contains("404")) + { + Console.WriteLine($"Caught exception in Test1 with: {ex}"); + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(false)] +#line 1 "Test2.cs" + public static async Task Test2(int i) + { + throw new NullReferenceException("Exception from Test2"); + } + } + + public class V2Methods + { + // v2 -> v1 -> v2 -> v1 + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Foo.cs" + public static async Task Foo() + { + await Task.Yield(); + try + { + await V1Methods.Test0(Foo1); + } + catch (NotImplementedException) + { + throw; + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Foo1.cs" + private static async Task Foo1(int i) + { + await Task.Yield(); + try + { + await Foo2(i); + return i * 2; + } + catch (NotImplementedException) + { + throw; + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Foo2.cs" + private static async Task Foo2(int i) + { + try + { + await Task.Yield(); + await V1Methods.Test2(i); + } + finally + { + throw new NotImplementedException("Exception from Foo2"); + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Bar.cs" + public static async Task Bar(int i) + { + if (i == 0) + throw new Exception("Exception from Bar"); + await Bar(i - 1); + } + + // also v2 v1 chaining but this time we don't have finally + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Quux.cs" + public static async Task Quux() + { + await Task.Yield(); + try + { + await V1Methods.Test0(Quux1); + } + catch (NotImplementedException) + { + throw; + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Quux1.cs" + private static async Task Quux1(int i) + { + try + { + await Task.Delay(10); + throw new NotImplementedException("Exception from Quux1"); + } + catch (NotImplementedException) + { + throw; + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Quuux.cs" + public static async Task Quuux() + { + var task = Quuux2(); + await Task.Yield(); + return await task; + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Quuux2.cs" + private static async Task Quuux2() + { + await Task.Yield(); + throw new Exception("Exception from Quuux2"); + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Bux.cs" + public static async Task Bux() + { + await Task.Yield(); + try + { + Baz().Wait(); + } + catch (Exception) + { + throw; + } + } + + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + [System.Runtime.CompilerServices.RuntimeAsyncMethodGeneration(true)] +#line 1 "Baz.cs" + public static async Task Baz() + { + if (Random.Shared.Next(1) == 100) await Task.Yield(); + throw new Exception("Exception from Baz method."); + } + } } +#line default namespace System.Diagnostics.Tests { @@ -406,6 +583,76 @@ public void ToString_ShowILOffset() }, regPattern).Dispose(); } + public static Dictionary MethodExceptionStrings = new() + { + { "Foo", new[] { + @"Exception from Foo2", + @"V2Methods\.Foo2\(Int32.*Foo2.*\.cs:line 10", + @"V2Methods\.Foo1\(Int32.*Foo1.*\.cs:line 6", + @"V1Methods\.Test1\(Func`2.*Test1.*\.cs:line 5", + @"V1Methods\.Test0\(Func`2.*Test0.*\.cs:line 3", + @"V2Methods\.Foo\(\).*Foo.*\.cs:line 6" + }}, + { "Bar", new[] { + @"Exception from Bar", + @"V2Methods\.Bar\(Int32.*Bar.*\.cs:line 4", + @"V2Methods\.Bar\(Int32.*Bar.*\.cs:line 5" + }}, + {"Quux", new[] { + @"Exception from Quux1", + @"V2Methods\.Quux1\(Int32.*Quux1.*\.cs:line 6", + @"V1Methods\.Test1\(Func`2.*Test1.*\.cs:line 5", + @"V1Methods\.Test0\(Func`2.*Test0.*\.cs:line 3", + @"V2Methods\.Quux\(\).*Quux.*\.cs:line 6" + }}, + { "Quuux", new[] { + @"Exception from Quuux2", + @"V2Methods\.Quuux2\(\).*Quuux2.*\.cs:line 4", + @"V2Methods\.Quuux\(\).*Quuux.*\.cs:line [3|5]" // if yield finishes before Task is awaited, line 3 else line 5. Either is ok. + }}, + {"Bux", new[] { + @"Exception from Baz method.", + @"V2Methods\.Baz\(\).*Baz.*\.cs:line 4", + @"V2Methods\.Bux\(\).*Bux.*\.cs:line 6" + }}, + }; + + public static IEnumerable Ctor_Async_TestData() + { + yield return new object[] { () => V2Methods.Foo(), MethodExceptionStrings["Foo"] }; + yield return new object[] { () => V2Methods.Bar(3), MethodExceptionStrings["Bar"] }; + yield return new object[] { () => V2Methods.Quux(), MethodExceptionStrings["Quux"] }; + yield return new object[] { () => V2Methods.Quuux(), MethodExceptionStrings["Quuux"] }; + yield return new object[] { () => V2Methods.Bux(), MethodExceptionStrings["Bux"] }; + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsRuntimeAsyncSupported))] + [MemberData(nameof(Ctor_Async_TestData))] + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] + public async Task ToString_Async(Func asyncMethod, string[] expectedPatterns) + { + Exception? caughtException = null; + try + { + await asyncMethod(); + } + catch (Exception ex) + { + caughtException = ex; + } + + Assert.NotNull(caughtException); + string exceptionText = caughtException.ToString(); + int startIndex = 0; + foreach (string pattern in expectedPatterns) + { + Regex regex = new(pattern, RegexOptions.None, TimeSpan.FromSeconds(10)); + Match match = regex.Match(exceptionText, startIndex); + Assert.True(match.Success); + startIndex = match.Index + match.Length; + } + } + [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] private static StackTrace NoParameters() => new StackTrace(); [MethodImpl(MethodImplOptions.NoOptimization | MethodImplOptions.NoInlining)] diff --git a/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj b/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj index 87f7d5be40d428..0d298cbce39401 100644 --- a/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj +++ b/src/libraries/System.Diagnostics.StackTrace/tests/System.Diagnostics.StackTrace.Tests.csproj @@ -4,6 +4,7 @@ true true true + true true @@ -18,6 +19,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.NonBrowser.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.NonBrowser.cs index bfb38d0cde7559..2a999416d8845c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.NonBrowser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.NonBrowser.cs @@ -1,6 +1,7 @@ // 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.Threading.Tasks; namespace System.Runtime.CompilerServices @@ -15,6 +16,7 @@ public static partial class AsyncHelpers /// On single-threaded environments (for example, browser), it registers a continuation and yields to the browser event loop immediately. /// /// The result from the main entry point to await. + [StackTraceHidden] public static void HandleAsyncEntryPoint(Task task) { task.GetAwaiter().GetResult(); @@ -28,6 +30,7 @@ public static void HandleAsyncEntryPoint(Task task) /// On single-threaded environments (for example, browser), it registers a continuation and yields to the browser event loop immediately. /// /// The result from the main entry point to await. + [StackTraceHidden] public static int HandleAsyncEntryPoint(Task task) { return task.GetAwaiter().GetResult(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs index 4a3c2df7d9eb3e..acbe2c960f8ad0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.cs @@ -26,6 +26,7 @@ public static partial class AsyncHelpers [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INotifyCompletion { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; @@ -49,6 +50,7 @@ public static void AwaitAwaiter(TAwaiter awaiter) where TAwaiter : INo [BypassReadyToRun] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter : ICriticalNotifyCompletion { ref RuntimeAsyncAwaitState state = ref t_runtimeAsyncAwaitState; @@ -70,6 +72,7 @@ public static void UnsafeAwaitAwaiter(TAwaiter awaiter) where TAwaiter [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static T Await(Task task) { TaskAwaiter awaiter = task.GetAwaiter(); @@ -89,6 +92,7 @@ public static T Await(Task task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void Await(Task task) { TaskAwaiter awaiter = task.GetAwaiter(); @@ -109,6 +113,7 @@ public static void Await(Task task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static T Await(ValueTask task) { ValueTaskAwaiter awaiter = task.GetAwaiter(); @@ -128,6 +133,7 @@ public static T Await(ValueTask task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void Await(ValueTask task) { ValueTaskAwaiter awaiter = task.GetAwaiter(); @@ -147,6 +153,7 @@ public static void Await(ValueTask task) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void Await(ConfiguredTaskAwaitable configuredAwaitable) { ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter(); @@ -166,6 +173,7 @@ public static void Await(ConfiguredTaskAwaitable configuredAwaitable) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable) { ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter = configuredAwaitable.GetAwaiter(); @@ -186,6 +194,7 @@ public static void Await(ConfiguredValueTaskAwaitable configuredAwaitable) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static T Await(ConfiguredTaskAwaitable configuredAwaitable) { ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter = configuredAwaitable.GetAwaiter(); @@ -206,6 +215,7 @@ public static T Await(ConfiguredTaskAwaitable configuredAwaitable) [BypassReadyToRun] [MethodImpl(MethodImplOptions.Async)] [RequiresPreviewFeatures] + [StackTraceHidden] public static T Await(ConfiguredValueTaskAwaitable configuredAwaitable) { ConfiguredValueTaskAwaitable.ConfiguredValueTaskAwaiter awaiter = configuredAwaitable.GetAwaiter(); diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 7f94b863343adc..465d1f0e279155 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -399,6 +399,10 @@ + + +