Skip to content

Commit

Permalink
Make FailFast a QCall (#98908)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
AustinWise and jkotas authored Mar 3, 2024
1 parent 8aff565 commit 37445d4
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;

namespace System
Expand Down Expand Up @@ -33,12 +34,15 @@ public static extern int ExitCode
set;
}

// Note: The CLR's Watson bucketization code looks at the caller of the FCALL method
// to assign blame for crashes. Don't mess with this, such as by making it call
// another managed helper method, unless you consult with some CLR Watson experts.
[DoesNotReturn]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void FailFast(string? message);
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static void FailFast(string? message)
{
// Note: The CLR's Watson bucketization code looks at the our caller
// to assign blame for crashes.
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
FailFast(ref mark, message, exception: null, errorMessage: null);
}

// This overload of FailFast will allow you to specify the exception object
// whose bucket details *could* be used when undergoing the failfast process.
Expand All @@ -54,12 +58,34 @@ public static extern int ExitCode
// IP for bucketing. If the exception object is not preallocated, it will use the bucket
// details contained in the object (if any).
[DoesNotReturn]
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void FailFast(string? message, Exception? exception);
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
public static void FailFast(string? message, Exception? exception)
{
// Note: The CLR's Watson bucketization code looks at the our caller
// to assign blame for crashes.
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
FailFast(ref mark, message, exception, errorMessage: null);
}

[DoesNotReturn]
[DynamicSecurityMethod] // Methods containing StackCrawlMark local var has to be marked DynamicSecurityMethod
internal static void FailFast(string? message, Exception? exception, string? errorMessage)
{
// Note: The CLR's Watson bucketization code looks at the our caller
// to assign blame for crashes.
StackCrawlMark mark = StackCrawlMark.LookForMyCaller;
FailFast(ref mark, message, exception, errorMessage);
}

[DoesNotReturn]
private static void FailFast(ref StackCrawlMark mark, string? message, Exception? exception, string? errorMessage)
{
FailFast(new StackCrawlMarkHandle(ref mark), new StringHandleOnStack(ref message), ObjectHandleOnStack.Create(ref exception), new StringHandleOnStack(ref errorMessage));
}

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

private static unsafe string[] InitializeCommandLineArgs(char* exePath, int argc, char** argv) // invoked from VM
{
Expand Down
107 changes: 52 additions & 55 deletions src/coreclr/classlibnative/bcltype/system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,38 @@ extern "C" INT32 QCALLTYPE Environment_GetProcessorCount()
return processorCount;
}

struct FindFailFastCallerStruct {
StackCrawlMark* pStackMark;
UINT_PTR retAddress;
};

// This method is called by the GetMethod function and will crawl backward
// up the stack for integer methods.
static StackWalkAction FindFailFastCallerCallback(CrawlFrame* frame, VOID* data) {
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
MODE_ANY;
}
CONTRACTL_END;

FindFailFastCallerStruct* pFindCaller = (FindFailFastCallerStruct*) data;

// The check here is between the address of a local variable
// (the stack mark) and a pointer to the EIP for a frame
// (which is actually the pointer to the return address to the
// function from the previous frame). So we'll actually notice
// which frame the stack mark was in one frame later. This is
// fine since we only implement LookForMyCaller.
_ASSERTE(*pFindCaller->pStackMark == LookForMyCaller);
if (!frame->IsInCalleesFrames(pFindCaller->pStackMark))
return SWA_CONTINUE;

pFindCaller->retAddress = GetControlPC(frame->GetRegisterSet());
return SWA_ABORT;
}

// FailFast is supported in BCL.small as internal to support failing fast in places where EEE used to be thrown.
//
// Static message buffer used by SystemNative::FailFast to avoid reliance on a
Expand All @@ -143,9 +175,10 @@ WCHAR *g_pFailFastBuffer = g_szFailFastBuffer;

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

// This is the common code for FailFast processing that is wrapped by the two

// This is the common code for FailFast processing that is wrapped by the
// FailFast FCalls below.
void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF refErrorSourceString)
void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackMark, STRINGREF refErrorSourceString)
{
CONTRACTL
{
Expand All @@ -166,6 +199,11 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce

GCPROTECT_BEGIN(gc);

FindFailFastCallerStruct findCallerData;
findCallerData.pStackMark = stackMark;
findCallerData.retAddress = 0;
StackWalkFunctions(GetThread(), FindFailFastCallerCallback, &findCallerData);

// Managed code injected FailFast maps onto the unmanaged version
// (EEPolicy::HandleFatalError) in the following manner: the exit code is
// always set to COR_E_FAILFAST and the address passed (usually a failing
Expand Down Expand Up @@ -267,7 +305,7 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
{
PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
_ASSERTE(pUEWatsonBucketTracker != NULL);
pUEWatsonBucketTracker->SaveIpForWatsonBucket(retAddress);
pUEWatsonBucketTracker->SaveIpForWatsonBucket(findCallerData.retAddress);
pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::FatalError, pThread, NULL);
if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
{
Expand All @@ -282,69 +320,28 @@ void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExce
if (gc.refExceptionForWatsonBucketing != NULL)
pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing);

EEPolicy::HandleFatalError(COR_E_FAILFAST, retAddress, pszMessage, NULL, errorSourceString, argExceptionString);
EEPolicy::HandleFatalError(COR_E_FAILFAST, findCallerData.retAddress, pszMessage, NULL, errorSourceString, argExceptionString);

GCPROTECT_END();
}

// Note: Do not merge this FCALL method with any other FailFast overloads.
// Watson uses the managed FailFast method with one String for crash dump bucketization.
FCIMPL1(VOID, SystemNative::FailFast, StringObject* refMessageUNSAFE)
{
FCALL_CONTRACT;

STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;

HELPER_METHOD_FRAME_BEGIN_1(refMessage);

// The HelperMethodFrame knows how to get the return address.
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();

// Call the actual worker to perform failfast
GenericFailFast(refMessage, NULL, retaddr, NULL);

HELPER_METHOD_FRAME_END();
}
FCIMPLEND

FCIMPL2(VOID, SystemNative::FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE)
{
FCALL_CONTRACT;

STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;

HELPER_METHOD_FRAME_BEGIN_2(refMessage, refException);

// The HelperMethodFrame knows how to get the return address.
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();

// Call the actual worker to perform failfast
GenericFailFast(refMessage, refException, retaddr, NULL);

HELPER_METHOD_FRAME_END();
}
FCIMPLEND

FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE)
extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource)
{
FCALL_CONTRACT;

STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE;
QCALL_CONTRACT;

HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource);
BEGIN_QCALL;

// The HelperMethodFrame knows how to get the return address.
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
GCX_COOP();

STRINGREF refMessage = message.Get();
EXCEPTIONREF refException = (EXCEPTIONREF)exception.Get();
STRINGREF refErrorSource = errorSource.Get();

// Call the actual worker to perform failfast
GenericFailFast(refMessage, refException, retaddr, errorSource);
SystemNative::GenericFailFast(refMessage, refException, mark, refErrorSource);

HELPER_METHOD_FRAME_END();
END_QCALL;
}
FCIMPLEND

FCIMPL0(FC_BOOL_RET, SystemNative::IsServerGC)
{
Expand Down
11 changes: 4 additions & 7 deletions src/coreclr/classlibnative/bcltype/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,19 @@ class SystemNative
static FCDECL1(VOID,SetExitCode,INT32 exitcode);
static FCDECL0(INT32, GetExitCode);

static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE);

static FCDECL0(FC_BOOL_RET, IsServerGC);

// Return a method info for the method were the exception was thrown
static FCDECL1(ReflectMethodObject*, GetMethodFromStackTrace, ArrayBase* pStackTraceUNSAFE);

private:

// Common processing code for FailFast
static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, STRINGREF errorSource);
static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, StackCrawlMark* stackCrawlMark, STRINGREF errorSource);
};

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

extern "C" void QCALLTYPE Environment_FailFast(QCall::StackCrawlMarkHandle mark, QCall::StringHandleOnStack message, QCall::ObjectHandleOnStack exception, QCall::StringHandleOnStack errorSource);

// Returns the number of logical processors that can be used by managed code
extern "C" INT32 QCALLTYPE Environment_GetProcessorCount();

Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,6 @@ FCFuncStart(gEnvironmentFuncs)
FCFuncElement("get_TickCount64", SystemNative::GetTickCount64)
FCFuncElement("set_ExitCode", SystemNative::SetExitCode)
FCFuncElement("get_ExitCode", SystemNative::GetExitCode)

FCFuncElementSig("FailFast", &gsig_SM_Str_RetVoid, SystemNative::FailFast)
FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_RetVoid, SystemNative::FailFastWithException)
FCFuncElementSig("FailFast", &gsig_SM_Str_Exception_Str_RetVoid, SystemNative::FailFastWithExceptionAndSource)
FCFuncEnd()

FCFuncStart(gExceptionFuncs)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ static const Entry s_QCall[] =
DllImportEntry(Delegate_FindMethodHandle)
DllImportEntry(Delegate_InternalEqualMethodHandles)
DllImportEntry(Environment_Exit)
DllImportEntry(Environment_FailFast)
DllImportEntry(Environment_GetProcessorCount)
DllImportEntry(ExceptionNative_GetMessageFromNativeResources)
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)
Expand Down

0 comments on commit 37445d4

Please sign in to comment.