Skip to content

Commit 7fa306e

Browse files
rcj1jakobbotscham11jkotas
authored
Runtime-async Exception.ToString() (#122722)
This PR implements exception stack trace collection in runtime-async stacks for both coreclr and nativeAOT. * Augmenting stack unwinding logic in AsyncHelpers.CoreCLR.cs to append frames from runtime-async async-await chain to exception stacktrace using DiagnosticIP of Continuation. * Implements ThrowExact helper to throw exception that has been stored in Continuation, while keeping its existing stack trace intact. Accomplished by creating throw helpers that set ExKind.RethrowFlag. Would be open to rename "RethrowFlag" and companions to "NoEraseFlag" or something similar, or leave as is. * ILC changes: Ensuring that stack trace records are emitted for runtime-async methods, and hidden for resumption stubs. * Testing: Added tests for line/method/document correctness including v1-v2 chaining. @dotnet/ilc-contrib --------- Co-authored-by: Jakob Botsch Nielsen <[email protected]> Co-authored-by: Adeel <[email protected]> Co-authored-by: Jan Kotas <[email protected]>
1 parent 0402dfa commit 7fa306e

File tree

41 files changed

+734
-133
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+734
-133
lines changed

docs/design/coreclr/botr/readytorun-format.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ enum ReadyToRunHelper
858858
READYTORUN_HELPER_FailFast = 0x24,
859859
READYTORUN_HELPER_ThrowNullRef = 0x25,
860860
READYTORUN_HELPER_ThrowDivZero = 0x26,
861+
READYTORUN_HELPER_ThrowExact = 0x27,
861862

862863
// Write barriers
863864
READYTORUN_HELPER_WriteBarrier = 0x30,

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,14 @@ public void CaptureContexts()
229229
[ThreadStatic]
230230
private static RuntimeAsyncAwaitState t_runtimeAsyncAwaitState;
231231

232+
#if !NATIVEAOT
233+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AsyncHelpers_AddContinuationToExInternal")]
234+
private static unsafe partial void AddContinuationToExInternal(void* diagnosticIP, ObjectHandleOnStack ex);
235+
236+
internal static unsafe void AddContinuationToExInternal(void* diagnosticIP, Exception e)
237+
=> AddContinuationToExInternal(diagnosticIP, ObjectHandleOnStack.Create(ref e));
238+
#endif
239+
232240
private static unsafe Continuation AllocContinuation(Continuation prevContinuation, MethodTable* contMT)
233241
{
234242
#if NATIVEAOT
@@ -436,6 +444,7 @@ internal void HandleSuspended()
436444
}
437445
}
438446

447+
[StackTraceHidden]
439448
private unsafe void DispatchContinuations()
440449
{
441450
ExecutionAndSyncBlockStore contexts = default;
@@ -469,7 +478,7 @@ private unsafe void DispatchContinuations()
469478
}
470479
catch (Exception ex)
471480
{
472-
Continuation? handlerContinuation = UnwindToPossibleHandler(asyncDispatcherInfo.NextContinuation);
481+
Continuation? handlerContinuation = UnwindToPossibleHandler(asyncDispatcherInfo.NextContinuation, ex);
473482
if (handlerContinuation == null)
474483
{
475484
// Tail of AsyncTaskMethodBuilderT.SetException
@@ -520,10 +529,19 @@ private unsafe void DispatchContinuations()
520529

521530
private ref byte GetResultStorage() => ref Unsafe.As<T?, byte>(ref m_result);
522531

523-
private static Continuation? UnwindToPossibleHandler(Continuation? continuation)
532+
private static unsafe Continuation? UnwindToPossibleHandler(Continuation? continuation, Exception ex)
524533
{
525534
while (true)
526535
{
536+
if (continuation != null && continuation.ResumeInfo != null && continuation.ResumeInfo->DiagnosticIP != null)
537+
{
538+
#if !NATIVEAOT
539+
AddContinuationToExInternal(continuation.ResumeInfo->DiagnosticIP, ex);
540+
#else
541+
IntPtr ip = (IntPtr)continuation.ResumeInfo->DiagnosticIP;
542+
System.Exception.AppendExceptionStackFrame(ex, ip, 0);
543+
#endif
544+
}
527545
if (continuation == null || (continuation.Flags & ContinuationFlags.HasException) != 0)
528546
return continuation;
529547

@@ -769,12 +787,14 @@ private static void CaptureContinuationContext(ref object continuationContext, r
769787
flags |= ContinuationFlags.ContinueOnThreadPool;
770788
}
771789

790+
[StackTraceHidden]
772791
internal static T CompletedTaskResult<T>(Task<T> task)
773792
{
774793
TaskAwaiter.ValidateEnd(task);
775794
return task.ResultOnSuccess;
776795
}
777796

797+
[StackTraceHidden]
778798
internal static void CompletedTask(Task task)
779799
{
780800
TaskAwaiter.ValidateEnd(task);

src/coreclr/inc/readytorun.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// If you update this, ensure you run `git grep MINIMUM_READYTORUN_MAJOR_VERSION`
2121
// and handle pending work.
2222
#define READYTORUN_MAJOR_VERSION 18
23-
#define READYTORUN_MINOR_VERSION 0x0000
23+
#define READYTORUN_MINOR_VERSION 0x0001
2424

2525
#define MINIMUM_READYTORUN_MAJOR_VERSION 18
2626

@@ -337,6 +337,7 @@ enum ReadyToRunHelper
337337
READYTORUN_HELPER_FailFast = 0x24,
338338
READYTORUN_HELPER_ThrowNullRef = 0x25,
339339
READYTORUN_HELPER_ThrowDivZero = 0x26,
340+
READYTORUN_HELPER_ThrowExact = 0x27,
340341

341342
// Write barriers
342343
READYTORUN_HELPER_WriteBarrier = 0x30,

src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ EXTERN_C CODE_LOCATION ReturnFromUniversalTransitionReturnResult;
4040
EXTERN_C CODE_LOCATION RhpCallCatchFunclet2;
4141
EXTERN_C CODE_LOCATION RhpCallFinallyFunclet2;
4242
EXTERN_C CODE_LOCATION RhpCallFilterFunclet2;
43-
EXTERN_C CODE_LOCATION RhpThrowEx2;
43+
EXTERN_C CODE_LOCATION RhpThrowImpl2;
4444
EXTERN_C CODE_LOCATION RhpThrowHwEx2;
4545
EXTERN_C CODE_LOCATION RhpRethrow2;
4646
#endif // !defined(FEATURE_PORTABLE_HELPERS)
@@ -2241,7 +2241,7 @@ StackFrameIterator::ReturnAddressCategory StackFrameIterator::CategorizeUnadjust
22412241
}
22422242
#endif
22432243

2244-
if (EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowEx2) ||
2244+
if (EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowImpl2) ||
22452245
EQUALS_RETURN_ADDRESS(returnAddress, RhpThrowHwEx2) ||
22462246
EQUALS_RETURN_ADDRESS(returnAddress, RhpRethrow2))
22472247
{

src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.S

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,24 @@ ALTERNATE_ENTRY RhpThrowHwEx2
7878
NESTED_END RhpThrowHwEx, _TEXT
7979

8080

81+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
82+
//
83+
// RhpThrowExact
84+
//
85+
// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag
86+
//
87+
// INPUT: RDI: exception object
88+
//
89+
// OUTPUT:
90+
//
91+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
92+
LEAF_ENTRY RhpThrowExact, _TEXT
93+
94+
mov r8d, 4 // r8d = ExKind.RethrowFlag
95+
jmp C_FUNC(RhpThrowImpl)
96+
97+
LEAF_END RhpThrowExact, _TEXT
98+
8199
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
82100
//
83101
// RhpThrowEx
@@ -87,7 +105,14 @@ NESTED_END RhpThrowHwEx, _TEXT
87105
// OUTPUT:
88106
//
89107
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
90-
NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
108+
LEAF_ENTRY RhpThrowEx, _TEXT
109+
110+
mov r8d, 1 // r8d = ExKind.Throw
111+
jmp C_FUNC(RhpThrowImpl)
112+
113+
LEAF_END RhpThrowEx, _TEXT
114+
115+
NESTED_ENTRY RhpThrowImpl, _TEXT, NoHandler
91116

92117
STACKSIZEOF_ExInfo = ((SIZEOF__ExInfo + 15) & (~ 15))
93118
rsp_offsetof_Context = STACKSIZEOF_ExInfo
@@ -136,7 +161,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
136161
mov [rsi + OFFSETOF__ExInfo__m_exception], rdx // init the exception object to null
137162
mov byte ptr [rsi + OFFSETOF__ExInfo__m_passNumber], 1 // init to the first pass
138163
mov dword ptr [rsi + OFFSETOF__ExInfo__m_idxCurClause], 0xFFFFFFFF
139-
mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], 1 // ExKind.Throw
164+
mov byte ptr [rsi + OFFSETOF__ExInfo__m_kind], r8b // ExKind (from r8b)
140165

141166
// link the ExInfo into the thread's ExInfo chain
142167
mov rdx, [rax + OFFSETOF__Thread__m_pExInfoStackHead]
@@ -151,12 +176,12 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
151176
// rsi contains the address of the ExInfo
152177
call EXTERNAL_C_FUNC(RhThrowEx)
153178

154-
ALTERNATE_ENTRY RhpThrowEx2
179+
ALTERNATE_ENTRY RhpThrowImpl2
155180

156181
// no return
157182
int 3
158183

159-
NESTED_END RhpThrowEx, _TEXT
184+
NESTED_END RhpThrowImpl, _TEXT
160185

161186
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
162187
//

src/coreclr/nativeaot/Runtime/amd64/ExceptionHandling.asm

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,24 @@ ALTERNATE_ENTRY RhpThrowHwEx2
105105

106106
NESTED_END RhpThrowHwEx, _TEXT
107107

108+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
109+
;;
110+
;; RhpThrowExact
111+
;;
112+
;; SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag
113+
;;
114+
;; INPUT: RCX: exception object
115+
;;
116+
;; OUTPUT:
117+
;;
118+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119+
LEAF_ENTRY RhpThrowExact, _TEXT
120+
121+
mov r9d, 4 ;; r9d = ExKind.RethrowFlag
122+
jmp RhpThrowImpl
123+
124+
LEAF_END RhpThrowExact, _TEXT
125+
108126
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
109127
;;
110128
;; RhpThrowEx
@@ -114,7 +132,14 @@ NESTED_END RhpThrowHwEx, _TEXT
114132
;; OUTPUT:
115133
;;
116134
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
117-
NESTED_ENTRY RhpThrowEx, _TEXT
135+
LEAF_ENTRY RhpThrowEx, _TEXT
136+
137+
mov r9d, 1 ;; r9d = ExKind.Throw
138+
jmp RhpThrowImpl
139+
LEAF_END RhpThrowEx, _TEXT
140+
141+
142+
NESTED_ENTRY RhpThrowImpl, _TEXT
118143

119144
SIZEOF_XmmSaves equ SIZEOF__PAL_LIMITED_CONTEXT - OFFSETOF__PAL_LIMITED_CONTEXT__Xmm6
120145
STACKSIZEOF_ExInfo equ ((SIZEOF__ExInfo + 15) AND (NOT 15))
@@ -172,7 +197,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT
172197
;; address could have been hijacked when we were in that C# code and we must remove the hijack and
173198
;; reflect the correct return address in our exception context record. The other throw helpers don't
174199
;; need this because they cannot be tail-called from C#.
175-
INLINE_THREAD_UNHIJACK rax, r9, rdx ;; trashes R9, RDX
200+
INLINE_THREAD_UNHIJACK rax, r10, rdx ;; trashes R10, RDX
176201
mov rdx, [rbx] ;; rdx <- return address
177202
mov [rsp + rsp_offsetof_Context + OFFSETOF__PAL_LIMITED_CONTEXT__IP], rdx ;; set 'faulting' IP after unhijack
178203

@@ -181,7 +206,7 @@ NESTED_ENTRY RhpThrowEx, _TEXT
181206
mov [rdx + OFFSETOF__ExInfo__m_exception], r8 ;; init the exception object to null
182207
mov byte ptr [rdx + OFFSETOF__ExInfo__m_passNumber], 1 ;; init to the first pass
183208
mov dword ptr [rdx + OFFSETOF__ExInfo__m_idxCurClause], 0FFFFFFFFh
184-
mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], 1 ;; ExKind.Throw
209+
mov byte ptr [rdx + OFFSETOF__ExInfo__m_kind], r9b ;; ExKind (from r9b)
185210

186211
;; link the ExInfo into the thread's ExInfo chain
187212
mov r8, [rax + OFFSETOF__Thread__m_pExInfoStackHead]
@@ -196,12 +221,12 @@ NESTED_ENTRY RhpThrowEx, _TEXT
196221
;; rdx contains the address of the ExInfo
197222
call RhThrowEx
198223

199-
ALTERNATE_ENTRY RhpThrowEx2
224+
ALTERNATE_ENTRY RhpThrowImpl2
200225

201226
;; no return
202227
int 3
203228

204-
NESTED_END RhpThrowEx, _TEXT
229+
NESTED_END RhpThrowImpl, _TEXT
205230

206231
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
207232
;;

src/coreclr/nativeaot/Runtime/arm/ExceptionHandling.S

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,24 @@ GLOBAL_LABEL RhpThrowHwEx2
7979
NESTED_END RhpThrowHwEx
8080

8181

82+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
83+
//
84+
// RhpThrowExact
85+
//
86+
// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag
87+
//
88+
// INPUT: R0: exception object
89+
//
90+
// OUTPUT:
91+
//
92+
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
93+
LEAF_ENTRY RhpThrowExact, _TEXT
94+
95+
mov r2, #4 // r2 = ExKind.RethrowFlag
96+
b C_FUNC(RhpThrowImpl)
97+
98+
LEAF_END RhpThrowExact, _TEXT
99+
82100
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
83101
//
84102
// RhpThrowEx
@@ -88,7 +106,13 @@ NESTED_END RhpThrowHwEx
88106
// OUTPUT:
89107
//
90108
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
91-
NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
109+
LEAF_ENTRY RhpThrowEx, _TEXT
110+
111+
mov r2, #1 // r2 = ExKind.Throw
112+
b C_FUNC(RhpThrowImpl)
113+
LEAF_END RhpThrowEx, _TEXT
114+
115+
NESTED_ENTRY RhpThrowImpl, _TEXT, NoHandler
92116

93117
// Setup a PAL_LIMITED_CONTEXT on the stack {
94118
PROLOG_VPUSH {d8-d15}
@@ -106,8 +130,6 @@ NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
106130
// r0 = GetThread()
107131
INLINE_GETTHREAD
108132

109-
add r2, sp, #(rsp_offsetof_Context + SIZEOF__PAL_LIMITED_CONTEXT + 0x8) // r2 <- addr of return address
110-
111133
// There is runtime C# code that can tail call to RhpThrowEx using a binder intrinsic. So the return
112134
// address could have been hijacked when we were in that C# code and we must remove the hijack and
113135
// reflect the correct return address in our exception context record. The other throw helpers don't
@@ -158,9 +180,9 @@ LOCAL_LABEL(NotHiJacked):
158180
str r3, [r1, #OFFSETOF__ExInfo__m_exception] // init the exception object to null
159181
mov r3, #1
160182
strb r3, [r1, #OFFSETOF__ExInfo__m_passNumber] // init to the first pass
161-
strb r3, [r1, #OFFSETOF__ExInfo__m_kind]
183+
strb r2, [r1, #OFFSETOF__ExInfo__m_kind] // ExKind (from r2)
162184
mov r3, #0xFFFFFFFF
163-
str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause] // ExKind.Throw
185+
str r3, [r1, #OFFSETOF__ExInfo__m_idxCurClause]
164186

165187
// link the ExInfo into the thread's ExInfo chain
166188
ldr r3, [r0, #OFFSETOF__Thread__m_pExInfoStackHead]
@@ -176,12 +198,12 @@ LOCAL_LABEL(NotHiJacked):
176198
// r1 contains the address of the new ExInfo
177199
bl C_FUNC(RhThrowEx)
178200

179-
GLOBAL_LABEL RhpThrowEx2
201+
GLOBAL_LABEL RhpThrowImpl2
180202

181203
// no return
182204
EMIT_BREAKPOINT
183205

184-
NESTED_END RhpThrowEx, _TEXT
206+
NESTED_END RhpThrowImpl, _TEXT
185207

186208

187209
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

src/coreclr/nativeaot/Runtime/arm64/ExceptionHandling.S

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,23 @@
252252

253253
NESTED_END RhpThrowHwEx, _TEXT
254254

255+
//
256+
// RhpThrowExact
257+
//
258+
// SUMMARY: Similar to RhpThrowEx, except that it sets the rethrow flag
259+
//
260+
// INPUT: X0: exception object
261+
//
262+
// OUTPUT:
263+
//
264+
265+
LEAF_ENTRY RhpThrowExact, _TEXT
266+
267+
mov w4, #4 // w4 = ExKind.RethrowFlag
268+
b C_FUNC(RhpThrowImpl)
269+
270+
LEAF_END RhpThrowExact, _TEXT
271+
255272
//
256273
// RhpThrowEx
257274
//
@@ -260,7 +277,14 @@
260277
// OUTPUT:
261278
//
262279

263-
NESTED_ENTRY RhpThrowEx, _TEXT, NoHandler
280+
LEAF_ENTRY RhpThrowEx, _TEXT
281+
282+
mov w4, #1 // w4 = ExKind.Throw
283+
b C_FUNC(RhpThrowImpl)
284+
285+
LEAF_END RhpThrowEx, _TEXT
286+
287+
NESTED_ENTRY RhpThrowImpl, _TEXT, NoHandler
264288

265289
ALLOC_THROW_FRAME SOFTWARE_EXCEPTION
266290

@@ -316,8 +340,7 @@ LOCAL_LABEL(NotHijacked):
316340
strb w3, [x1, #OFFSETOF__ExInfo__m_passNumber] // pExInfo->m_passNumber = 1
317341
mov w3, #0xFFFFFFFF
318342
str w3, [x1, #OFFSETOF__ExInfo__m_idxCurClause] // pExInfo->m_idxCurClause = MaxTryRegionIdx
319-
mov w3, #1
320-
strb w3, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind.Throw
343+
strb w4, [x1, #OFFSETOF__ExInfo__m_kind] // pExInfo->m_kind = ExKind (from w4)
321344

322345
// link the ExInfo into the thread's ExInfo chain
323346
ldr x3, [x2, #OFFSETOF__Thread__m_pExInfoStackHead]
@@ -332,12 +355,11 @@ LOCAL_LABEL(NotHijacked):
332355
// x1: ExInfo*
333356
bl C_FUNC(RhThrowEx)
334357

335-
ALTERNATE_ENTRY RhpThrowEx2
358+
ALTERNATE_ENTRY RhpThrowImpl2
336359

337360
// no return
338361
EMIT_BREAKPOINT
339-
NESTED_END RhpThrowEx, _TEXT
340-
362+
NESTED_END RhpThrowImpl, _TEXT
341363

342364
//
343365
// void FASTCALL RhpRethrow()

0 commit comments

Comments
 (0)