Skip to content

Commit aed8fd4

Browse files
committed
Make FailFast a QCALL
1 parent 664c8f9 commit aed8fd4

File tree

5 files changed

+131
-52
lines changed

5 files changed

+131
-52
lines changed

src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics.CodeAnalysis;
66
using System.Runtime.CompilerServices;
77
using System.Runtime.InteropServices;
8+
using System.Security;
89
using System.Threading;
910

1011
namespace System
@@ -33,12 +34,20 @@ public static extern int ExitCode
3334
set;
3435
}
3536

36-
// Note: The CLR's Watson bucketization code looks at the caller of the FCALL method
37+
// Note: The CLR's Watson bucketization code looks at the caller of the QCALL method
3738
// to assign blame for crashes. Don't mess with this, such as by making it call
3839
// another managed helper method, unless you consult with some CLR Watson experts.
3940
[DoesNotReturn]
40-
[MethodImpl(MethodImplOptions.InternalCall)]
41-
public static extern void FailFast(string? message);
41+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
42+
public static void FailFast(string? message)
43+
{
44+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
45+
FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message));
46+
}
47+
48+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_FailFast")]
49+
[DoesNotReturn]
50+
private static partial void FailFast(StackCrawlMarkHandle mark, StringHandleOnStack message);
4251

4352
// This overload of FailFast will allow you to specify the exception object
4453
// whose bucket details *could* be used when undergoing the failfast process.
@@ -54,12 +63,28 @@ public static extern int ExitCode
5463
// IP for bucketing. If the exception object is not preallocated, it will use the bucket
5564
// details contained in the object (if any).
5665
[DoesNotReturn]
57-
[MethodImpl(MethodImplOptions.InternalCall)]
58-
public static extern void FailFast(string? message, Exception? exception);
66+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
67+
public static void FailFast(string? message, Exception? exception)
68+
{
69+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
70+
FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message), ObjectHandleOnStack.Create(ref exception));
71+
}
72+
73+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_FailFastWithException")]
74+
[DoesNotReturn]
75+
private static partial void FailFast(StackCrawlMarkHandle mark, StringHandleOnStack message, ObjectHandleOnStack exception);
76+
77+
[DoesNotReturn]
78+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
79+
internal static void FailFast(string? message, Exception? exception, string? errorMessage)
80+
{
81+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
82+
FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message), ObjectHandleOnStack.Create(ref exception), new StringHandleOnStack(ref errorMessage));
83+
}
5984

85+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_FailFastWithExceptionAndSource")]
6086
[DoesNotReturn]
61-
[MethodImpl(MethodImplOptions.InternalCall)]
62-
internal static extern void FailFast(string? message, Exception? exception, string? errorMessage);
87+
private static partial void FailFast(StackCrawlMarkHandle mark, StringHandleOnStack message, ObjectHandleOnStack exception, StringHandleOnStack errorMessage);
6388

6489
private static unsafe string[] InitializeCommandLineArgs(char* exePath, int argc, char** argv) // invoked from VM
6590
{

src/coreclr/classlibnative/bcltype/system.cpp

Lines changed: 88 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,38 @@ extern "C" INT32 QCALLTYPE Environment_GetProcessorCount()
133133
return processorCount;
134134
}
135135

136+
struct FindFailFastCallerStruct {
137+
StackCrawlMark* pStackMark;
138+
UINT_PTR retAddress;
139+
};
140+
141+
// This method is called by the GetMethod function and will crawl backward
142+
// up the stack for integer methods.
143+
static StackWalkAction FindFailFastCallerCallback(CrawlFrame* frame, VOID* data) {
144+
CONTRACTL
145+
{
146+
NOTHROW;
147+
GC_NOTRIGGER;
148+
MODE_ANY;
149+
}
150+
CONTRACTL_END;
151+
152+
FindFailFastCallerStruct* pFindCaller = (FindFailFastCallerStruct*) data;
153+
154+
// The check here is between the address of a local variable
155+
// (the stack mark) and a pointer to the EIP for a frame
156+
// (which is actually the pointer to the return address to the
157+
// function from the previous frame). So we'll actually notice
158+
// which frame the stack mark was in one frame later. This is
159+
// fine since we only implement LookForMyCaller.
160+
_ASSERTE(*pFindCaller->pStackMark == LookForMyCaller);
161+
if (!frame->IsInCalleesFrames(pFindCaller->pStackMark))
162+
return SWA_CONTINUE;
163+
164+
pFindCaller->retAddress = GetControlPC(frame->GetRegisterSet());
165+
return SWA_ABORT;
166+
}
167+
136168
// FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown.
137169
//
138170
// Static message buffer used by SystemNative::FailFast to avoid reliance on a
@@ -143,9 +175,10 @@ WCHAR *g_pFailFastBuffer = g_szFailFastBuffer;
143175

144176
#define FAIL_FAST_STATIC_BUFFER_LENGTH (sizeof(g_szFailFastBuffer) / sizeof(WCHAR))
145177

146-
// This is the common code for FailFast processing that is wrapped by the two
178+
179+
// This is the common code for FailFast processing that is wrapped by the
147180
// FailFast FCalls below.
148-
void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF refErrorSourceString)
181+
void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackMark, STRINGREF refErrorSourceString)
149182
{
150183
CONTRACTL
151184
{
@@ -166,6 +199,11 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
166199

167200
GCPROTECT_BEGIN(gc);
168201

202+
FindFailFastCallerStruct findCallerData;
203+
findCallerData.pStackMark = stackMark;
204+
findCallerData.retAddress = 0;
205+
StackWalkFunctions(GetThread(), FindFailFastCallerCallback, &findCallerData);
206+
169207
// Managed code injected FailFast maps onto the unmanaged version
170208
// (EEPolicy::HandleFatalError) in the following manner: the exit code is
171209
// always set to COR_E_FAILFAST and the address passed (usually a failing
@@ -267,7 +305,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
267305
{
268306
PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
269307
_ASSERTE(pUEWatsonBucketTracker != NULL);
270-
pUEWatsonBucketTracker->SaveIpForWatsonBucket(retAddress);
308+
pUEWatsonBucketTracker->SaveIpForWatsonBucket(findCallerData.retAddress);
271309
pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL);
272310
if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
273311
{
@@ -282,69 +320,85 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
282320
if (gc.refExceptionForWatsonBucketing != NULL)
283321
pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing);
284322

285-
EEPolicy::HandleFatalError(COR_E_FAILFAST, retAddress, pszMessage, NULL, errorSourceString, argExceptionString);
323+
EEPolicy::HandleFatalError(COR_E_FAILFAST, findCallerData.retAddress, pszMessage, NULL, errorSourceString, argExceptionString);
286324

287325
GCPROTECT_END();
288326
}
289327

290328
// Note: Do not merge this FCALL method with any other FailFast overloads.
291329
// Watson uses the managed FailFast method with one String for crash dump bucketization.
292-
FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE)
330+
extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message)
293331
{
294-
FCALL_CONTRACT;
332+
QCALL_CONTRACT;
295333

296-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
334+
BEGIN_QCALL;
297335

298-
HELPER_METHOD_FRAME_BEGIN_1(refMessage);
336+
GCX_COOP();
299337

300-
// The HelperMethodFrame knows how to get the return address.
301-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
338+
STRINGREF refMessage = message.Get();
339+
GCPROTECT_BEGIN(refMessage);
302340

303341
// Call the actual worker to perform failfast
304-
GenericFailFast(refMessage, NULL, retaddr, NULL);
342+
SystemNative::GenericFailFast(refMessage, NULL, mark, NULL);
305343

306-
HELPER_METHOD_FRAME_END();
344+
GCPROTECT_END();
345+
346+
END_QCALL;
307347
}
308-
FCIMPLEND
309348

310-
FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE)
349+
extern "C" void QCALLTYPE Environment_FailFastWithException(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception)
311350
{
312-
FCALL_CONTRACT;
351+
QCALL_CONTRACT;
313352

314-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
315-
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
353+
BEGIN_QCALL;
316354

317-
HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException);
355+
GCX_COOP();
356+
357+
struct
358+
{
359+
STRINGREF refMessage;
360+
EXCEPTIONREF refException;
361+
} gc;
362+
gc.refMessage = message.Get();
363+
gc.refException = (EXCEPTIONREF)exception.Get();
318364

319-
// The HelperMethodFrame knows how to get the return address.
320-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
365+
GCPROTECT_BEGIN(gc);
321366

322367
// Call the actual worker to perform failfast
323-
GenericFailFast(refMessage, refException, retaddr, NULL);
368+
SystemNative::GenericFailFast(gc.refMessage, gc.refException, mark, NULL);
324369

325-
HELPER_METHOD_FRAME_END();
370+
GCPROTECT_END();
371+
372+
END_QCALL;
326373
}
327-
FCIMPLEND
328374

329-
FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE)
375+
extern "C" void QCALLTYPE Environment_FailFastWithExceptionAndSource(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource)
330376
{
331-
FCALL_CONTRACT;
377+
QCALL_CONTRACT;
332378

333-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
334-
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
335-
STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE;
379+
BEGIN_QCALL;
336380

337-
HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource);
381+
GCX_COOP();
382+
383+
struct
384+
{
385+
STRINGREF refMessage;
386+
EXCEPTIONREF refException;
387+
STRINGREF refErrorSource;
388+
} gc;
389+
gc.refMessage = message.Get();
390+
gc.refException = (EXCEPTIONREF)exception.Get();
391+
gc.refErrorSource = errorSource.Get();
338392

339-
// The HelperMethodFrame knows how to get the return address.
340-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
393+
GCPROTECT_BEGIN(gc);
341394

342395
// Call the actual worker to perform failfast
343-
GenericFailFast(refMessage, refException, retaddr, errorSource);
396+
SystemNative::GenericFailFast(gc.refMessage, gc.refException, mark, gc.refErrorSource);
344397

345-
HELPER_METHOD_FRAME_END();
398+
GCPROTECT_END();
399+
400+
END_QCALL;
346401
}
347-
FCIMPLEND
348402

349403
FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC)
350404
{

src/coreclr/classlibnative/bcltype/system.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,23 @@ class SystemNative
4343
static FCDECL1(VOID,SetExitCode,INT32 exitcode);
4444
static FCDECL0(INT32, GetExitCode);
4545

46-
static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
47-
static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
48-
static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE);
49-
5046
static FCDECL0(FC_BOOL_RET, IsServerGC);
5147

5248
// Return a method info for the method were the exception was thrown
5349
static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE);
54-
55-
private:
50+
5651
// Common processing code for FailFast
57-
static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF errorSource);
52+
static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackCrawlMark, STRINGREF errorSource);
5853
};
5954

6055
extern "C" void QCALLTYPE Environment_Exit(INT32 exitcode);
6156

57+
extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message);
58+
59+
extern "C" void QCALLTYPE Environment_FailFastWithException(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception);
60+
61+
extern "C" void QCALLTYPE Environment_FailFastWithExceptionAndSource(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource);
62+
6263
// Returns the number of logical processors that can be used by managed code
6364
extern "C" INT32 QCALLTYPE Environment_GetProcessorCount();
6465

src/coreclr/vm/ecalllist.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,6 @@ FCFuncStart(gEnvironmentFuncs)
9393
FCFuncElement("get_TickCount64", SystemNative::GetTickCount64)
9494
FCFuncElement("set_ExitCode", SystemNative::SetExitCode)
9595
FCFuncElement("get_ExitCode", SystemNative::GetExitCode)
96-
97-
FCFuncElementSig("FailFast", &gsig_SM_Str_RetVoid, SystemNative::FailFast)
98-
FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_RetVoid, SystemNative::FailFastWithException)
99-
FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_Str_RetVoid, SystemNative::FailFastWithExceptionAndSource)
10096
FCFuncEnd()
10197

10298
FCFuncStart(gExceptionFuncs)

src/coreclr/vm/qcallentrypoints.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ static const Entry s_QCall[] =
9494
DllImportEntry(Delegate_FindMethodHandle)
9595
DllImportEntry(Delegate_InternalEqualMethodHandles)
9696
DllImportEntry(Environment_Exit)
97+
DllImportEntry(Environment_FailFast)
98+
DllImportEntry(Environment_FailFastWithException)
99+
DllImportEntry(Environment_FailFastWithExceptionAndSource)
97100
DllImportEntry(Environment_GetProcessorCount)
98101
DllImportEntry(ExceptionNative_GetMessageFromNativeResources)
99102
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)

0 commit comments

Comments
 (0)