// This is the common code for FailFast processing that is wrapped by the two
// FailFast FCalls below.
-void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode)
+void SystemNative::GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF refErrorSourceString)
{
CONTRACTL
{
{
STRINGREF refMesgString;
EXCEPTIONREF refExceptionForWatsonBucketing;
+ STRINGREF refErrorSourceString;
} gc;
ZeroMemory(&gc, sizeof(gc));
gc.refMesgString = refMesgString;
gc.refExceptionForWatsonBucketing = refExceptionForWatsonBucketing;
+ gc.refErrorSourceString = refErrorSourceString;
// Managed code injected FailFast maps onto the unmanaged version
// (EEPolicy::HandleFatalError) in the following manner: the exit code is
WCHAR *pszMessage = NULL;
DWORD cchMessage = (gc.refMesgString == NULL) ? 0 : gc.refMesgString->GetStringLength();
+ WCHAR * errorSourceString = NULL;
+
+ if (gc.refErrorSourceString != NULL)
+ {
+ DWORD cchErrorSource = gc.refErrorSourceString->GetStringLength();
+ errorSourceString = new (nothrow) WCHAR[cchErrorSource + 1];
+
+ if (errorSourceString != NULL)
+ {
+ memcpyNoGCRefs(errorSourceString, gc.refErrorSourceString->GetBuffer(), cchErrorSource * sizeof(WCHAR));
+ errorSourceString[cchErrorSource] = W('\0');
+ }
+ }
+
if (cchMessage < FAIL_FAST_STATIC_BUFFER_LENGTH)
{
pszMessage = g_szFailFastBuffer;
if (gc.refExceptionForWatsonBucketing != NULL)
pThread->SetLastThrownObject(gc.refExceptionForWatsonBucketing);
- EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage);
+ EEPolicy::HandleFatalError(exitCode, retAddress, pszMessage, NULL, errorSourceString);
GCPROTECT_END();
}
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
// Call the actual worker to perform failfast
- GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST);
+ GenericFailFast(refMessage, NULL, retaddr, COR_E_FAILFAST, NULL);
HELPER_METHOD_FRAME_END();
}
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
// Call the actual worker to perform failfast
- GenericFailFast(refMessage, NULL, retaddr, exitCode);
+ GenericFailFast(refMessage, NULL, retaddr, exitCode, NULL);
HELPER_METHOD_FRAME_END();
}
UINT_PTR retaddr = HELPER_METHOD_FRAME_GET_RETURN_ADDRESS();
// Call the actual worker to perform failfast
- GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST);
+ GenericFailFast(refMessage, refException, retaddr, COR_E_FAILFAST, NULL);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+FCIMPL3(VOID, SystemNative::FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE)
+{
+ FCALL_CONTRACT;
+
+ STRINGREF refMessage = (STRINGREF)refMessageUNSAFE;
+ EXCEPTIONREF refException = (EXCEPTIONREF)refExceptionUNSAFE;
+ STRINGREF errorSource = (STRINGREF)errorSourceUNSAFE;
+
+ HELPER_METHOD_FRAME_BEGIN_3(refMessage, refException, errorSource);
+
+ // 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, COR_E_FAILFAST, errorSource);
HELPER_METHOD_FRAME_END();
}
static FCDECL1(VOID, FailFast, StringObject* refMessageUNSAFE);
static FCDECL2(VOID, FailFastWithExitCode, StringObject* refMessageUNSAFE, UINT exitCode);
static FCDECL2(VOID, FailFastWithException, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE);
+ static FCDECL3(VOID, FailFastWithExceptionAndSource, StringObject* refMessageUNSAFE, ExceptionObject* refExceptionUNSAFE, StringObject* errorSourceUNSAFE);
static FCDECL0(StringObject*, _GetModuleFileName);
static FCDECL0(StringObject*, GetRuntimeDirectory);
private:
// Common processing code for FailFast
- static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode);
+ static void GenericFailFast(STRINGREF refMesgString, EXCEPTIONREF refExceptionForWatsonBucketing, UINT_PTR retAddress, UINT exitCode, STRINGREF errorSource);
};
/* static */
VPTR_CLASS(HelperMethodFrame)
VPTR_CLASS(HelperMethodFrame_1OBJ)
VPTR_CLASS(HelperMethodFrame_2OBJ)
+VPTR_CLASS(HelperMethodFrame_3OBJ)
VPTR_CLASS(HelperMethodFrame_PROTECTOBJ)
#ifdef FEATURE_HIJACK
VPTR_CLASS(HijackFrame)
{
private static readonly bool s_shouldWriteToStdErr = Environment.GetEnvironmentVariable("COMPlus_DebugWriteToStdErr") == "1";
- private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+ private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
{
if (Debugger.IsAttached)
{
// Fail in order to avoid anyone catching an exception and masking
// an assert failure.
var ex = new DebugAssertException(message, detailMessage, stackTrace);
- Environment.FailFast(ex.Message, ex);
+ Environment.FailFast(ex.Message, ex, errorSource);
}
}
// Do not remove this, it is needed to retain calls to these conditional methods in release builds
#define DEBUG
+using System.Diagnostics.Contracts;
+using System.Runtime.CompilerServices;
namespace System.Diagnostics
{
if (!condition)
{
string stackTrace;
-
try
{
- stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
+ stackTrace = new StackTrace(0, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
}
catch
{
stackTrace = "";
}
+ WriteLine(FormatAssert(stackTrace, message, detailMessage));
+ s_ShowDialog(stackTrace, message, detailMessage, "Assertion Failed");
+ }
+ }
+ internal static void ContractFailure(bool condition, string message, string detailMessage, string failureKindMessage)
+ {
+ if (!condition)
+ {
+ string stackTrace;
+ try
+ {
+ stackTrace = new StackTrace(0, true).ToString(System.Diagnostics.StackTrace.TraceFormat.Normal);
+ }
+ catch
+ {
+ stackTrace = "";
+ }
WriteLine(FormatAssert(stackTrace, message, detailMessage));
- s_ShowAssertDialog(stackTrace, message, detailMessage);
+ s_ShowDialog(stackTrace, message, detailMessage, SR.GetResourceString(failureKindMessage));
}
}
}
// internal and not readonly so that the tests can swap this out.
- internal static Action<string, string, string> s_ShowAssertDialog = ShowAssertDialog;
+ internal static Action<string, string, string, string> s_ShowDialog = ShowDialog;
+
internal static Action<string> s_WriteCore = WriteCore;
}
}
displayMessage = GetDisplayMessage(kind, userMessage, conditionText);
}
- // TODO: https://github.com/dotnet/coreclr/issues/14867
- System.Diagnostics.Debug.Fail(displayMessage);
+ System.Diagnostics.Debug.ContractFailure(false, displayMessage, string.Empty, GetResourceNameForFailure(kind));
}
private static String GetResourceNameForFailure(ContractFailureKind failureKind)
{
public static partial class Debug
{
- private static void ShowAssertDialog(string stackTrace, string message, string detailMessage)
+ private static void ShowDialog(string stackTrace, string message, string detailMessage, string errorSource)
{
if (Debugger.IsAttached)
{
// Fail in order to avoid anyone catching an exception and masking
// an assert failure.
var ex = new DebugAssertException(message, detailMessage, stackTrace);
- Environment.FailFast(ex.Message, ex);
+ Environment.FailFast(ex.Message, ex, errorSource);
}
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void FailFast(String message, Exception exception);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern void FailFast(String message, Exception exception, String errorMessage);
+
#if FEATURE_WIN32_REGISTRY
// This is only used by RegistryKey on Windows.
public static String ExpandEnvironmentVariables(String name)
return 0;
}
-void EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+void EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
{
fprintf(stderr, "Fatal error: %08x\n", exitCode);
ExitProcess(exitCode);
#endif // FEATURE_COMINTEROP
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(gRuntimeEnvironmentFuncs)
// Return Value:
// None
//
-inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+inline void DoLogForFailFastException(LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
{
WRAPPER_NO_CONTRACT;
Thread *pThread = GetThread();
EX_TRY
{
- PrintToStdErrA("FailFast: ");
+ if (errorSource == NULL)
+ {
+ PrintToStdErrA("FailFast:");
+ }
+ else
+ {
+ PrintToStdErrW((WCHAR*)errorSource);
+ }
+
+ PrintToStdErrA("\n");
PrintToStdErrW((WCHAR*)pszMessage);
PrintToStdErrA("\n");
- if (pThread)
+ if (pThread && errorSource == NULL)
{
PrintToStdErrA("\n");
LogCallstackForLogWorker();
// Log an error to the event log if possible, then throw up a dialog box.
//
-void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo)
+void EEPolicy::LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_TRIGGERS;
// Log FailFast exception to StdErr
if (exitCode == (UINT)COR_E_FAILFAST)
{
- DoLogForFailFastException(pszMessage, pExceptionInfo);
+ DoLogForFailFastException(pszMessage, pExceptionInfo, errorSource);
}
if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
UNREACHABLE();
}
-void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */)
+void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */, LPCWSTR errorSource /* = NULL */)
{
WRAPPER_NO_CONTRACT;
switch (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime))
{
case eRudeExitProcess:
- LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+ LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource);
SafeExitProcess(exitCode, TRUE);
break;
case eDisableRuntime:
- LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+ LogFatalError(exitCode, address, pszMessage, pExceptionInfo, errorSource);
DisableRuntime(SCA_ExitProcessWhenShutdownComplete);
break;
default:
static void HandleExitProcess(ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete);
- static void DECLSPEC_NORETURN HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL);
+ static void DECLSPEC_NORETURN HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage=NULL, PEXCEPTION_POINTERS pExceptionInfo= NULL, LPCWSTR errorSource=NULL);
static void DECLSPEC_NORETURN HandleFatalStackOverflow(EXCEPTION_POINTERS *pException, BOOL fSkipDebugger = FALSE);
BOOL IsValidActionForFailure(EClrFailure failure, EPolicyAction action);
EPolicyAction GetFinalAction(EPolicyAction action, Thread *pThread);
- static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo);
+ static void LogFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pMessage, PEXCEPTION_POINTERS pExceptionInfo, LPCWSTR errorSource);
// IMPORTANT NOTE: only the following two functions should be calling ExitProcessViaShim.
// - CorHost2::ExitProcess
// FailFast with specific error code and exception details
#define EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_INFO(_exitcode, _pExceptionInfo) EEPolicy::HandleFatalError(_exitcode, GetCurrentIP(), NULL, _pExceptionInfo);
+// Failfast with specific error code, exception details, and debug info
+#define EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_AND_DEBUG_INFO(_exitcode, _pExceptionInfo, _isDebug) EEPolicy::HandleFatalError(_exitcode, GetCurrentIP(), NULL, _pExceptionInfo, _isDebug);
+
#endif // EEPOLICY_H_
#define HELPER_METHOD_FRAME_BEGIN_2(arg1, arg2) HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_NONE, arg1, arg2)
+#define HELPER_METHOD_FRAME_BEGIN_ATTRIB_3(attribs, arg1, arg2, arg3) \
+ static_assert(sizeof(arg1) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+ static_assert(sizeof(arg2) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+ static_assert(sizeof(arg3) == sizeof(OBJECTREF), "GC protecting structs of multiple OBJECTREFs requires a PROTECT variant of the HELPER METHOD FRAME macro");\
+ HELPER_METHOD_FRAME_BEGIN_EX( \
+ return, \
+ HELPER_FRAME_DECL(3)(HELPER_FRAME_ARGS(attribs), \
+ (OBJECTREF*) &arg1, (OBJECTREF*) &arg2, (OBJECTREF*) &arg3), \
+ HELPER_METHOD_POLL(),TRUE)
+
+#define HELPER_METHOD_FRAME_BEGIN_3(arg1, arg2, arg3) HELPER_METHOD_FRAME_BEGIN_ATTRIB_3(Frame::FRAME_ATTR_NONE, arg1, arg2, arg3)
+
#define HELPER_METHOD_FRAME_BEGIN_PROTECT(gc) \
HELPER_METHOD_FRAME_BEGIN_EX( \
return, \
// | |
// + +-HelperMethodFrame_2OBJ- reports additional object references
// | |
+// + +-HelperMethodFrame_3OBJ- reports additional object references
+// | |
// + +-HelperMethodFrame_PROTECTOBJ - reports additional object references
// |
// +-TransitionFrame - this abstract frame represents a transition from
FRAME_TYPE_NAME(HelperMethodFrame)
FRAME_TYPE_NAME(HelperMethodFrame_1OBJ)
FRAME_TYPE_NAME(HelperMethodFrame_2OBJ)
+FRAME_TYPE_NAME(HelperMethodFrame_3OBJ)
FRAME_TYPE_NAME(HelperMethodFrame_PROTECTOBJ)
FRAME_ABSTRACT_TYPE_NAME(FramedMethodFrame)
FRAME_TYPE_NAME(SecureDelegateFrame)
DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_2OBJ)
};
+//-----------------------------------------------------------------------------
+// HelperMethodFrame_3OBJ
+//-----------------------------------------------------------------------------
+
+class HelperMethodFrame_3OBJ : public HelperMethodFrame
+{
+ VPTR_VTABLE_CLASS(HelperMethodFrame_3OBJ, HelperMethodFrame)
+
+public:
+#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
+ HelperMethodFrame_3OBJ(
+ void* fCallFtnEntry,
+ unsigned attribs,
+ OBJECTREF* aGCPtr1,
+ OBJECTREF* aGCPtr2,
+ OBJECTREF* aGCPtr3)
+ : HelperMethodFrame(fCallFtnEntry, attribs)
+ {
+ LIMITED_METHOD_CONTRACT;
+ gcPtrs[0] = aGCPtr1;
+ gcPtrs[1] = aGCPtr2;
+ gcPtrs[2] = aGCPtr3;
+ INDEBUG(Thread::ObjectRefProtected(aGCPtr1);)
+ INDEBUG(Thread::ObjectRefProtected(aGCPtr2);)
+ INDEBUG(Thread::ObjectRefProtected(aGCPtr3);)
+ INDEBUG((*aGCPtr1).Validate();)
+ INDEBUG((*aGCPtr2).Validate();)
+ INDEBUG((*aGCPtr3).Validate();)
+ }
+#endif
+
+ virtual void GcScanRoots(promote_func *fn, ScanContext* sc)
+ {
+ WRAPPER_NO_CONTRACT;
+ DoPromote(fn, sc, gcPtrs[0], FALSE);
+ DoPromote(fn, sc, gcPtrs[1], FALSE);
+ DoPromote(fn, sc, gcPtrs[2], FALSE);
+ HelperMethodFrame::GcScanRoots(fn, sc);
+ }
+
+#ifdef _DEBUG
+#ifndef DACCESS_COMPILE
+ void Pop()
+ {
+ WRAPPER_NO_CONTRACT;
+ HelperMethodFrame::Pop();
+ Thread::ObjectRefNew(gcPtrs[0]);
+ Thread::ObjectRefNew(gcPtrs[1]);
+ Thread::ObjectRefNew(gcPtrs[2]);
+ }
+#endif // DACCESS_COMPILE
+
+ BOOL Protects(OBJECTREF *ppORef)
+ {
+ LIMITED_METHOD_CONTRACT;
+ return (ppORef == gcPtrs[0] || ppORef == gcPtrs[1] || ppORef == gcPtrs[2]) ? TRUE : FALSE;
+ }
+#endif
+
+private:
+ PTR_OBJECTREF gcPtrs[3];
+
+ // Keep as last entry in class
+ DEFINE_VTABLE_GETTER_AND_CTOR_AND_DTOR(HelperMethodFrame_3OBJ)
+};
+
//-----------------------------------------------------------------------------
// HelperMethodFrame_PROTECTOBJ
FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF * aGCPtr1, OBJECTREF * aGCPtr2) :
m_frame(fCallFtnEntry, attribs, aGCPtr1, aGCPtr2) { WRAPPER_NO_CONTRACT; }
+ // HelperMethodFrame_3OBJ
+ FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF * aGCPtr1, OBJECTREF * aGCPtr2, OBJECTREF * aGCPtr3) :
+ m_frame(fCallFtnEntry, attribs, aGCPtr1, aGCPtr2, aGCPtr3) { WRAPPER_NO_CONTRACT; }
+
// HelperMethodFrame_PROTECTOBJ
FORCEINLINE FrameWithCookie(void* fCallFtnEntry, unsigned attribs, OBJECTREF* pObjRefs, int numObjRefs) :
m_frame(fCallFtnEntry, attribs, pObjRefs, numObjRefs) { WRAPPER_NO_CONTRACT; }
DEFINE_METASIG(SM(Str_RetVoid, s, v))
DEFINE_METASIG(SM(Str_Uint_RetVoid, s K, v))
DEFINE_METASIG_T(SM(Str_Exception_RetVoid, s C(EXCEPTION), v))
+DEFINE_METASIG_T(SM(Str_Exception_Str_RetVoid, s C(EXCEPTION) s, v))
// fields - e.g.:
// DEFINE_METASIG(Fld(PtrVoid, P(v)))