summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAustin Wise <AustinWise@gmail.com>2024-03-02 23:12:22 -0800
committerGitHub <noreply@github.com>2024-03-02 23:12:22 -0800
commit37445d4964a50eeff87ca7ed8cbdf251b547b779 (patch)
tree0cb207541d8bdcb52c5939d650f32bbcd8fba718
parent8aff565e389f10535d9520182e26d7b45de71b60 (diff)
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>
-rw-r--r--src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs44
-rw-r--r--src/coreclr/classlibnative/bcltype/system.cpp107
-rw-r--r--src/coreclr/classlibnative/bcltype/system.h11
-rw-r--r--src/coreclr/vm/ecalllist.h4
-rw-r--r--src/coreclr/vm/qcallentrypoints.cpp1
5 files changed, 92 insertions, 75 deletions
diff --git a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs
index 8bbd6e98ddaa..73d6795985a8 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.cs
@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Security;
using System.Threading;
namespace System
@@ -33,12 +34,15 @@ namespace System
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.
@@ -54,12 +58,34 @@ namespace System
// 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
{
diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp
index 6e9a9a9ee956..0d6091387a97 100644
--- a/src/coreclr/classlibnative/bcltype/system.cpp
+++ b/src/coreclr/classlibnative/bcltype/system.cpp
@@ -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
@@ -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
{
@@ -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
@@ -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)
{
@@ -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)
{
diff --git a/src/coreclr/classlibnative/bcltype/system.h b/src/coreclr/classlibnative/bcltype/system.h
index e440f1fa8b06..c7dae76de978 100644
--- a/src/coreclr/classlibnative/bcltype/system.h
+++ b/src/coreclr/classlibnative/bcltype/system.h
@@ -43,22 +43,19 @@ public:
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();
diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h
index 9da9ebdb102a..228a0d50f375 100644
--- a/src/coreclr/vm/ecalllist.h
+++ b/src/coreclr/vm/ecalllist.h
@@ -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)
diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp
index 2d6466fc7d7c..d546af82cff0 100644
--- a/src/coreclr/vm/qcallentrypoints.cpp
+++ b/src/coreclr/vm/qcallentrypoints.cpp
@@ -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)