Skip to content

Commit 37445d4

Browse files
AustinWisejkotas
andauthored
Make FailFast a QCall (#98908)
* Make FailFast a QCALL * Remove extra GCPROTECT. * Update Environment.CoreCLR.cs Co-authored-by: Jan Kotas <jkotas@microsoft.com> * Consolidate to a single QCall. --------- Co-authored-by: Jan Kotas <jkotas@microsoft.com>
1 parent 8aff565 commit 37445d4

File tree

5 files changed

+92
-75
lines changed

5 files changed

+92
-75
lines changed

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

Lines changed: 35 additions & 9 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,15 @@ 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-
// to assign blame for crashes. Don't mess with this, such as by making it call
38-
// another managed helper method, unless you consult with some CLR Watson experts.
3937
[DoesNotReturn]
40-
[MethodImpl(MethodImplOptions.InternalCall)]
41-
public static extern void FailFast(string? message);
38+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
39+
public static void FailFast(string? message)
40+
{
41+
// Note: The CLR's Watson bucketization code looks at the our caller
42+
// to assign blame for crashes.
43+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
44+
FailFast(ref mark, message, exception: null, errorMessage: null);
45+
}
4246

4347
// This overload of FailFast will allow you to specify the exception object
4448
// whose bucket details *could* be used when undergoing the failfast process.
@@ -54,12 +58,34 @@ public static extern int ExitCode
5458
// IP for bucketing. If the exception object is not preallocated, it will use the bucket
5559
// details contained in the object (if any).
5660
[DoesNotReturn]
57-
[MethodImpl(MethodImplOptions.InternalCall)]
58-
public static extern void FailFast(string? message, Exception? exception);
61+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
62+
public static void FailFast(string? message, Exception? exception)
63+
{
64+
// Note: The CLR's Watson bucketization code looks at the our caller
65+
// to assign blame for crashes.
66+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
67+
FailFast(ref mark, message, exception, errorMessage: null);
68+
}
69+
70+
[DoesNotReturn]
71+
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
72+
internal static void FailFast(string? message, Exception? exception, string? errorMessage)
73+
{
74+
// Note: The CLR's Watson bucketization code looks at the our caller
75+
// to assign blame for crashes.
76+
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
77+
FailFast(ref mark, message, exception, errorMessage);
78+
}
79+
80+
[DoesNotReturn]
81+
private static void FailFast(ref StackCrawlMark mark, string? message, Exception? exception, string? errorMessage)
82+
{
83+
FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message), ObjectHandleOnStack.Create(ref exception), new StringHandleOnStack(ref errorMessage));
84+
}
5985

86+
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_FailFast")]
6087
[DoesNotReturn]
61-
[MethodImpl(MethodImplOptions.InternalCall)]
62-
internal static extern void FailFast(string? message, Exception? exception, string? errorMessage);
88+
private static partial void FailFast(StackCrawlMarkHandle mark, StringHandleOnStack message, ObjectHandleOnStack exception, StringHandleOnStack errorMessage);
6389

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

src/coreclr/classlibnative/bcltype/system.cpp

Lines changed: 52 additions & 55 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,28 @@ 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

290-
// Note: Do not merge this FCALL method with any other FailFast overloads.
291-
// Watson uses the managed FailFast method with one String for crash dump bucketization.
292-
FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE)
293-
{
294-
FCALL_CONTRACT;
295-
296-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
297-
298-
HELPER_METHOD_FRAME_BEGIN_1(refMessage);
299-
300-
// The HelperMethodFrame knows how to get the return address.
301-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
302-
303-
// Call the actual worker to perform failfast
304-
GenericFailFast(refMessage, NULL, retaddr, NULL);
305-
306-
HELPER_METHOD_FRAME_END();
307-
}
308-
FCIMPLEND
309-
310-
FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE)
311-
{
312-
FCALL_CONTRACT;
313-
314-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
315-
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
316-
317-
HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException);
318-
319-
// The HelperMethodFrame knows how to get the return address.
320-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
321-
322-
// Call the actual worker to perform failfast
323-
GenericFailFast(refMessage, refException, retaddr, NULL);
324-
325-
HELPER_METHOD_FRAME_END();
326-
}
327-
FCIMPLEND
328-
329-
FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE)
328+
extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource)
330329
{
331-
FCALL_CONTRACT;
332-
333-
STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
334-
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
335-
STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE;
330+
QCALL_CONTRACT;
336331

337-
HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource);
332+
BEGIN_QCALL;
338333

339-
// The HelperMethodFrame knows how to get the return address.
340-
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
334+
GCX_COOP();
335+
336+
STRINGREF refMessage = message.Get();
337+
EXCEPTIONREF refException = (EXCEPTIONREF)exception.Get();
338+
STRINGREF refErrorSource = errorSource.Get();
341339

342340
// Call the actual worker to perform failfast
343-
GenericFailFast(refMessage, refException, retaddr, errorSource);
341+
SystemNative::GenericFailFast(refMessage, refException, mark, refErrorSource);
344342

345-
HELPER_METHOD_FRAME_END();
343+
END_QCALL;
346344
}
347-
FCIMPLEND
348345

349346
FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC)
350347
{

src/coreclr/classlibnative/bcltype/system.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,19 @@ 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, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource);
58+
6259
// Returns the number of logical processors that can be used by managed code
6360
extern "C" INT32 QCALLTYPE Environment_GetProcessorCount();
6461

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ static const Entry s_QCall[] =
9494
DllImportEntry(Delegate_FindMethodHandle)
9595
DllImportEntry(Delegate_InternalEqualMethodHandles)
9696
DllImportEntry(Environment_Exit)
97+
DllImportEntry(Environment_FailFast)
9798
DllImportEntry(Environment_GetProcessorCount)
9899
DllImportEntry(ExceptionNative_GetMessageFromNativeResources)
99100
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)

0 commit comments

Comments
 (0)