summaryrefslogtreecommitdiff
path: root/src/vm/eepolicy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/eepolicy.cpp')
-rw-r--r--src/vm/eepolicy.cpp1586
1 files changed, 1586 insertions, 0 deletions
diff --git a/src/vm/eepolicy.cpp b/src/vm/eepolicy.cpp
new file mode 100644
index 0000000000..8c3f2ec625
--- /dev/null
+++ b/src/vm/eepolicy.cpp
@@ -0,0 +1,1586 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+//
+// ---------------------------------------------------------------------------
+// EEPolicy.cpp
+// ---------------------------------------------------------------------------
+
+
+#include "common.h"
+#include "eepolicy.h"
+#include "corhost.h"
+#include "dbginterface.h"
+#include "eemessagebox.h"
+
+#include "eventreporter.h"
+#include "finalizerthread.h"
+#include "threadsuspend.h"
+
+#ifndef FEATURE_PAL
+#include "dwreport.h"
+#endif // !FEATURE_PAL
+
+#include "eventtrace.h"
+#undef ExitProcess
+
+BYTE g_EEPolicyInstance[sizeof(EEPolicy)];
+
+void InitEEPolicy()
+{
+ WRAPPER_NO_CONTRACT;
+ new (g_EEPolicyInstance) EEPolicy();
+}
+
+EEPolicy::EEPolicy ()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int n;
+ for (n = 0; n < MaxClrOperation; n++) {
+ m_Timeout[n] = INFINITE;
+ m_ActionOnTimeout[n] = eNoAction;
+ m_DefaultAction[n] = eNoAction;
+ }
+ m_Timeout[OPR_ProcessExit] = 40000;
+ m_ActionOnTimeout[OPR_ProcessExit] = eRudeExitProcess;
+ m_ActionOnTimeout[OPR_ThreadAbort] = eAbortThread;
+ m_ActionOnTimeout[OPR_ThreadRudeAbortInNonCriticalRegion] = eRudeAbortThread;
+ m_ActionOnTimeout[OPR_ThreadRudeAbortInCriticalRegion] = eRudeAbortThread;
+
+ m_DefaultAction[OPR_ThreadAbort] = eAbortThread;
+ m_DefaultAction[OPR_ThreadRudeAbortInNonCriticalRegion] = eRudeAbortThread;
+ m_DefaultAction[OPR_ThreadRudeAbortInCriticalRegion] = eRudeAbortThread;
+ m_DefaultAction[OPR_AppDomainUnload] = eUnloadAppDomain;
+ m_DefaultAction[OPR_AppDomainRudeUnload] = eRudeUnloadAppDomain;
+ m_DefaultAction[OPR_ProcessExit] = eExitProcess;
+ m_DefaultAction[OPR_FinalizerRun] = eNoAction;
+
+ for (n = 0; n < MaxClrFailure; n++) {
+ m_ActionOnFailure[n] = eNoAction;
+ }
+ m_ActionOnFailure[FAIL_CriticalResource] = eThrowException;
+ m_ActionOnFailure[FAIL_NonCriticalResource] = eThrowException;
+ m_ActionOnFailure[FAIL_OrphanedLock] = eNoAction;
+ m_ActionOnFailure[FAIL_FatalRuntime] = eRudeExitProcess;
+#ifdef FEATURE_CORECLR
+ // For CoreCLR, initialize the default action for AV processing to all
+ // all kind of code to catch AV exception. If the host wants, they can
+ // specify a different action for this.
+ m_ActionOnFailure[FAIL_AccessViolation] = eNoAction;
+#endif // FEATURE_CORECLR
+ m_ActionOnFailure[FAIL_StackOverflow] = eRudeExitProcess;
+ m_ActionOnFailure[FAIL_CodeContract] = eThrowException;
+ m_unhandledExceptionPolicy = eRuntimeDeterminedPolicy;
+}
+
+BOOL EEPolicy::IsValidActionForOperation(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ switch (operation) {
+ case OPR_ThreadAbort:
+ return action >= eAbortThread &&
+ action < MaxPolicyAction;
+ case OPR_ThreadRudeAbortInNonCriticalRegion:
+ case OPR_ThreadRudeAbortInCriticalRegion:
+ return action >= eRudeAbortThread && action != eUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_AppDomainUnload:
+ return action >= eUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_AppDomainRudeUnload:
+ return action >= eRudeUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_ProcessExit:
+ return action >= eExitProcess &&
+ action < MaxPolicyAction;
+ case OPR_FinalizerRun:
+ return action == eNoAction ||
+ (action >= eAbortThread &&
+ action < MaxPolicyAction);
+ default:
+ _ASSERT (!"Do not know valid action for this operation");
+ break;
+ }
+ return FALSE;
+}
+
+BOOL EEPolicy::IsValidActionForTimeout(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ switch (operation) {
+ case OPR_ThreadAbort:
+ return action > eAbortThread &&
+ action < MaxPolicyAction;
+ case OPR_ThreadRudeAbortInNonCriticalRegion:
+ case OPR_ThreadRudeAbortInCriticalRegion:
+ return action > eRudeUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_AppDomainUnload:
+ return action > eUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_AppDomainRudeUnload:
+ return action > eRudeUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case OPR_ProcessExit:
+ return action > eExitProcess &&
+ action < MaxPolicyAction;
+ case OPR_FinalizerRun:
+ return action == eNoAction ||
+ (action >= eAbortThread &&
+ action < MaxPolicyAction);
+ default:
+ _ASSERT (!"Do not know valid action for this operation");
+ break;
+ }
+ return FALSE;
+}
+
+BOOL EEPolicy::IsValidActionForFailure(EClrFailure failure, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ switch (failure) {
+ case FAIL_NonCriticalResource:
+ return action >= eThrowException &&
+ action < MaxPolicyAction;
+ case FAIL_CriticalResource:
+ return action >= eThrowException &&
+ action < MaxPolicyAction;
+ case FAIL_FatalRuntime:
+ return action >= eRudeExitProcess &&
+ action < MaxPolicyAction;
+ case FAIL_OrphanedLock:
+ return action >= eUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case FAIL_AccessViolation:
+#ifdef FEATURE_CORECLR
+ // Allowed actions on failure are:
+ //
+ // eNoAction or eRudeExitProcess.
+ return ((action == eNoAction) || (action == eRudeExitProcess));
+#else // !FEATURE_CORECLR
+ // FAIL_AccessViolation is defined for the desktop so that
+ // if any more definitions are added after it, their value
+ // should remain constant irrespective of whether its the
+ // desktop CLR or CoreCLR.
+ //
+ // That said, currently, Desktop CLR does not support
+ // FAIL_AccessViolation. Thus, any calls which use
+ // this failure are not allowed.
+ return FALSE;
+#endif // FEATURE_CORECLR
+ case FAIL_StackOverflow:
+ return action >= eRudeUnloadAppDomain &&
+ action < MaxPolicyAction;
+ case FAIL_CodeContract:
+ return action >= eThrowException &&
+ action <= eExitProcess;
+ default:
+ _ASSERTE (!"Do not know valid action for this failure");
+ break;
+ }
+
+ return FALSE;
+}
+
+HRESULT EEPolicy::SetTimeout(EClrOperation operation, DWORD timeout)
+{
+ CONTRACTL
+ {
+ MODE_ANY;
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if (static_cast<UINT>(operation) < MaxClrOperation)
+ {
+ m_Timeout[operation] = timeout;
+ if (operation == OPR_FinalizerRun &&
+ g_fEEStarted)
+ {
+ FastInterlockOr((DWORD*)&g_FinalizerWaiterStatus, FWS_WaitInterrupt);
+ FinalizerThread::SignalFinalizationDone(FALSE);
+ }
+ return S_OK;
+}
+ else
+ {
+ return E_INVALIDARG;
+ }
+}
+
+HRESULT EEPolicy::SetActionOnTimeout(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if (static_cast<UINT>(operation) < MaxClrOperation &&
+ IsValidActionForTimeout(operation, action))
+ {
+ m_ActionOnTimeout[operation] = action;
+ return S_OK;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+}
+
+EPolicyAction EEPolicy::GetFinalAction(EPolicyAction action, Thread *pThread)
+{
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(static_cast<UINT>(action) < MaxPolicyAction);
+
+ if (action < eAbortThread || action > eFastExitProcess)
+ {
+ return action;
+ }
+
+ while(TRUE)
+ {
+ // Look at default action. If the default action is more severe,
+ // use the default action instead.
+ EPolicyAction defaultAction = action;
+ switch (action)
+ {
+ case eAbortThread:
+ defaultAction = m_DefaultAction[OPR_ThreadAbort];
+ break;
+ case eRudeAbortThread:
+ if (pThread && !pThread->HasLockInCurrentDomain())
+ {
+ defaultAction = m_DefaultAction[OPR_ThreadRudeAbortInNonCriticalRegion];
+ }
+ else
+ {
+ defaultAction = m_DefaultAction[OPR_ThreadRudeAbortInCriticalRegion];
+ }
+ break;
+ case eUnloadAppDomain:
+ defaultAction = m_DefaultAction[OPR_AppDomainUnload];
+ break;
+ case eRudeUnloadAppDomain:
+ defaultAction = m_DefaultAction[OPR_AppDomainRudeUnload];
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ defaultAction = m_DefaultAction[OPR_ProcessExit];
+ if (defaultAction < action)
+ {
+ defaultAction = action;
+ }
+ break;
+ default:
+ break;
+ }
+ _ASSERTE(static_cast<UINT>(defaultAction) < MaxPolicyAction);
+
+ if (defaultAction == action)
+ {
+ return action;
+ }
+
+ _ASSERTE(defaultAction > action);
+ action = defaultAction;
+ }
+}
+
+// Allow setting timeout and action in one call.
+// If we decide to have atomical operation on Policy, we can use lock here
+// while SetTimeout and SetActionOnTimeout can not.
+HRESULT EEPolicy::SetTimeoutAndAction(EClrOperation operation, DWORD timeout, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if (static_cast<UINT>(operation) < MaxClrOperation &&
+ IsValidActionForTimeout(operation, action))
+ {
+ m_ActionOnTimeout[operation] = action;
+ m_Timeout[operation] = timeout;
+ if (operation == OPR_FinalizerRun &&
+ g_fEEStarted)
+ {
+ FastInterlockOr((DWORD*)&g_FinalizerWaiterStatus, FWS_WaitInterrupt);
+ FinalizerThread::SignalFinalizationDone(FALSE);
+ }
+ return S_OK;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+}
+
+HRESULT EEPolicy::SetDefaultAction(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if (static_cast<UINT>(operation) < MaxClrOperation &&
+ IsValidActionForOperation(operation, action))
+ {
+ m_DefaultAction[operation] = action;
+ return S_OK;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+}
+
+HRESULT EEPolicy::SetActionOnFailure(EClrFailure failure, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ if (static_cast<UINT>(failure) < MaxClrFailure &&
+ IsValidActionForFailure(failure, action))
+ {
+ m_ActionOnFailure[failure] = action;
+ return S_OK;
+ }
+ else
+ {
+ return E_INVALIDARG;
+ }
+}
+
+EPolicyAction EEPolicy::GetActionOnFailureNoHostNotification(EClrFailure failure)
+{
+ CONTRACTL
+ {
+ SO_TOLERANT;
+ MODE_ANY;
+ GC_NOTRIGGER;
+ NOTHROW;
+ }CONTRACTL_END;
+
+ _ASSERTE (failure < MaxClrFailure);
+ if (failure == FAIL_StackOverflow)
+ {
+ return m_ActionOnFailure[failure];
+ }
+
+ return GetFinalAction(m_ActionOnFailure[failure], GetThread());
+}
+
+EPolicyAction EEPolicy::GetActionOnFailure(EClrFailure failure)
+{
+ CONTRACTL
+ {
+ SO_TOLERANT;
+ MODE_ANY;
+ GC_NOTRIGGER;
+ NOTHROW;
+ }CONTRACTL_END;
+
+ _ASSERTE(static_cast<UINT>(failure) < MaxClrFailure);
+ if (failure == FAIL_StackOverflow)
+ {
+ return m_ActionOnFailure[failure];
+ }
+
+ EPolicyAction finalAction = GetActionOnFailureNoHostNotification(failure);
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostPolicyManager *pHostPolicyManager = CorHost2::GetHostPolicyManager();
+ if (pHostPolicyManager)
+ {
+#ifdef _DEBUG
+ Thread* pThread = GetThread();
+ if (pThread)
+ {
+ pThread->AddFiberInfo(Thread::ThreadTrackInfo_Escalation);
+ }
+#endif
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pHostPolicyManager->OnFailure(failure, finalAction);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+ return finalAction;
+}
+
+
+void EEPolicy::NotifyHostOnTimeout(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostPolicyManager *pHostPolicyManager = CorHost2::GetHostPolicyManager();
+ if (pHostPolicyManager)
+ {
+#ifdef _DEBUG
+ Thread* pThread = GetThread();
+ if (pThread)
+ {
+ pThread->AddFiberInfo(Thread::ThreadTrackInfo_Escalation);
+ }
+#endif
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pHostPolicyManager->OnTimeout(operation, action);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+}
+
+
+void EEPolicy::NotifyHostOnDefaultAction(EClrOperation operation, EPolicyAction action)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_INCLUDE_ALL_INTERFACES
+ IHostPolicyManager *pHostPolicyManager = CorHost2::GetHostPolicyManager();
+ if (pHostPolicyManager)
+ {
+#ifdef _DEBUG
+ Thread* pThread = GetThread();
+ if (pThread)
+ {
+ pThread->AddFiberInfo(Thread::ThreadTrackInfo_Escalation);
+ }
+#endif
+ BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread());
+ pHostPolicyManager->OnDefaultAction(operation, action);
+ END_SO_TOLERANT_CODE_CALLING_HOST;
+ }
+#endif // FEATURE_INCLUDE_ALL_INTERFACES
+}
+
+void SafeExitProcess(UINT exitCode, BOOL fAbort = FALSE, ShutdownCompleteAction sca = SCA_ExitProcessWhenShutdownComplete)
+{
+ // The process is shutting down. No need to check SO contract.
+ SO_NOT_MAINLINE_FUNCTION;
+ STRESS_LOG2(LF_SYNC, LL_INFO10, "SafeExitProcess: exitCode = %d, fAbort = %d\n", exitCode, fAbort);
+ CONTRACTL
+ {
+ DISABLED(GC_TRIGGERS);
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // The runtime must be in the appropriate thread mode when we exit, so that we
+ // aren't surprised by the thread mode when our DLL_PROCESS_DETACH occurs, or when
+ // other DLLs call Release() on us in their detach [dangerous!], etc.
+ GCX_PREEMP_NO_DTOR();
+
+ FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE);
+
+ ProcessEventForHost(Event_ClrDisabled, NULL);
+
+ // Note that for free and retail builds StressLog must also be enabled
+ if (g_pConfig && g_pConfig->StressLog())
+ {
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnBadExit))
+ {
+ // Workaround for aspnet
+ PathString wszFilename;
+ bool bShouldAssert = true;
+ if (WszGetModuleFileName(NULL, wszFilename))
+ {
+ wszFilename.LowerCase();
+
+ if (wcsstr(wszFilename, W("aspnet_compiler")))
+ {
+ bShouldAssert = false;
+ }
+ }
+
+ unsigned goodExit = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_SuccessExit);
+ if (bShouldAssert && exitCode != goodExit)
+ {
+ _ASSERTE(!"Bad Exit value");
+ FAULT_NOT_FATAL(); // if we OOM we can simply give up
+ SetErrorMode(0); // Insure that we actually cause the messsage box to pop.
+ EEMessageBoxCatastrophic(IDS_EE_ERRORMESSAGETEMPLATE, IDS_EE_ERRORTITLE, exitCode, W("BreakOnBadExit: returning bad exit code"));
+ }
+ }
+ }
+
+ // If we call ExitProcess, other threads will be torn down
+ // so we don't get to debug their state. Stop this!
+#ifdef _DEBUG
+ if (_DbgBreakCount)
+ _ASSERTE(!"In SafeExitProcess: An assert was hit on some other thread");
+#endif
+
+ // Turn off exception processing, because if some other random DLL has a
+ // fault in DLL_PROCESS_DETACH, we could get called for exception handling.
+ // Since we've turned off part of the runtime, we can't, for instance,
+ // properly execute the GC that handling an exception might trigger.
+ g_fNoExceptions = true;
+ LOG((LF_EH, LL_INFO10, "SafeExitProcess: turning off exceptions\n"));
+
+ if (sca == SCA_ExitProcessWhenShutdownComplete)
+ {
+ // disabled because if we fault in this code path we will trigger our
+ // Watson code via EntryPointFilter which is THROWS (see Dev11 317016)
+ CONTRACT_VIOLATION(ThrowsViolation);
+
+#ifdef FEATURE_PAL
+ if (fAbort)
+ {
+ TerminateProcess(GetCurrentProcess(), exitCode);
+ }
+#endif
+
+ EEPolicy::ExitProcessViaShim(exitCode);
+ }
+}
+
+// This is a helper to exit the process after coordinating with the shim. It is used by
+// SafeExitProcess above, as well as from CorHost2::ExitProcess when we know that we must
+// exit the process without doing further work to shutdown this runtime. This first attempts
+// to call back to the Shim to shutdown any other runtimes within the process.
+//
+// IMPORTANT NOTE: exercise extreme caution when adding new calls to this method. It is highly
+// likely that you want to call SafeExitProcess, or EEPolicy::HandleExitProcess instead of this.
+// This function only exists to factor some common code out of the methods mentioned above.
+
+//static
+void EEPolicy::ExitProcessViaShim(UINT exitCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // We must call back to the Shim in order to exit the process, as this may be just one
+ // runtime in a process with many. We need to give the other runtimes a chance to exit
+ // cleanly. If we can't make the call, or if the call fails for some reason, then we
+ // simply exit the process here, which is rude to the others, but the best we can do.
+#if !defined(FEATURE_CORECLR)
+ {
+ ReleaseHolder<ICLRRuntimeHostInternal> pRuntimeHostInternal;
+
+ HRESULT hr = g_pCLRRuntime->GetInterface(CLSID_CLRRuntimeHostInternal,
+ IID_ICLRRuntimeHostInternal,
+ &pRuntimeHostInternal);
+
+ if (SUCCEEDED(hr))
+ {
+ pRuntimeHostInternal->ShutdownAllRuntimesThenExit(exitCode);
+ LOG((LF_EH, LL_INFO10, "ExitProcessViaShim: shim returned... exiting now.\n"));
+ }
+ }
+#endif // !FEATURE_CORECLR
+
+ ExitProcess(exitCode);
+}
+
+
+//---------------------------------------------------------------------------------------
+// DisableRuntime disables this runtime, suspending all managed execution and preventing
+// threads from entering the runtime. This will cause the caller to block forever as well
+// unless sca is SCA_ReturnWhenShutdownComplete.
+//---------------------------------------------------------------------------------------
+void DisableRuntime(ShutdownCompleteAction sca)
+{
+ CONTRACTL
+ {
+ DISABLED(GC_TRIGGERS);
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ FastInterlockExchange((LONG*)&g_fForbidEnterEE, TRUE);
+
+ if (!g_fSuspendOnShutdown)
+ {
+ if (!IsGCThread())
+ {
+ if (ThreadStore::HoldingThreadStore(GetThread()))
+ {
+ ThreadSuspend::UnlockThreadStore();
+ }
+ ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_SHUTDOWN);
+ }
+
+ if (!g_fSuspendOnShutdown)
+ {
+ ThreadStore::TrapReturningThreads(TRUE);
+ g_fSuspendOnShutdown = TRUE;
+ ClrFlsSetThreadType(ThreadType_Shutdown);
+ }
+
+ // Don't restart runtime. CLR is disabled.
+ }
+
+ GCX_PREEMP_NO_DTOR();
+
+ ProcessEventForHost(Event_ClrDisabled, NULL);
+ ClrFlsClearThreadType(ThreadType_Shutdown);
+
+ if (g_pDebugInterface != NULL)
+ {
+ g_pDebugInterface->DisableDebugger();
+ }
+
+ if (sca == SCA_ExitProcessWhenShutdownComplete)
+ {
+ __SwitchToThread(INFINITE, CALLER_LIMITS_SPINNING);
+ _ASSERTE (!"Should not reach here");
+ SafeExitProcess(0);
+ }
+}
+
+//---------------------------------------------------------------------------------------
+// HandleExitProcessHelper is used to shutdown the runtime as specified by the given
+// action, then to exit the process. Note, however, that the process will not exit if
+// sca is SCA_ReturnWhenShutdownComplete. In that case, this method will simply return after
+// performing the shutdown actions.
+//---------------------------------------------------------------------------------------
+
+// If g_fFastExitProcess is 0, normal shutdown
+// If g_fFastExitProcess is 1, fast shutdown. Only doing log.
+// If g_fFastExitProcess is 2, do not run EEShutDown.
+DWORD g_fFastExitProcess = 0;
+
+extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading);
+
+static void HandleExitProcessHelper(EPolicyAction action, UINT exitCode, ShutdownCompleteAction sca)
+{
+ WRAPPER_NO_CONTRACT;
+
+ switch (action) {
+ case eFastExitProcess:
+ g_fFastExitProcess = 1;
+ case eExitProcess:
+ if (g_fEEStarted)
+ {
+ EEShutDown(FALSE);
+ }
+ if (exitCode == 0)
+ {
+ exitCode = GetLatchedExitCode();
+ }
+ SafeExitProcess(exitCode, FALSE, sca);
+ break;
+ case eRudeExitProcess:
+ g_fFastExitProcess = 2;
+ SafeExitProcess(exitCode, TRUE, sca);
+ break;
+ case eDisableRuntime:
+ DisableRuntime(sca);
+ break;
+ default:
+ _ASSERTE (!"Invalid policy");
+ break;
+ }
+}
+
+
+EPolicyAction EEPolicy::DetermineResourceConstraintAction(Thread *pThread)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ EPolicyAction action;
+ if (pThread->HasLockInCurrentDomain()) {
+ action = GetEEPolicy()->GetActionOnFailure(FAIL_CriticalResource);
+ }
+ else
+ action = GetEEPolicy()->GetActionOnFailure(FAIL_NonCriticalResource);
+
+ AppDomain *pDomain = GetAppDomain();
+ // If it is default domain, we can not unload the appdomain
+ if (pDomain == SystemDomain::System()->DefaultDomain() &&
+ (action == eUnloadAppDomain || action == eRudeUnloadAppDomain))
+ {
+ action = eThrowException;
+ }
+ // If the current thread is AD unload helper thread, it should not block itself.
+ else if (pThread->HasThreadStateNC(Thread::TSNC_ADUnloadHelper) &&
+ action < eExitProcess)
+ {
+ action = eThrowException;
+ }
+ return action;
+}
+
+
+void EEPolicy::PerformADUnloadAction(EPolicyAction action, BOOL haveStack, BOOL forStackOverflow)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_COOPERATIVE;
+
+ STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::PerformADUnloadAction\n");
+
+ Thread *pThread = GetThread();
+
+ AppDomain *pDomain = GetAppDomain();
+
+ if (!IsFinalizerThread())
+ {
+ int count = 0;
+ Frame *pFrame = pThread->GetFirstTransitionInto(GetAppDomain(), &count);
+ {
+ pThread->SetUnloadBoundaryFrame(pFrame);
+ }
+ }
+
+ pDomain->EnableADUnloadWorker(action==eUnloadAppDomain? ADU_Safe : ADU_Rude);
+ // Can't perform a join when we are handling a true SO. We need to enable the unload woker but let the thread continue running
+ // through EH processing so that we can recover the stack and reset the guard page.
+ if (haveStack)
+ {
+ pThread->SetAbortRequest(action==eUnloadAppDomain? EEPolicy::TA_V1Compatible : EEPolicy::TA_Rude);
+ if (forStackOverflow)
+ {
+ OBJECTREF exceptObj = CLRException::GetPreallocatedRudeThreadAbortException();
+ pThread->SetAbortInitiated();
+ RaiseTheExceptionInternalOnly(exceptObj, FALSE, TRUE);
+ }
+
+ OBJECTREF exceptObj = CLRException::GetPreallocatedThreadAbortException();
+ pThread->SetAbortInitiated();
+ RaiseTheExceptionInternalOnly(exceptObj, FALSE, FALSE);
+ }
+}
+
+void EEPolicy::PerformResourceConstraintAction(Thread *pThread, EPolicyAction action, UINT exitCode, BOOL haveStack)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(GetAppDomain() != NULL);
+
+ switch (action) {
+ case eThrowException:
+ // Caller is going to rethrow.
+ return;
+ break;
+ case eAbortThread:
+ pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+ case eRudeAbortThread:
+ pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+ case eUnloadAppDomain:
+ case eRudeUnloadAppDomain:
+ {
+ GCX_ASSERT_COOP();
+ PerformADUnloadAction(action,haveStack);
+ }
+ break;
+ case eExitProcess:
+ case eFastExitProcess:
+ case eRudeExitProcess:
+ case eDisableRuntime:
+ HandleExitProcessFromEscalation(action, exitCode);
+ break;
+ default:
+ _ASSERTE (!"Invalid policy");
+ break;
+ }
+}
+
+void EEPolicy::HandleOutOfMemory()
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE (g_pOutOfMemoryExceptionClass);
+
+ Thread *pThread = GetThread();
+ _ASSERTE (pThread);
+
+ EPolicyAction action = DetermineResourceConstraintAction(pThread);
+
+ // Check if we are executing in the context of a Constrained Execution Region.
+ if (action != eThrowException && Thread::IsExecutingWithinCer())
+ {
+ // Hitting OOM in a CER region should throw the OOM without regard to the escalation policy
+ // since the CER author has declared they are hardened against such failures. That's
+ // the whole point of CERs, to denote regions where code knows exactly how to deal with
+ // failures in an attempt to minimize the need for rollback or recycling.
+ return;
+ }
+
+ PerformResourceConstraintAction(pThread, action, HOST_E_EXITPROCESS_OUTOFMEMORY, TRUE);
+}
+
+#ifdef FEATURE_STACK_PROBE
+//---------------------------------------------------------------------------------------
+//
+// IsSOTolerant - Is the current thread in SO Tolerant region?
+//
+// Arguments:
+// pLimitFrame: the limit of search for frames
+//
+// Return Value:
+// TRUE if in SO tolerant region.
+// FALSE if in SO intolerant region.
+//
+// Note:
+// We walk our frame chain to decide. If HelperMethodFrame is seen first, we are in tolerant
+// region. If EnterSOIntolerantCodeFrame is seen first, we are in intolerant region.
+//
+BOOL Thread::IsSOTolerant(void * pLimitFrame)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Frame *pFrame = GetFrame();
+ void* pSOIntolerantMarker = ClrFlsGetValue(TlsIdx_SOIntolerantTransitionHandler);
+ if (pSOIntolerantMarker == FRAME_TOP)
+ {
+ // We have not set a marker for intolerant transition yet.
+ return TRUE;
+ }
+ while (pFrame != FRAME_TOP && pFrame < pLimitFrame)
+ {
+ Frame::ETransitionType type = pFrame->GetTransitionType();
+ if (pFrame > pSOIntolerantMarker)
+ {
+ return FALSE;
+ }
+ else if (type == Frame::TT_M2U || type == Frame::TT_InternalCall ||
+ // We can not call HelperMethodFrame::GetFunction on SO since the call
+ // may need to call into host. This is why we check for TT_InternalCall first.
+ pFrame->GetFunction() != NULL)
+ {
+ return TRUE;
+ }
+ pFrame = pFrame->Next();
+ }
+
+ if (pFrame == FRAME_TOP)
+ // We walked to the end of chain, but the thread has one IntolerantMarker on stack decided from
+ // the check above while loop.
+ return FALSE;
+ else
+ return TRUE;
+}
+
+#endif
+
+//---------------------------------------------------------------------------------------
+//
+// EEPolicy::HandleStackOverflow - Handle stack overflow according to policy
+//
+// Arguments:
+// detector:
+// pLimitFrame: the limit of search for frames in order to decide if in SO tolerant
+//
+// Return Value:
+// None.
+//
+// How is stack overflow handled?
+// If stack overflows in non-hosted case, we terminate the process.
+// For hosted case with escalation policy
+// 1. If stack overflows in managed code, or in VM before switching to SO intolerant region, and the GC mode is Cooperative
+// the domain is rudely unloaded, or the process is terminated if the current domain is default domain.
+// a. This action is done through BEGIN_SO_TOLERANT_CODE if there is one.
+// b. If there is not this macro on the stack, we mark the domain being unload requested, and when the thread
+// dies or is recycled, we finish the AD unload.
+// 2. If stack overflows in SO tolerant region, but the GC mode is Preemptive, the process is killed in vector handler, or our
+// managed exception handler (COMPlusFrameHandler or ProcessCLRException).
+// 3. If stack overflows in SO intolerant region, the process is killed as soon as the exception is seen by our vector handler, or
+// our managed exception handler.
+//
+// If SO Probing code is disabled (by FEATURE_STACK_PROBE not defined) then the process
+// is terminated if there is StackOverflow as all clr code will be considered SO Intolerant.
+void EEPolicy::HandleStackOverflow(StackOverflowDetector detector, void * pLimitFrame)
+{
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleStackOverflow\n");
+
+ Thread *pThread = GetThread();
+
+ if (pThread == NULL)
+ {
+ //_ASSERTE (detector != SOD_ManagedFrameHandler);
+ // ProcessSOEventForHost(NULL, FALSE);
+
+ // For security reason, it is not safe to continue execution if stack overflow happens
+ // unless a host tells us to do something different.
+ // EEPolicy::HandleFatalStackOverflow(NULL);
+ return;
+ }
+
+#ifdef FEATURE_STACK_PROBE
+
+ // We only process SO once at
+ // 1. VectoredExceptionHandler if SO in mscorwks
+ // 2. managed exception handler
+ // 3. SO_Tolerant transition handler
+ if (pThread->HasThreadStateNC(Thread::TSNC_SOWorkNeeded) &&
+ detector != SOD_UnmanagedFrameHandler)
+ {
+ return;
+ }
+#endif
+
+#ifdef FEATURE_STACK_PROBE
+ BOOL fInSoTolerant = pThread->IsSOTolerant(pLimitFrame);
+#else
+ BOOL fInSoTolerant = false;
+#endif
+
+ EXCEPTION_POINTERS exceptionInfo;
+ GetCurrentExceptionPointers(&exceptionInfo);
+
+ _ASSERTE(exceptionInfo.ExceptionRecord);
+
+#ifdef FEATURE_STACK_PROBE
+ DWORD exceptionCode = exceptionInfo.ExceptionRecord->ExceptionCode;
+
+ AppDomain *pCurrentDomain = ::GetAppDomain();
+ BOOL fInDefaultDomain = (pCurrentDomain == SystemDomain::System()->DefaultDomain());
+ BOOL fInCLR = IsIPInModule(g_pMSCorEE, (PCODE)GetIP(exceptionInfo.ContextRecord));
+
+ if (exceptionCode == EXCEPTION_SOFTSO)
+ {
+ // Our probe detects a thread does not have enough stack. But we have not trashed the process
+ // state yet.
+ fInSoTolerant = TRUE;
+ }
+ else
+ {
+ _ASSERTE (exceptionCode == STATUS_STACK_OVERFLOW);
+
+ switch (detector)
+ {
+ case SOD_ManagedFrameHandler:
+ if (!pThread->PreemptiveGCDisabled() && !fInCLR && fInSoTolerant
+ &&
+ // Before we call managed code, we probe inside ReverseEnterRuntime for BACKOUT_CODE_STACK_LIMIT pages
+ // If we hit hard so here, we are still in our stub
+ (!CLRTaskHosted() || (UINT_PTR)pThread->m_pFrame - pThread->GetLastAllowableStackAddress() >=
+ ADJUST_PROBE(BACKOUT_CODE_STACK_LIMIT) * OS_PAGE_SIZE)
+ )
+ {
+ // Managed exception handler detects SO, but the thread is in preemptive GC mode,
+ // and the IP is outside CLR. This means we are inside a PINVOKE call.
+ fInSoTolerant = FALSE;
+ }
+ break;
+
+ case SOD_UnmanagedFrameHandler:
+ break;
+
+ case SOD_SOIntolerantTransitor:
+ fInSoTolerant = FALSE;
+ break;
+
+ case SOD_SOTolerantTransitor:
+ if (!fInCLR)
+ {
+ // If SO happens outside of CLR, and it is not detected by managed frame handler,
+ // it is fatal
+ fInSoTolerant = FALSE;
+ }
+ break;
+
+ default:
+ _ASSERTE(!"should not get here");
+ }
+
+ if (fInDefaultDomain)
+ {
+ // StackOverflow in default domain is fatal
+ fInSoTolerant = FALSE;
+ }
+ }
+
+#endif // FEATURE_STACK_PROBE
+
+ ProcessSOEventForHost(&exceptionInfo, fInSoTolerant);
+
+#ifdef FEATURE_STACK_PROBE
+ if (!CLRHosted() || GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) != eRudeUnloadAppDomain)
+ {
+ // For security reason, it is not safe to continue execution if stack overflow happens
+ // unless a host tells us to do something different.
+ EEPolicy::HandleFatalStackOverflow(&exceptionInfo);
+ }
+#endif
+
+ if (!fInSoTolerant)
+ {
+ EEPolicy::HandleFatalStackOverflow(&exceptionInfo);
+ }
+#ifdef FEATURE_STACK_PROBE
+ else
+ {
+ // EnableADUnloadWorker is SO_Intolerant.
+ // But here we know that if we have only one page, we will only update states of the Domain.
+ CONTRACT_VIOLATION(SOToleranceViolation);
+
+ // Mark the current domain requested for rude unload
+ if (!fInDefaultDomain)
+ {
+ pCurrentDomain->EnableADUnloadWorker(ADU_Rude, FALSE);
+ }
+
+ pThread->PrepareThreadForSOWork();
+
+ pThread->MarkThreadForAbort(
+ (Thread::ThreadAbortRequester)(Thread::TAR_Thread|Thread::TAR_StackOverflow),
+ EEPolicy::TA_Rude);
+
+ pThread->SetSOWorkNeeded();
+ }
+#endif
+}
+
+
+// We provide WatsonLastChance with a SO exception record. The ExceptionAddress is set to 0
+// here. This ExceptionPointers struct is handed off to the debugger as is. A copy of this struct
+// is made before invoking Watson and the ExceptionAddress is set by inspecting the stack. Note
+// that the ExceptionContext member is unused and so it's ok to set it to NULL.
+static EXCEPTION_RECORD g_SOExceptionRecord = {
+ STATUS_STACK_OVERFLOW, // ExceptionCode
+ 0, // ExceptionFlags
+ NULL, // ExceptionRecord
+ 0, // ExceptionAddress
+ 0, // NumberOfParameters
+ {} }; // ExceptionInformation
+
+EXCEPTION_POINTERS g_SOExceptionPointers = {&g_SOExceptionRecord, NULL};
+
+#ifdef FEATURE_STACK_PROBE
+// This function may be called on a thread before debugger is notified of the thread, like in
+// ManagedThreadBase_DispatchMiddle. Currently we can not notify managed debugger, because
+// RS requires that notification is sent first.
+void EEPolicy::HandleSoftStackOverflow(BOOL fSkipDebugger)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If we trigger a SO while handling the soft stack overflow,
+ // we'll rip the process
+ BEGIN_SO_INTOLERANT_CODE_NOPROBE;
+
+ AppDomain *pCurrentDomain = ::GetAppDomain();
+
+ if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) != eRudeUnloadAppDomain ||
+ pCurrentDomain == SystemDomain::System()->DefaultDomain())
+ {
+ // We may not be able to build a context on stack
+ ProcessSOEventForHost(NULL, FALSE);
+
+
+ EEPolicy::HandleFatalStackOverflow(&g_SOExceptionPointers, fSkipDebugger);
+ }
+ //else if (pCurrentDomain == SystemDomain::System()->DefaultDomain())
+ //{
+ // We hit soft SO in Default domain, but default domain can not be unloaded.
+ // Soft SO can happen in default domain, eg. GetResourceString, or EnsureGrantSetSerialized.
+ // So the caller is going to throw a managed exception.
+ // RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL);
+ //}
+ else
+ {
+ Thread* pThread = GetThread();
+
+ if (pThread && pThread->PreemptiveGCDisabled())
+ {
+ // Mark the current domain requested for rude unload
+ GCX_ASSERT_COOP();
+ EEPolicy::PerformADUnloadAction(eRudeUnloadAppDomain, TRUE, TRUE);
+ }
+
+ // We are leaving VM boundary, either entering managed code, or entering
+ // non-VM unmanaged code.
+ // We should not throw internal C++ exception. Instead we throw an exception
+ // with EXCEPTION_SOFTSO code.
+ RaiseException(EXCEPTION_SOFTSO, 0, 0, NULL);
+ }
+
+ END_SO_INTOLERANT_CODE_NOPROBE;
+
+}
+
+void EEPolicy::HandleStackOverflowAfterCatch()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SO_TOLERANT;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+#ifdef STACK_GUARDS_DEBUG
+ BaseStackGuard::RestoreCurrentGuard(FALSE);
+#endif
+ Thread *pThread = GetThread();
+ pThread->RestoreGuardPage();
+ pThread->FinishSOWork();
+}
+#endif
+
+
+//---------------------------------------------------------------------------------------
+// HandleExitProcess is used to shutdown the runtime, based on policy previously set,
+// then to exit the process. Note, however, that the process will not exit if
+// sca is SCA_ReturnWhenShutdownComplete. In that case, this method will simply return after
+// performing the shutdown actions.
+//---------------------------------------------------------------------------------------
+void EEPolicy::HandleExitProcess(ShutdownCompleteAction sca)
+{
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleExitProcess\n");
+
+ EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL);
+ GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ProcessExit,action);
+ HandleExitProcessHelper(action, 0, sca);
+}
+
+//
+// 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)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_ANY;
+
+ _ASSERTE(pExceptionInfo != NULL);
+
+ if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
+ {
+ // Fire an ETW FailFast event
+ FireEtwFailFast(pszMessage,
+ (const PVOID)address,
+ ((pExceptionInfo && pExceptionInfo->ExceptionRecord) ? pExceptionInfo->ExceptionRecord->ExceptionCode : 0),
+ exitCode,
+ GetClrInstanceId());
+ }
+
+#ifndef FEATURE_PAL
+ // Write an event log entry. We do allocate some resources here (spread between the stack and maybe the heap for longer
+ // messages), so it's possible for the event write to fail. If needs be we can use a more elaborate scheme here in the future
+ // (maybe trying multiple approaches and backing off on failure, falling back on a limited size static buffer as a last
+ // resort). In all likelihood the Win32 event reporting mechanism requires resources though, so it's not clear how much
+ // effort we should put into this without knowing the benefit we'd receive.
+ EX_TRY
+ {
+ if (ShouldLogInEventLog())
+ {
+ // If the exit code is COR_E_FAILFAST then the fatal error was raised by managed code and the address argument points to a
+ // unicode message buffer rather than a faulting EIP.
+ EventReporter::EventReporterType failureType = EventReporter::ERT_UnmanagedFailFast;
+ if (exitCode == (UINT)COR_E_FAILFAST)
+ failureType = EventReporter::ERT_ManagedFailFast;
+ else if (exitCode == (UINT)COR_E_CODECONTRACTFAILED)
+ failureType = EventReporter::ERT_CodeContractFailed;
+ EventReporter reporter(failureType);
+
+
+ if ((exitCode == (UINT)COR_E_FAILFAST) || (exitCode == (UINT)COR_E_CODECONTRACTFAILED) || (exitCode == (UINT)CLR_E_GC_OOM))
+ {
+ if (pszMessage)
+ {
+ reporter.AddDescription((WCHAR*)pszMessage);
+ }
+
+ if (exitCode != (UINT)CLR_E_GC_OOM)
+ LogCallstackForEventReporter(reporter);
+ }
+ else
+ {
+ // Fetch the localized Fatal Execution Engine Error text or fall back on a hardcoded variant if things get dire.
+ InlineSString<80> ssMessage;
+ InlineSString<80> ssErrorFormat;
+ if(!ssErrorFormat.LoadResource(CCompRC::Optional, IDS_ER_UNMANAGEDFAILFASTMSG ))
+ ssErrorFormat.Set(W("at IP %1 (%2) with exit code %3."));
+ SmallStackSString addressString;
+ addressString.Printf(W("%p"), pExceptionInfo? (UINT_PTR)pExceptionInfo->ExceptionRecord->ExceptionAddress : address);
+
+ // We should always have the reference to the runtime's instance
+ _ASSERTE(g_pMSCorEE != NULL);
+
+ // Setup the string to contain the runtime's base address. Thus, when customers report FEEE with just
+ // the event log entry containing this string, we can use the absolute and base addresses to determine
+ // where the fault happened inside the runtime.
+ SmallStackSString runtimeBaseAddressString;
+ runtimeBaseAddressString.Printf(W("%p"), g_pMSCorEE);
+
+ SmallStackSString exitCodeString;
+ exitCodeString.Printf(W("%x"), exitCode);
+
+ // Format the string
+ ssMessage.FormatMessage(FORMAT_MESSAGE_FROM_STRING, (LPCWSTR)ssErrorFormat, 0, 0, addressString, runtimeBaseAddressString,
+ exitCodeString);
+ reporter.AddDescription(ssMessage);
+ }
+
+ reporter.Report();
+ }
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+#endif // !FEATURE_PAL
+
+#ifdef _DEBUG
+ // If we're native-only (Win32) debugging this process, we'd love to break now.
+ // However, we should not do this because a managed debugger attached to a
+ // SxS runtime also appears to be a native debugger. Unfortunately, the managed
+ // debugger won't handle any native event from another runtime, which means this
+ // breakpoint would go unhandled and terminate the process. Instead, we will let
+ // the process continue so at least the fatal error is logged rather than abrupt
+ // termination.
+ //
+ // This behavior can still be overridden if the right config value is set.
+ if (IsDebuggerPresent())
+ {
+ bool fBreak = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgOOBinFEEE) != 0);
+
+ if (fBreak)
+ {
+ DebugBreak();
+ }
+ }
+#endif // _DEBUG
+
+ // We're here logging a fatal error. If the policy is to then do anything other than
+ // disable the runtime (ie, if the policy is to terminate the runtime), we should give
+ // Watson an opportunity to capture an error report.
+ // Presumably, hosts that are sophisticated enough to disable the runtime are also cognizant
+ // of how they want to handle fatal errors in the runtime, including whether they want
+ // to capture Watson information (for which they are responsible).
+ if (GetEEPolicy()->GetActionOnFailureNoHostNotification(FAIL_FatalRuntime) != eDisableRuntime)
+ {
+#ifdef DEBUGGING_SUPPORTED
+ //Give a managed debugger a chance if this fatal error is on a managed thread.
+ Thread *pThread = GetThread();
+
+ if (pThread)
+ {
+ GCX_COOP();
+
+ OBJECTHANDLE ohException = NULL;
+
+ if (exitCode == (UINT)COR_E_STACKOVERFLOW)
+ {
+ // If we're going down because of stack overflow, go ahead and use the preallocated SO exception.
+ ohException = CLRException::GetPreallocatedStackOverflowExceptionHandle();
+ }
+ else
+ {
+ // Though we would like to remove the usage of ExecutionEngineException in any manner,
+ // we cannot. Its okay to use it in the case below since the process is terminating
+ // and this will serve as an exception object for debugger.
+ ohException = CLRException::GetPreallocatedExecutionEngineExceptionHandle();
+ }
+
+ // Preallocated exception handles can be null if FailFast is invoked before LoadBaseSystemClasses
+ // (in SystemDomain::Init) finished. See Dev10 Bug 677432 for the detail.
+ if (ohException != NULL)
+ {
+ // for fail-fast, if there's a LTO available then use that as the inner exception object
+ // for the FEEE we'll be reporting. this can help the Watson back-end to generate better
+ // buckets for apps that call Environment.FailFast() and supply an exception object.
+ OBJECTREF lto = pThread->LastThrownObject();
+
+ if (exitCode == static_cast<UINT>(COR_E_FAILFAST) && lto != NULL)
+ {
+ EXCEPTIONREF curEx = (EXCEPTIONREF)ObjectFromHandle(ohException);
+ curEx->SetInnerException(lto);
+ }
+ pThread->SetLastThrownObject(ObjectFromHandle(ohException), TRUE);
+ }
+
+ // If a managed debugger is already attached, and if that debugger is thinking it might be inclined to
+ // try to intercept this excepiton, then tell it that's not possible.
+ if (pThread->IsExceptionInProgress())
+ {
+ pThread->GetExceptionState()->GetFlags()->SetDebuggerInterceptNotPossible();
+ }
+ }
+
+ if (EXCEPTION_CONTINUE_EXECUTION == WatsonLastChance(pThread, pExceptionInfo, TypeOfReportedError::FatalError))
+ {
+ LOG((LF_EH, LL_INFO100, "EEPolicy::LogFatalError: debugger ==> EXCEPTION_CONTINUE_EXECUTION\n"));
+ _ASSERTE(!"Debugger should not have returned ContinueExecution");
+ }
+#endif // DEBUGGING_SUPPORTED
+ }
+}
+
+void DisplayStackOverflowException()
+{
+ LIMITED_METHOD_CONTRACT;
+ PrintToStdErrA("\n");
+
+ PrintToStdErrA("Process is terminated due to StackOverflowException.\n");
+}
+
+void DECLSPEC_NORETURN EEPolicy::HandleFatalStackOverflow(EXCEPTION_POINTERS *pExceptionInfo, BOOL fSkipDebugger)
+{
+ // This is fatal error. We do not care about SO mode any more.
+ // All of the code from here on out is robust to any failures in any API's that are called.
+ CONTRACT_VIOLATION(GCViolation | ModeViolation | SOToleranceViolation | FaultNotFatal | TakesLockViolation);
+
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG0(LF_EH, LL_INFO100, "In EEPolicy::HandleFatalStackOverflow\n");
+
+ DisplayStackOverflowException();
+
+ if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
+ {
+ // Fire an ETW FailFast event
+ FireEtwFailFast(W("StackOverflowException"),
+ (const PVOID)((pExceptionInfo && pExceptionInfo->ContextRecord) ? GetIP(pExceptionInfo->ContextRecord) : 0),
+ ((pExceptionInfo && pExceptionInfo->ExceptionRecord) ? pExceptionInfo->ExceptionRecord->ExceptionCode : 0),
+ COR_E_STACKOVERFLOW,
+ GetClrInstanceId());
+ }
+
+ if (!fSkipDebugger)
+ {
+ Thread *pThread = GetThread();
+ BOOL fTreatAsNativeUnhandledException = FALSE;
+ if (pThread)
+ {
+ GCX_COOP();
+ // If we had a SO before preallocated exception objects are initialized, we will AV here. This can happen
+ // during the initialization of SystemDomain during EEStartup. Thus, setup the SO throwable only if its not
+ // NULL.
+ //
+ // When WatsonLastChance (WLC) is invoked below, it treats this case as UnhandledException. If there is no
+ // managed exception object available, we should treat this case as NativeUnhandledException. This aligns
+ // well with the fact that there cannot be a managed debugger attached at this point that will require
+ // LastChanceManagedException notification to be delivered. Also, this is the same as how
+ // we treat an unhandled exception as NativeUnhandled when throwable is not available.
+ OBJECTHANDLE ohSO = CLRException::GetPreallocatedStackOverflowExceptionHandle();
+ if (ohSO != NULL)
+ {
+ pThread->SafeSetThrowables(ObjectFromHandle(ohSO)
+ DEBUG_ARG(ThreadExceptionState::STEC_CurrentTrackerEqualNullOkHackForFatalStackOverflow),
+ TRUE);
+ }
+ else
+ {
+ // We dont have a throwable - treat this as native unhandled exception
+ fTreatAsNativeUnhandledException = TRUE;
+ }
+ }
+ FrameWithCookie<FaultingExceptionFrame> fef;
+#if defined(WIN64EXCEPTIONS)
+ *((&fef)->GetGSCookiePtr()) = GetProcessGSCookie();
+#endif // WIN64EXCEPTIONS
+ if (pExceptionInfo && pExceptionInfo->ContextRecord)
+ {
+ GCX_COOP();
+ fef.InitAndLink(pExceptionInfo->ContextRecord);
+ }
+
+#ifndef FEATURE_PAL
+ if (RunningOnWin7() && IsWatsonEnabled() && (g_pDebugInterface != NULL))
+ {
+ _ASSERTE(pExceptionInfo != NULL);
+
+ ResetWatsonBucketsParams param;
+ param.m_pThread = pThread;
+ param.pExceptionRecord = pExceptionInfo->ExceptionRecord;
+ g_pDebugInterface->RequestFavor(ResetWatsonBucketsFavorWorker, reinterpret_cast<void *>(&param));
+ }
+#endif // !FEATURE_PAL
+
+ WatsonLastChance(pThread, pExceptionInfo,
+ (fTreatAsNativeUnhandledException == FALSE)? TypeOfReportedError::UnhandledException: TypeOfReportedError::NativeThreadUnhandledException);
+ }
+
+ TerminateProcess(GetCurrentProcess(), COR_E_STACKOVERFLOW);
+ UNREACHABLE();
+}
+
+void DECLSPEC_NORETURN EEPolicy::HandleFatalError(UINT exitCode, UINT_PTR address, LPCWSTR pszMessage /* = NULL */, PEXCEPTION_POINTERS pExceptionInfo /* = NULL */)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // All of the code from here on out is robust to any failures in any API's that are called.
+ FAULT_NOT_FATAL();
+
+ EXCEPTION_RECORD exceptionRecord;
+ EXCEPTION_POINTERS exceptionPointers;
+ CONTEXT context;
+
+ if (pExceptionInfo == NULL)
+ {
+ ZeroMemory(&exceptionPointers, sizeof(exceptionPointers));
+ ZeroMemory(&exceptionRecord, sizeof(exceptionRecord));
+ ZeroMemory(&context, sizeof(context));
+
+ context.ContextFlags = CONTEXT_CONTROL;
+ ClrCaptureContext(&context);
+
+ exceptionRecord.ExceptionCode = exitCode;
+ exceptionRecord.ExceptionAddress = reinterpret_cast< PVOID >(address);
+
+ exceptionPointers.ExceptionRecord = &exceptionRecord;
+ exceptionPointers.ContextRecord = &context;
+ pExceptionInfo = &exceptionPointers;
+ }
+
+ // All of the code from here on out is allowed to trigger a GC, even if we're in a no-trigger region. We're
+ // ripping the process down due to a fatal error... our invariants are already gone.
+ {
+ // This is fatal error. We do not care about SO mode any more.
+ // All of the code from here on out is robust to any failures in any API's that are called.
+ CONTRACT_VIOLATION(GCViolation | ModeViolation | SOToleranceViolation | FaultNotFatal | TakesLockViolation);
+
+ // ThreadStore lock needs to be released before continuing with the FatalError handling should
+ // because debugger is going to take CrstDebuggerMutex, whose lock level is higher than that of
+ // CrstThreadStore. It should be safe to release the lock since execution will not be resumed
+ // after fatal errors.
+ if (ThreadStore::HoldingThreadStore(GetThread()))
+ {
+ ThreadSuspend::UnlockThreadStore();
+ }
+
+ g_fFastExitProcess = 2;
+
+ STRESS_LOG0(LF_CORDB,LL_INFO100, "D::HFE: About to call LogFatalError\n");
+ switch (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime))
+ {
+ case eRudeExitProcess:
+ LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+ SafeExitProcess(exitCode, TRUE);
+ break;
+ case eDisableRuntime:
+ LogFatalError(exitCode, address, pszMessage, pExceptionInfo);
+ DisableRuntime(SCA_ExitProcessWhenShutdownComplete);
+ break;
+ default:
+ _ASSERTE(!"Invalid action for FAIL_FatalRuntime");
+ break;
+ }
+ }
+
+ UNREACHABLE();
+}
+
+void EEPolicy::HandleExitProcessFromEscalation(EPolicyAction action, UINT exitCode)
+{
+ WRAPPER_NO_CONTRACT;
+ CONTRACT_VIOLATION(GCViolation);
+
+ _ASSERTE (action >= eExitProcess);
+ // If policy for ExitProcess is not default action, i.e. ExitProcess, we will use it.
+ // Otherwise overwrite it with passing arg action;
+ EPolicyAction todo = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL);
+ if (todo == eExitProcess)
+ {
+ todo = action;
+ }
+ GetEEPolicy()->NotifyHostOnDefaultAction(OPR_ProcessExit,todo);
+
+ HandleExitProcessHelper(todo, exitCode, SCA_ExitProcessWhenShutdownComplete);
+}
+
+void EEPolicy::HandleCodeContractFailure(LPCWSTR pMessage, LPCWSTR pCondition, LPCWSTR pInnerExceptionAsString)
+{
+ WRAPPER_NO_CONTRACT;
+
+ EEPolicy* pPolicy = GetEEPolicy();
+ // GetActionOnFailure will notify the host for us.
+ EPolicyAction action = pPolicy->GetActionOnFailure(FAIL_CodeContract);
+ Thread* pThread = GetThread();
+ AppDomain* pCurrentDomain = ::GetAppDomain();
+
+ switch(action) {
+ case eThrowException:
+ // Let managed code throw a ContractException (it's easier to pass the right parameters to the constructor).
+ break;
+ case eAbortThread:
+ pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+ case eRudeAbortThread:
+ pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+ case eUnloadAppDomain:
+ // Register an appdomain unload, which starts on a separate thread.
+ IfFailThrow(AppDomain::UnloadById(pCurrentDomain->GetId(), FALSE));
+ // Don't continue execution on this thread.
+ pThread->UserAbort(Thread::TAR_Thread, TA_Safe, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+ case eRudeUnloadAppDomain:
+ pCurrentDomain->SetRudeUnload();
+ // Register an appdomain unload, which starts on a separate thread.
+ IfFailThrow(AppDomain::UnloadById(pCurrentDomain->GetId(), FALSE));
+ // Don't continue execution on this thread.
+ pThread->UserAbort(Thread::TAR_Thread, TA_Rude, GetEEPolicy()->GetTimeout(OPR_ThreadAbort), Thread::UAC_Normal);
+ break;
+
+ case eExitProcess: // Merged w/ default case
+ default:
+ _ASSERTE(action == eExitProcess);
+ // Since we have no exception object, make sure
+ // UE tracker is clean so that RetrieveManagedBucketParameters
+ // does not take any bucket details.
+#ifndef FEATURE_PAL
+ pThread->GetExceptionState()->GetUEWatsonBucketTracker()->ClearWatsonBucketDetails();
+#endif // !FEATURE_PAL
+ pPolicy->HandleFatalError(COR_E_CODECONTRACTFAILED, NULL, pMessage);
+ break;
+ }
+}
+