Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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