1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
19 #include "dbginterface.h"
21 #include "comutilnative.h"
22 #include "siginfo.hpp"
23 #include "gcheaputilities.h"
24 #include "eedbginterfaceimpl.h" //so we can clearexception in RealCOMPlusThrow
25 #include "perfcounters.h"
26 #include "dllimportcallback.h"
27 #include "stackwalk.h" //for CrawlFrame, in SetIPFromSrcToDst
30 #include "virtualcallstub.h"
31 #include "typestring.h"
35 #endif // !FEATURE_PAL
37 #include "eventreporter.h"
39 #ifdef FEATURE_COMINTEROP
40 #include<roerrorapi.h>
42 #ifdef WIN64EXCEPTIONS
43 #include "exceptionhandling.h"
48 // Include definition of GenericModeBlock
53 // Support for extracting MethodDesc of a delegate.
54 #include "comdelegate.h"
58 // Windows uses 64kB as the null-reference area
59 #define NULL_AREA_SIZE (64 * 1024)
61 #define NULL_AREA_SIZE GetOsPageSize()
62 #endif // !FEATURE_PAL
64 #ifndef CROSSGEN_COMPILE
66 BOOL IsIPInEE(void *ip);
68 //----------------------------------------------------------------------------
70 // IsExceptionFromManagedCode - determine if pExceptionRecord points to a managed exception
73 // pExceptionRecord - pointer to exception record
78 //----------------------------------------------------------------------------
79 BOOL IsExceptionFromManagedCode(const EXCEPTION_RECORD * pExceptionRecord)
86 PRECONDITION(CheckPointer(pExceptionRecord));
89 if (pExceptionRecord == NULL)
94 DACCOP_IGNORE(FieldAccess, "EXCEPTION_RECORD is a OS structure, and ExceptionAddress is actually a target address here.");
95 UINT_PTR address = reinterpret_cast<UINT_PTR>(pExceptionRecord->ExceptionAddress);
97 // An exception code of EXCEPTION_COMPLUS indicates a managed exception
98 // has occurred (most likely due to executing a "throw" instruction).
100 // Also, a hardware level exception may not have an exception code of
101 // EXCEPTION_COMPLUS. In this case, an exception address that resides in
102 // managed code indicates a managed exception has occurred.
103 return (IsComPlusException(pExceptionRecord) ||
104 (ExecutionManager::IsManagedCode((PCODE)address)));
108 #ifndef DACCESS_COMPILE
110 #define SZ_UNHANDLED_EXCEPTION W("Unhandled Exception:")
111 #define SZ_UNHANDLED_EXCEPTION_CHARLEN ((sizeof(SZ_UNHANDLED_EXCEPTION) / sizeof(WCHAR)))
115 OBJECTREF pThrowable;
117 OBJECTREF pTmpThrowable;
120 PEXCEPTION_REGISTRATION_RECORD GetCurrentSEHRecord();
121 BOOL IsUnmanagedToManagedSEHHandler(EXCEPTION_REGISTRATION_RECORD*);
123 VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow
124 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
125 , CorruptionSeverity severity = NotCorrupting
126 #endif // FEATURE_CORRUPTING_EXCEPTIONS
129 //-------------------------------------------------------------------------------
130 // Basically, this asks whether the exception is a managed exception thrown by
131 // this instance of the CLR.
133 // The way the result is used, however, is to decide whether this instance is the
134 // one to throw up the Watson box.
135 //-------------------------------------------------------------------------------
136 BOOL ShouldOurUEFDisplayUI(PEXCEPTION_POINTERS pExceptionInfo)
138 STATIC_CONTRACT_NOTHROW;
139 STATIC_CONTRACT_GC_NOTRIGGER;
140 STATIC_CONTRACT_FORBID_FAULT;
142 // Test first for the canned SO EXCEPTION_POINTERS structure as it has a NULL context record and will break the code below.
143 extern EXCEPTION_POINTERS g_SOExceptionPointers;
144 if (pExceptionInfo == &g_SOExceptionPointers)
148 return IsComPlusException(pExceptionInfo->ExceptionRecord) || ExecutionManager::IsManagedCode(GetIP(pExceptionInfo->ContextRecord));
151 BOOL NotifyAppDomainsOfUnhandledException(
152 PEXCEPTION_POINTERS pExceptionPointers,
153 OBJECTREF *pThrowableIn,
154 BOOL useLastThrownObject,
157 VOID SetManagedUnhandledExceptionBit(
158 BOOL useLastThrownObject);
161 void COMPlusThrowBoot(HRESULT hr)
163 STATIC_CONTRACT_THROWS;
165 _ASSERTE(g_fEEShutDown >= ShutDown_Finalize2 || !"This should not be called unless we are in the last phase of shutdown!");
167 RaiseException(BOOTUP_EXCEPTION_COMPLUS, EXCEPTION_NONCONTINUABLE, 1, &arg);
171 //-------------------------------------------------------------------------------
172 // This simply tests to see if the exception object is a subclass of
173 // the descriminating class specified in the exception clause.
174 //-------------------------------------------------------------------------------
175 BOOL ExceptionIsOfRightType(TypeHandle clauseType, TypeHandle thrownType)
186 // if not resolved to, then it wasn't loaded and couldn't have been thrown
187 if (clauseType.IsNull())
190 if (clauseType == thrownType)
193 // now look for parent match
194 TypeHandle superType = thrownType;
195 while (!superType.IsNull()) {
196 if (superType == clauseType) {
199 superType = superType.GetParent();
202 return !superType.IsNull();
205 //===========================================================================
206 // Gets the message text from an exception
207 //===========================================================================
208 ULONG GetExceptionMessage(OBJECTREF throwable,
209 __inout_ecount(bufferLength) LPWSTR buffer,
217 INJECT_FAULT(ThrowOutOfMemory());
221 // Prefast buffer sanity check. Don't call the API with a zero length buffer.
222 if (bufferLength == 0)
224 _ASSERTE(bufferLength > 0);
229 GetExceptionMessage(throwable, result);
231 ULONG length = result.GetCount();
232 LPCWSTR chars = result.GetUnicode();
234 if (length < bufferLength)
236 wcsncpy_s(buffer, bufferLength, chars, length);
240 wcsncpy_s(buffer, bufferLength, chars, bufferLength-1);
246 //-----------------------------------------------------------------------------
247 // Given an object, get the "message" from it. If the object is an Exception
248 // call Exception.InternalToString, otherwise, call Object.ToString
249 //-----------------------------------------------------------------------------
250 void GetExceptionMessage(OBJECTREF throwable, SString &result)
257 INJECT_FAULT(ThrowOutOfMemory());
261 STRINGREF pString = GetExceptionMessage(throwable);
263 // If call returned NULL (not empty), oh well, no message.
265 pString->GetSString(result);
266 } // void GetExceptionMessage()
268 #if FEATURE_COMINTEROP
269 // This method returns IRestrictedErrorInfo associated with the ErrorObject.
270 // It checks whether the given managed exception object has __HasRestrictedLanguageErrorObject set
271 // in which case it returns the IRestrictedErrorInfo associated with the __RestrictedErrorObject.
272 IRestrictedErrorInfo* GetRestrictedErrorInfoFromErrorObject(OBJECTREF throwable)
279 INJECT_FAULT(ThrowOutOfMemory());
283 IRestrictedErrorInfo* pRestrictedErrorInfo = NULL;
285 // If there is no object, there is no restricted error.
286 if (throwable == NULL)
289 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
290 if (!IsException(throwable->GetMethodTable()))
297 OBJECTREF RestrictedErrorInfoObjRef;
300 ZeroMemory(&gc, sizeof(gc));
303 gc.Throwable = throwable;
305 // Get the MethodDesc on which we'll call.
306 MethodDescCallSite getRestrictedLanguageErrorObject(METHOD__EXCEPTION__TRY_GET_RESTRICTED_LANGUAGE_ERROR_OBJECT, &gc.Throwable);
311 ObjToArgSlot(gc.Throwable),
312 PtrToArgSlot(&gc.RestrictedErrorInfoObjRef)
315 BOOL bHasLanguageRestrictedErrorObject = (BOOL)getRestrictedLanguageErrorObject.Call_RetBool(Args);
317 if(bHasLanguageRestrictedErrorObject)
319 // The __RestrictedErrorObject represents IRestrictedErrorInfo RCW of a non-CLR platform. Lets get the corresponding IRestrictedErrorInfo for it.
320 pRestrictedErrorInfo = (IRestrictedErrorInfo *)GetComIPFromObjectRef(&gc.RestrictedErrorInfoObjRef, IID_IRestrictedErrorInfo);
325 return pRestrictedErrorInfo;
329 STRINGREF GetExceptionMessage(OBJECTREF throwable)
336 INJECT_FAULT(ThrowOutOfMemory());
340 // If there is no object, there is no message.
341 if (throwable == NULL)
344 // Assume we're calling Exception.InternalToString() ...
345 BinderMethodID sigID = METHOD__EXCEPTION__INTERNAL_TO_STRING;
347 // ... but if it isn't an exception, call Object.ToString().
348 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
349 if (!IsException(throwable->GetMethodTable()))
351 sigID = METHOD__OBJECT__TO_STRING;
355 STRINGREF pString = NULL;
357 GCPROTECT_BEGIN(throwable);
359 // Get the MethodDesc on which we'll call.
360 MethodDescCallSite toString(sigID, &throwable);
363 ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
364 pString = toString.Call_RetSTRINGREF(arg);
371 HRESULT GetExceptionHResult(OBJECTREF throwable)
383 if (throwable == NULL)
386 // Since any object can be thrown in managed code, not only instances of System.Exception subclasses
387 // we need to check to see if we are dealing with an exception before attempting to retrieve
388 // the HRESULT field. If we are not dealing with an exception, then we will simply return E_FAIL.
389 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
390 if (IsException(throwable->GetMethodTable()))
392 hr = ((EXCEPTIONREF)throwable)->GetHResult();
396 } // HRESULT GetExceptionHResult()
398 DWORD GetExceptionXCode(OBJECTREF throwable)
410 if (throwable == NULL)
413 // Since any object can be thrown in managed code, not only instances of System.Exception subclasses
414 // we need to check to see if we are dealing with an exception before attempting to retrieve
415 // the HRESULT field. If we are not dealing with an exception, then we will simply return E_FAIL.
416 _ASSERTE(IsException(throwable->GetMethodTable())); // what is the pathway here?
417 if (IsException(throwable->GetMethodTable()))
419 hr = ((EXCEPTIONREF)throwable)->GetXCode();
423 } // DWORD GetExceptionXCode()
425 //------------------------------------------------------------------------------
426 // This function will extract some information from an Access Violation SEH
427 // exception, and store it in the System.AccessViolationException object.
428 // - the faulting instruction's IP.
429 // - the target address of the faulting instruction.
430 // - a code indicating attempted read vs write
431 //------------------------------------------------------------------------------
432 void SetExceptionAVParameters( // No return.
433 OBJECTREF throwable, // The object into which to set the values.
434 EXCEPTION_RECORD *pExceptionRecord) // The SEH exception information.
441 PRECONDITION(throwable != NULL);
445 GCPROTECT_BEGIN(throwable)
447 // This should only be called for AccessViolationException
448 _ASSERTE(MscorlibBinder::GetException(kAccessViolationException) == throwable->GetMethodTable());
450 FieldDesc *pFD_ip = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__IP);
451 FieldDesc *pFD_target = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__TARGET);
452 FieldDesc *pFD_access = MscorlibBinder::GetField(FIELD__ACCESS_VIOLATION_EXCEPTION__ACCESSTYPE);
454 _ASSERTE(pFD_ip->GetFieldType() == ELEMENT_TYPE_I);
455 _ASSERTE(pFD_target->GetFieldType() == ELEMENT_TYPE_I);
456 _ASSERTE(pFD_access->GetFieldType() == ELEMENT_TYPE_I4);
458 void *ip = pExceptionRecord->ExceptionAddress;
459 void *target = (void*)(pExceptionRecord->ExceptionInformation[1]);
460 DWORD access = (DWORD)(pExceptionRecord->ExceptionInformation[0]);
462 pFD_ip->SetValuePtr(throwable, ip);
463 pFD_target->SetValuePtr(throwable, target);
464 pFD_access->SetValue32(throwable, access);
469 } // void SetExceptionAVParameters()
471 //------------------------------------------------------------------------------
472 // This will call InternalPreserveStackTrace (if the throwable derives from
473 // System.Exception), to copy the stack trace to the _remoteStackTraceString.
474 // Doing so allows the stack trace of an exception caught by the runtime, and
475 // rethrown with COMPlusThrow(OBJECTREF thowable), to be preserved. Otherwise
476 // the exception handling code may clear the stack trace. (Generally, we see
477 // the stack trace preserved on win32 and cleared on win64.)
478 //------------------------------------------------------------------------------
479 void ExceptionPreserveStackTrace( // No return.
480 OBJECTREF throwable) // Object about to be thrown.
487 INJECT_FAULT(ThrowOutOfMemory());
491 // If there is no object, there is no stack trace to save.
492 if (throwable == NULL)
495 GCPROTECT_BEGIN(throwable);
497 // Make sure it is derived from System.Exception, that it is not one of the
498 // preallocated exception objects, and that it has a stack trace to save.
499 if (IsException(throwable->GetMethodTable()) &&
500 !CLRException::IsPreallocatedExceptionObject(throwable))
502 LOG((LF_EH, LL_INFO1000, "ExceptionPreserveStackTrace called\n"));
504 // We're calling Exception.InternalPreserveStackTrace() ...
505 BinderMethodID sigID = METHOD__EXCEPTION__INTERNAL_PRESERVE_STACK_TRACE;
508 // Get the MethodDesc on which we'll call.
509 MethodDescCallSite preserveStackTrace(sigID, &throwable);
512 ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
513 preserveStackTrace.Call(arg);
518 } // void ExceptionPreserveStackTrace()
521 // We have to cache the MethodTable and FieldDesc for wrapped non-compliant exceptions the first
522 // time we wrap, because we cannot tolerate a GC when it comes time to detect and unwrap one.
524 static MethodTable *pMT_RuntimeWrappedException;
525 static FieldDesc *pFD_WrappedException;
527 // Non-compliant exceptions are immediately wrapped in a RuntimeWrappedException instance. The entire
528 // exception system can now ignore the possibility of these cases except:
530 // 1) IL_Throw, which must wrap via this API
531 // 2) Calls to Filters & Catch handlers, which must unwrap based on whether the assembly is on the legacy
534 void WrapNonCompliantException(OBJECTREF *ppThrowable)
541 PRECONDITION(IsProtectedByGCFrame(ppThrowable));
545 _ASSERTE(!IsException((*ppThrowable)->GetMethodTable()));
549 // idempotent operations, so the race condition is okay.
550 if (pMT_RuntimeWrappedException == NULL)
551 pMT_RuntimeWrappedException = MscorlibBinder::GetException(kRuntimeWrappedException);
553 if (pFD_WrappedException == NULL)
554 pFD_WrappedException = MscorlibBinder::GetField(FIELD__RUNTIME_WRAPPED_EXCEPTION__WRAPPED_EXCEPTION);
556 OBJECTREF orWrapper = AllocateObject(MscorlibBinder::GetException(kRuntimeWrappedException));
558 GCPROTECT_BEGIN(orWrapper);
560 MethodDescCallSite ctor(METHOD__RUNTIME_WRAPPED_EXCEPTION__OBJ_CTOR, &orWrapper);
564 ObjToArgSlot(orWrapper),
565 ObjToArgSlot(*ppThrowable)
570 *ppThrowable = orWrapper;
576 // If we took an exception while binding, or running the constructor of the RuntimeWrappedException
577 // instance, we know that this new exception is CLS compliant. In fact, it's likely to be
578 // OutOfMemoryException, StackOverflowException or ThreadAbortException.
579 OBJECTREF orReplacement = GET_THROWABLE();
581 _ASSERTE(IsException(orReplacement->GetMethodTable()));
583 *ppThrowable = orReplacement;
585 } EX_END_CATCH(SwallowAllExceptions);
588 // Before presenting an exception object to a handler (filter or catch, not finally or fault), it
589 // may be necessary to turn it back into a non-compliant exception. This is conditioned on an
590 // assembly level setting.
591 OBJECTREF PossiblyUnwrapThrowable(OBJECTREF throwable, Assembly *pAssembly)
593 // Check if we are required to compute the RuntimeWrapExceptions status.
594 BOOL fIsRuntimeWrappedException = ((throwable != NULL) && (throwable->GetMethodTable() == pMT_RuntimeWrappedException));
595 BOOL fRequiresComputingRuntimeWrapExceptionsStatus = (fIsRuntimeWrappedException &&
596 (!(pAssembly->GetManifestModule()->IsRuntimeWrapExceptionsStatusComputed())));
601 // If we are required to compute the status of RuntimeWrapExceptions, then the operation could trigger a GC.
602 // Thus, conditionally setup the contract.
603 if (fRequiresComputingRuntimeWrapExceptionsStatus) GC_TRIGGERS; else GC_NOTRIGGER;
605 PRECONDITION(CheckPointer(pAssembly));
609 if (fIsRuntimeWrappedException && (!pAssembly->GetManifestModule()->IsRuntimeWrapExceptions()))
611 // We already created the instance, fetched the field. We know it is
612 // not marshal by ref, or any of the other cases that might trigger GC.
613 ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
615 throwable = pFD_WrappedException->GetRefValue(throwable);
622 // This is used by a holder in CreateTypeInitializationExceptionObject to
623 // reset the state as appropriate.
624 void ResetTypeInitializationExceptionState(BOOL isAlreadyCreating)
626 LIMITED_METHOD_CONTRACT;
627 if (!isAlreadyCreating)
628 GetThread()->ResetIsCreatingTypeInitException();
631 void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed,
632 OBJECTREF *pInnerException,
633 OBJECTREF *pInitException,
634 OBJECTREF *pThrowable)
640 PRECONDITION(CheckPointer(pInnerException, NULL_OK));
641 PRECONDITION(CheckPointer(pInitException));
642 PRECONDITION(CheckPointer(pThrowable));
643 PRECONDITION(IsProtectedByGCFrame(pInnerException));
644 PRECONDITION(IsProtectedByGCFrame(pInitException));
645 PRECONDITION(IsProtectedByGCFrame(pThrowable));
646 PRECONDITION(CheckPointer(GetThread()));
649 Thread *pThread = GetThread();
652 // This will make sure to put the thread back to its original state if something
653 // throws out of this function (like an OOM exception or something)
654 Holder< BOOL, DoNothing< BOOL >, ResetTypeInitializationExceptionState, FALSE, NoNull< BOOL > >
655 isAlreadyCreating(pThread->IsCreatingTypeInitException());
658 // This will contain the type of exception we want to create. Read comment below
659 // on why we'd want to create an exception other than TypeInitException
661 BinderMethodID methodID;
663 // If we are already in the midst of creating a TypeInitializationException object,
664 // and we get here, it means there was an exception thrown while initializing the
665 // TypeInitializationException type itself, or one of the types used by its class
666 // constructor. In this case, we're going to back down and use a SystemException
667 // object in its place. It is *KNOWN* that both these exception types have identical
668 // .ctor sigs "void instance (string, exception)" so both can be used interchangeably
669 // in the code that follows.
670 if (!isAlreadyCreating.GetValue()) {
671 pThread->SetIsCreatingTypeInitException();
672 pMT = MscorlibBinder::GetException(kTypeInitializationException);
673 methodID = METHOD__TYPE_INIT_EXCEPTION__STR_EX_CTOR;
676 // If we ever hit one of these asserts, then it is bad
677 // because we do not know what exception to return then.
678 _ASSERTE(pInnerException != NULL);
679 _ASSERTE(*pInnerException != NULL);
680 *pThrowable = *pInnerException;
681 *pInitException = *pInnerException;
685 // Allocate the exception object
686 *pThrowable = AllocateObject(pMT);
688 MethodDescCallSite ctor(methodID, pThrowable);
690 // Since the inner exception object in the .ctor is of type Exception, make sure
691 // that the object we're passed in derives from Exception. If not, pass NULL.
692 BOOL isException = FALSE;
693 if (pInnerException != NULL)
694 isException = IsException((*pInnerException)->GetMethodTable());
696 _ASSERTE(isException); // What pathway can give us non-compliant exceptions?
698 STRINGREF sType = StringObject::NewString(pTypeThatFailed);
700 // If the inner object derives from exception, set it as the third argument.
701 ARG_SLOT args[] = { ObjToArgSlot(*pThrowable),
703 ObjToArgSlot(isException ? *pInnerException : NULL) };
708 // On success, set the init exception.
709 *pInitException = *pThrowable;
712 // If calling the constructor fails, then we'll call ourselves again, and this time
713 // through we will try and create an EEException object. If that fails, then the
714 // else block of this will be executed.
715 if (!isAlreadyCreating.GetValue()) {
716 CreateTypeInitializationExceptionObject(pTypeThatFailed, pInnerException, pInitException, pThrowable);
719 // If we were already in the middle of creating a type init
720 // exception when we were called, we would have tried to create an EEException instead
721 // of a TypeInitException.
723 // If we're recursing, then we should be calling ourselves from DoRunClassInitThrowing,
724 // in which case we're guaranteed that we're passing in all three arguments.
725 *pInitException = pInnerException ? *pInnerException : NULL;
726 *pThrowable = GET_THROWABLE();
728 } EX_END_CATCH(SwallowAllExceptions);
730 CONSISTENCY_CHECK(*pInitException != NULL || !pInnerException);
736 // ==========================================================================
737 // ComputeEnclosingHandlerNestingLevel
739 // This is code factored out of COMPlusThrowCallback to figure out
740 // what the number of nested exception handlers is.
741 // ==========================================================================
742 DWORD ComputeEnclosingHandlerNestingLevel(IJitManager *pIJM,
743 const METHODTOKEN& mdTok,
755 // Determine the nesting level of EHClause. Just walk the table
756 // again, and find out how many handlers enclose it
757 DWORD nestingLevel = 0;
758 EH_CLAUSE_ENUMERATOR pEnumState;
759 unsigned EHCount = pIJM->InitializeEHEnumeration(mdTok, &pEnumState);
761 for (unsigned j=0; j<EHCount; j++)
763 EE_ILEXCEPTION_CLAUSE EHClause;
765 pIJM->GetNextEHClause(&pEnumState,&EHClause);
766 _ASSERTE(EHClause.HandlerEndPC != (DWORD) -1); // <TODO> remove, only protects against a deprecated convention</TODO>
768 if ((offsNat > EHClause.HandlerStartPC) &&
769 (offsNat < EHClause.HandlerEndPC))
778 // ******************************* EHRangeTreeNode ************************** //
779 EHRangeTreeNode::EHRangeTreeNode(void)
782 CommonCtor(0, false);
785 EHRangeTreeNode::EHRangeTreeNode(DWORD offset, bool fIsRange /* = false */)
788 CommonCtor(offset, fIsRange);
791 void EHRangeTreeNode::CommonCtor(DWORD offset, bool fIsRange)
793 LIMITED_METHOD_CONTRACT;
798 m_pContainedBy = NULL;
801 m_fIsRange = fIsRange;
802 m_fIsRoot = false; // must set this flag explicitly
805 inline bool EHRangeTreeNode::IsRange()
807 // Please see the header file for an explanation of this assertion.
808 _ASSERTE(m_fIsRoot || m_clause != NULL || !m_fIsRange);
812 void EHRangeTreeNode::MarkAsRange()
819 inline bool EHRangeTreeNode::IsRoot()
821 // Please see the header file for an explanation of this assertion.
822 _ASSERTE(m_fIsRoot || m_clause != NULL || !m_fIsRange);
826 void EHRangeTreeNode::MarkAsRoot(DWORD offset)
833 inline DWORD EHRangeTreeNode::GetOffset()
835 _ASSERTE(m_clause == NULL);
836 _ASSERTE(IsRoot() || !IsRange());
840 inline DWORD EHRangeTreeNode::GetTryStart()
850 return m_clause->TryStartPC;
854 inline DWORD EHRangeTreeNode::GetTryEnd()
864 return m_clause->TryEndPC;
868 inline DWORD EHRangeTreeNode::GetHandlerStart()
878 return m_clause->HandlerStartPC;
882 inline DWORD EHRangeTreeNode::GetHandlerEnd()
892 return m_clause->HandlerEndPC;
896 inline DWORD EHRangeTreeNode::GetFilterStart()
906 return m_clause->FilterOffset;
910 // Get the end offset of the filter clause. This offset is exclusive.
911 inline DWORD EHRangeTreeNode::GetFilterEnd()
917 // We should never get here if the "this" node is the root.
918 // By definition, the root contains everything. No checking is necessary.
923 return m_FilterEndPC;
927 bool EHRangeTreeNode::Contains(DWORD offset)
931 EHRangeTreeNode node(offset);
932 return Contains(&node);
935 bool EHRangeTreeNode::TryContains(DWORD offset)
939 EHRangeTreeNode node(offset);
940 return TryContains(&node);
943 bool EHRangeTreeNode::HandlerContains(DWORD offset)
947 EHRangeTreeNode node(offset);
948 return HandlerContains(&node);
951 bool EHRangeTreeNode::FilterContains(DWORD offset)
955 EHRangeTreeNode node(offset);
956 return FilterContains(&node);
959 bool EHRangeTreeNode::Contains(EHRangeTreeNode* pNode)
961 LIMITED_METHOD_CONTRACT;
963 // If we are checking a range of address, then we should check the end address inclusively.
966 // No node contains the root node.
969 else if (this->IsRoot())
971 return (pNode->IsRange() ?
972 (pNode->GetTryEnd() <= this->GetOffset()) && (pNode->GetHandlerEnd() <= this->GetOffset())
973 : (pNode->GetOffset() < this->GetOffset()) );
977 return (this->TryContains(pNode) || this->HandlerContains(pNode) || this->FilterContains(pNode));
981 bool EHRangeTreeNode::TryContains(EHRangeTreeNode* pNode)
983 LIMITED_METHOD_CONTRACT;
985 _ASSERTE(this->IsRange());
989 // No node contains the root node.
992 else if (this->IsRoot())
994 // We will only get here from GetTcf() to determine if an address is in a try clause.
995 // In this case we want to return false.
1000 DWORD tryStart = this->GetTryStart();
1001 DWORD tryEnd = this->GetTryEnd();
1003 // If we are checking a range of address, then we should check the end address inclusively.
1004 if (pNode->IsRange())
1006 DWORD start = pNode->GetTryStart();
1007 DWORD end = pNode->GetTryEnd();
1009 if (start == tryStart && end == tryEnd)
1013 else if (start == end)
1015 // This is effectively a single offset.
1016 if ((tryStart <= start) && (end < tryEnd))
1021 else if ((tryStart <= start) && (end <= tryEnd))
1028 DWORD offset = pNode->GetOffset();
1029 if ((tryStart <= offset) && (offset < tryEnd))
1036 #ifdef WIN64EXCEPTIONS
1037 // If we are boot-strapping the tree, don't recurse down because the result could be unreliable. Note that
1038 // even if we don't recurse, given a particular node, we can still always find its most specific container with
1039 // the logic above, i.e. it's always safe to do one depth level of checking.
1041 // To build the tree, all we need to know is the most specific container of a particular node. This can be
1042 // done by just comparing the offsets of the try regions. However, funclets create a problem because even if
1043 // a funclet is conceptually contained in a try region, we cannot determine this fact just by comparing the offsets.
1044 // This is when we need to recurse the tree. Here is a classic example:
1052 // // If the offset is here, then we need to recurse.
1058 if (!m_pTree->m_fInitializing)
1060 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1061 // ask if the requested range is contained by it.
1063 USHORT numNodes = m_containees.Count();
1064 EHRangeTreeNode** ppNodes = NULL;
1065 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1067 // This variable is purely used for readability.
1068 EHRangeTreeNode* pNodeCur = *ppNodes;
1070 // it's possible for nested try blocks to have the same beginning and end offsets
1071 if ( ( this->GetTryStart() <= pNodeCur->GetTryStart() ) &&
1072 ( pNodeCur->GetTryEnd() <= this->GetTryEnd() ) )
1074 if (pNodeCur->Contains(pNode))
1081 #endif // WIN64EXCEPTIONS
1086 bool EHRangeTreeNode::HandlerContains(EHRangeTreeNode* pNode)
1088 LIMITED_METHOD_CONTRACT;
1090 _ASSERTE(this->IsRange());
1092 if (pNode->IsRoot())
1094 // No node contains the root node.
1097 else if (this->IsRoot())
1099 // We will only get here from GetTcf() to determine if an address is in a try clause.
1100 // In this case we want to return false.
1105 DWORD handlerStart = this->GetHandlerStart();
1106 DWORD handlerEnd = this->GetHandlerEnd();
1108 // If we are checking a range of address, then we should check the end address inclusively.
1109 if (pNode->IsRange())
1111 DWORD start = pNode->GetTryStart();
1112 DWORD end = pNode->GetTryEnd();
1114 if (start == handlerStart && end == handlerEnd)
1118 else if ((handlerStart <= start) && (end <= handlerEnd))
1125 DWORD offset = pNode->GetOffset();
1126 if ((handlerStart <= offset) && (offset < handlerEnd))
1133 #ifdef WIN64EXCEPTIONS
1134 // Refer to the comment in TryContains().
1135 if (!m_pTree->m_fInitializing)
1137 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1138 // ask if the requested range is contained by it.
1140 USHORT numNodes = m_containees.Count();
1141 EHRangeTreeNode** ppNodes = NULL;
1142 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1144 // This variable is purely used for readability.
1145 EHRangeTreeNode* pNodeCur = *ppNodes;
1147 if ( ( this->GetHandlerStart() <= pNodeCur->GetTryStart() ) &&
1148 ( pNodeCur->GetTryEnd() < this->GetHandlerEnd() ) )
1150 if (pNodeCur->Contains(pNode))
1157 #endif // WIN64EXCEPTIONS
1162 bool EHRangeTreeNode::FilterContains(EHRangeTreeNode* pNode)
1164 LIMITED_METHOD_CONTRACT;
1166 _ASSERTE(this->IsRange());
1168 if (pNode->IsRoot())
1170 // No node contains the root node.
1173 else if (this->IsRoot() || !IsFilterHandler(this->m_clause))
1175 // We will only get here from GetTcf() to determine if an address is in a try clause.
1176 // In this case we want to return false.
1181 DWORD filterStart = this->GetFilterStart();
1182 DWORD filterEnd = this->GetFilterEnd();
1184 // If we are checking a range of address, then we should check the end address inclusively.
1185 if (pNode->IsRange())
1187 DWORD start = pNode->GetTryStart();
1188 DWORD end = pNode->GetTryEnd();
1190 if (start == filterStart && end == filterEnd)
1194 else if ((filterStart <= start) && (end <= filterEnd))
1201 DWORD offset = pNode->GetOffset();
1202 if ((filterStart <= offset) && (offset < filterEnd))
1209 #ifdef WIN64EXCEPTIONS
1210 // Refer to the comment in TryContains().
1211 if (!m_pTree->m_fInitializing)
1213 // Iterate all the contained clauses, and for the ones which are contained in the try region,
1214 // ask if the requested range is contained by it.
1216 USHORT numNodes = m_containees.Count();
1217 EHRangeTreeNode** ppNodes = NULL;
1218 for (i = 0, ppNodes = m_containees.Table(); i < numNodes; i++, ppNodes++)
1220 // This variable is purely used for readability.
1221 EHRangeTreeNode* pNodeCur = *ppNodes;
1223 if ( ( this->GetFilterStart() <= pNodeCur->GetTryStart() ) &&
1224 ( pNodeCur->GetTryEnd() < this->GetFilterEnd() ) )
1226 if (pNodeCur->Contains(pNode))
1233 #endif // WIN64EXCEPTIONS
1238 EHRangeTreeNode* EHRangeTreeNode::GetContainer()
1240 return m_pContainedBy;
1243 HRESULT EHRangeTreeNode::AddNode(EHRangeTreeNode *pNode)
1250 INJECT_FAULT(return E_OUTOFMEMORY;);
1251 PRECONDITION(pNode != NULL);
1255 EHRangeTreeNode **ppEH = m_containees.Append();
1258 return E_OUTOFMEMORY;
1264 // ******************************* EHRangeTree ************************** //
1266 EHRangeTree::EHRangeTree(IJitManager* pIJM,
1267 const METHODTOKEN& methodToken,
1270 const DWORD * rgFunclet)
1272 LIMITED_METHOD_CONTRACT;
1274 LOG((LF_CORDB, LL_INFO10000, "EHRT::ERHT: already loaded!\n"));
1276 EH_CLAUSE_ENUMERATOR pEnumState;
1277 m_EHCount = pIJM->InitializeEHEnumeration(methodToken, &pEnumState);
1279 _ASSERTE(m_EHCount != 0xFFFFFFFF);
1287 m_fInitializing = true;
1291 m_rgClauses = new (nothrow) EE_ILEXCEPTION_CLAUSE[m_EHCount];
1292 if (m_rgClauses == NULL)
1294 m_hrInit = E_OUTOFMEMORY;
1299 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: m_ehcount:0x%x, m_rgClauses:0%x\n",
1300 m_EHCount, m_rgClauses));
1302 m_rgNodes = new (nothrow) EHRangeTreeNode[m_EHCount+1];
1303 if (m_rgNodes == NULL)
1305 m_hrInit = E_OUTOFMEMORY;
1309 //this contains everything, even stuff on the last IP
1310 m_root = &(m_rgNodes[m_EHCount]);
1311 m_root->MarkAsRoot(methodSize + 1);
1313 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: rgNodes:0x%x\n", m_rgNodes));
1317 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: About to leave!\n"));
1321 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: Sticking around!\n"));
1323 // First, load all the EH clauses into the object.
1324 for (i = 0; i < m_EHCount; i++)
1326 EE_ILEXCEPTION_CLAUSE * pEHClause = &(m_rgClauses[i]);
1328 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: i:0x%x!\n", i));
1330 pIJM->GetNextEHClause(&pEnumState, pEHClause);
1332 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: EHRTT_JIT_MANAGER got clause\n", i));
1334 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: clause 0x%x,"
1335 "addrof:0x%x\n", i, pEHClause ));
1337 _ASSERTE(pEHClause->HandlerEndPC != (DWORD) -1); // <TODO> remove, only protects against a deprecated convention</TODO>
1339 EHRangeTreeNode * pNodeCur = &(m_rgNodes[i]);
1341 pNodeCur->m_pTree = this;
1342 pNodeCur->m_clause = pEHClause;
1344 if (pEHClause->Flags == COR_ILEXCEPTION_CLAUSE_FILTER)
1346 #ifdef WIN64EXCEPTIONS
1347 // Because of funclets, there is no way to guarantee the placement of a filter.
1348 // Thus, we need to loop through the funclets to find the end offset.
1349 for (int f = 0; f < cFunclet; f++)
1351 // Check the start offset of the filter funclet.
1352 if (pEHClause->FilterOffset == rgFunclet[f])
1354 if (f < (cFunclet - 1))
1356 // If it's NOT the last funclet, use the start offset of the next funclet.
1357 pNodeCur->m_FilterEndPC = rgFunclet[f + 1];
1361 // If it's the last funclet, use the size of the method.
1362 pNodeCur->m_FilterEndPC = methodSize;
1367 #else // WIN64EXCEPTIONS
1368 // On x86, since the filter doesn't have an end FilterPC, the only way we can know the size
1369 // of the filter is if it's located immediately prior to it's handler and immediately after
1370 // its try region. We assume that this is, and if it isn't, we're so amazingly hosed that
1371 // we can't continue.
1372 if ((pEHClause->FilterOffset >= pEHClause->HandlerStartPC) ||
1373 (pEHClause->FilterOffset < pEHClause->TryEndPC))
1375 m_hrInit = CORDBG_E_SET_IP_IMPOSSIBLE;
1378 pNodeCur->m_FilterEndPC = pEHClause->HandlerStartPC;
1379 #endif // WIN64EXCEPTIONS
1382 pNodeCur->MarkAsRange();
1385 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: about to do the second pass\n"));
1388 // Second, for each EH, find it's most limited, containing clause
1389 // On WIN64, we have duplicate clauses. There are two types of duplicate clauses.
1391 // The first type is described in ExceptionHandling.cpp. This type doesn't add additional information to the
1392 // EH tree structure. For example, if an offset is in the try region of a duplicate clause of this type,
1393 // then some clause which comes before the duplicate clause should contain the offset in its handler region.
1394 // Therefore, even though this type of duplicate clauses are added to the EH tree, they should never be used.
1396 // The second type is what's called the protected clause. These clauses are used to mark the cloned finally
1397 // region. They have an empty try region. Here's an example:
1421 // A protected clause covers the B' region in the parent method. In essence you can think of the method as
1422 // having two try/finally regions, and that's exactly how protected clauses are handled in the EH tree.
1423 // They are added to the EH tree just like any other EH clauses.
1424 for (i = 0; i < m_EHCount; i++)
1426 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: SP:0x%x\n", i));
1428 EHRangeTreeNode * pNodeCur = &(m_rgNodes[i]);
1430 EHRangeTreeNode *pNodeCandidate = NULL;
1431 pNodeCandidate = FindContainer(pNodeCur);
1432 _ASSERTE(pNodeCandidate != NULL);
1434 pNodeCur->m_pContainedBy = pNodeCandidate;
1436 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: SP: about to add to tree\n"));
1438 HRESULT hr = pNodeCandidate->AddNode(pNodeCur);
1447 m_fInitializing = false;
1451 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: LError - something went wrong!\n"));
1453 if (m_rgClauses != NULL)
1455 delete [] m_rgClauses;
1459 if (m_rgNodes != NULL)
1461 delete [] m_rgNodes;
1465 m_fInitializing = false;
1467 LOG((LF_CORDB, LL_INFO10000, "EHRT::CC: Falling off of LError!\n"));
1470 EHRangeTree::~EHRangeTree()
1472 LIMITED_METHOD_CONTRACT;
1474 if (m_rgNodes != NULL)
1475 delete [] m_rgNodes;
1477 if (m_rgClauses != NULL)
1478 delete [] m_rgClauses;
1481 EHRangeTreeNode *EHRangeTree::FindContainer(EHRangeTreeNode *pNodeSearch)
1483 LIMITED_METHOD_CONTRACT;
1485 EHRangeTreeNode *pNodeCandidate = NULL;
1487 // Examine the root, too.
1488 for (ULONG iInner = 0; iInner < m_EHCount+1; iInner++)
1490 EHRangeTreeNode *pNodeCur = &(m_rgNodes[iInner]);
1492 // Check if the current node contains the node we are searching for.
1493 if ((pNodeSearch != pNodeCur) &&
1494 pNodeCur->Contains(pNodeSearch))
1496 // Update the candidate node if it is NULL or if it contains the current node
1497 // (i.e. the current node is more specific than the candidate node).
1498 if ((pNodeCandidate == NULL) ||
1499 pNodeCandidate->Contains(pNodeCur))
1501 pNodeCandidate = pNodeCur;
1506 return pNodeCandidate;
1509 EHRangeTreeNode *EHRangeTree::FindMostSpecificContainer(DWORD addr)
1511 WRAPPER_NO_CONTRACT;
1513 EHRangeTreeNode node(addr);
1514 return FindContainer(&node);
1517 EHRangeTreeNode *EHRangeTree::FindNextMostSpecificContainer(EHRangeTreeNode *pNodeSearch, DWORD addr)
1519 WRAPPER_NO_CONTRACT;
1521 _ASSERTE(!m_fInitializing);
1523 EHRangeTreeNode **rgpNodes = pNodeSearch->m_containees.Table();
1525 if (NULL == rgpNodes)
1528 // It's possible that no subrange contains the desired address, so
1529 // keep a reasonable default around.
1530 EHRangeTreeNode *pNodeCandidate = pNodeSearch;
1532 USHORT cSubRanges = pNodeSearch->m_containees.Count();
1533 EHRangeTreeNode **ppNodeCur = pNodeSearch->m_containees.Table();
1535 for (int i = 0; i < cSubRanges; i++, ppNodeCur++)
1537 if ((*ppNodeCur)->Contains(addr) &&
1538 pNodeCandidate->Contains((*ppNodeCur)))
1540 pNodeCandidate = (*ppNodeCur);
1544 return pNodeCandidate;
1547 BOOL EHRangeTree::isAtStartOfCatch(DWORD offset)
1549 LIMITED_METHOD_CONTRACT;
1551 if (NULL != m_rgNodes && m_EHCount != 0)
1553 for(unsigned i = 0; i < m_EHCount;i++)
1555 if (m_rgNodes[i].m_clause->HandlerStartPC == offset &&
1556 (!IsFilterHandler(m_rgNodes[i].m_clause) && !IsFaultOrFinally(m_rgNodes[i].m_clause)))
1564 enum TRY_CATCH_FINALLY
1571 TCF_COUNT, //count of all elements, not an element itself
1575 const char *TCFStringFromConst(TRY_CATCH_FINALLY tcf)
1577 LIMITED_METHOD_CONTRACT;
1588 return "TCF_FILTER";
1591 return "TCFS_CATCH";
1594 return "TCFS_FINALLY";
1597 return "TCFS_COUNT";
1600 return "INVALID TCFS VALUE";
1606 #ifndef WIN64EXCEPTIONS
1607 // We're unwinding if we'll return to the EE's code. Otherwise
1608 // we'll return to someplace in the current code. Anywhere outside
1609 // this function is "EE code".
1610 bool FinallyIsUnwinding(EHRangeTreeNode *pNode,
1611 ICodeManager* pEECM,
1624 const BYTE *pbRetAddr = pEECM->GetFinallyReturnAddr(pReg);
1626 if (pbRetAddr < (const BYTE *)addrStart)
1629 DWORD offset = (DWORD)(size_t)(pbRetAddr - addrStart);
1630 EHRangeTreeNode *pRoot = pNode->m_pTree->m_root;
1632 if (!pRoot->Contains(offset))
1638 BOOL LeaveCatch(ICodeManager* pEECM,
1641 GCInfoToken gcInfoToken,
1652 // We can assert these things here, and skip a call
1653 // to COMPlusCheckForAbort later.
1655 // If no abort has been requested,
1656 _ASSERTE((pThread->GetThrowable() != NULL) ||
1657 // or if there is a pending exception.
1658 (!pThread->IsAbortRequested()) );
1660 LPVOID esp = COMPlusEndCatchWorker(pThread);
1662 PopNestedExceptionRecords(esp, pCtx, pThread->GetExceptionListPtr());
1664 // Do JIT-specific work
1665 pEECM->LeaveCatch(gcInfoToken, offset, pCtx);
1667 SetSP(pCtx, (UINT_PTR)esp);
1670 #endif // WIN64EXCEPTIONS
1672 TRY_CATCH_FINALLY GetTcf(EHRangeTreeNode *pNode,
1684 _ASSERTE(pNode->IsRange() && !pNode->IsRoot());
1686 TRY_CATCH_FINALLY tcf;
1688 if (!pNode->Contains(offset))
1692 else if (pNode->TryContains(offset))
1696 else if (pNode->FilterContains(offset))
1702 _ASSERTE(pNode->HandlerContains(offset));
1703 if (IsFaultOrFinally(pNode->m_clause))
1712 const DWORD bEnter = 0x01;
1713 const DWORD bLeave = 0x02;
1715 HRESULT IsLegalTransition(Thread *pThread,
1718 EHRangeTreeNode *pNode,
1721 ICodeManager* pEECM,
1724 GCInfoToken gcInfoToken,
1736 if (fEnter & bEnter)
1738 _ASSERTE(pNode->Contains(offTo));
1740 if (fEnter & bLeave)
1742 _ASSERTE(pNode->Contains(offFrom));
1746 // First, figure out where we're coming from/going to
1747 TRY_CATCH_FINALLY tcfFrom = GetTcf(pNode,
1750 TRY_CATCH_FINALLY tcfTo = GetTcf(pNode,
1753 LOG((LF_CORDB, LL_INFO10000, "ILT: from %s to %s\n",
1754 TCFStringFromConst(tcfFrom),
1755 TCFStringFromConst(tcfTo)));
1757 // Now we'll consider, case-by-case, the various permutations that
1775 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1781 return CORDBG_E_CANT_SET_IP_INTO_CATCH;
1787 return CORDBG_E_CANT_SET_IP_INTO_FINALLY;
1805 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1827 #if !defined(WIN64EXCEPTIONS)
1828 CONTEXT *pFilterCtx = pThread->GetFilterContext();
1829 if (pFilterCtx == NULL)
1830 return CORDBG_E_SET_IP_IMPOSSIBLE;
1834 if (!LeaveCatch(pEECM,
1842 #else // WIN64EXCEPTIONS
1844 // Setting IP out of a catch clause is not supported for WIN64EXCEPTIONS because of funclets.
1845 // This scenario is disabled with approval from VS because it's not considered to
1846 // be a common user scenario.
1848 return CORDBG_E_CANT_SET_IP_OUT_OF_CATCH_ON_WIN64;
1849 #endif // !WIN64EXCEPTIONS
1855 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1867 return CORDBG_E_CANT_SET_IP_INTO_FINALLY;
1883 #ifndef WIN64EXCEPTIONS
1884 if (!FinallyIsUnwinding(pNode, pEECM, pReg, addrStart))
1886 CONTEXT *pFilterCtx = pThread->GetFilterContext();
1887 if (pFilterCtx == NULL)
1888 return CORDBG_E_SET_IP_IMPOSSIBLE;
1892 if (!pEECM->LeaveFinally(gcInfoToken,
1901 return CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY;
1903 #else // !WIN64EXCEPTIONS
1905 // Setting IP out of a non-unwinding finally clause is not supported on WIN64EXCEPTIONS because of funclets.
1906 // This scenario is disabled with approval from VS because it's not considered to be a common user
1909 return CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY_ON_WIN64;
1910 #endif // WIN64EXCEPTIONS
1917 return CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER;
1923 return CORDBG_E_CANT_SET_IP_INTO_CATCH;
1942 _ASSERTE( !"IsLegalTransition: We should never reach this point!" );
1944 return CORDBG_E_SET_IP_IMPOSSIBLE;
1947 // We need this to determine what
1948 // to do based on whether the stack in general is empty
1949 HRESULT DestinationIsValid(void *pDjiToken,
1962 // We'll add a call to the DebugInterface that takes this
1963 // & tells us if the destination is a stack empty point.
1964 // DebuggerJitInfo *pDji = (DebuggerJitInfo *)pDjiToken;
1966 if (pEHRT->isAtStartOfCatch(offTo))
1967 return CORDBG_S_BAD_START_SEQUENCE_POINT;
1970 } // HRESULT DestinationIsValid()
1972 // We want to keep the 'worst' HRESULT - if one has failed (..._E_...) & the
1973 // other hasn't, take the failing one. If they've both/neither failed, then
1974 // it doesn't matter which we take.
1975 // Note that this macro favors retaining the first argument
1976 #define WORST_HR(hr1,hr2) (FAILED(hr1)?hr1:hr2)
1977 HRESULT SetIPFromSrcToDst(Thread *pThread,
1978 SLOT addrStart, // base address of method
1979 DWORD offFrom, // native offset
1980 DWORD offTo, // native offset
1981 bool fCanSetIPOnly, // if true, don't do any real work
1992 INJECT_FAULT(return E_OUTOFMEMORY;);
1997 HRESULT hrReturn = S_OK;
1998 bool fCheckOnly = true;
2000 EECodeInfo codeInfo((TADDR)(addrStart));
2002 ICodeManager * pEECM = codeInfo.GetCodeManager();
2003 GCInfoToken gcInfoToken = codeInfo.GetGCInfoToken();
2005 // Do both checks here so compiler doesn't complain about skipping
2006 // initialization b/c of goto.
2007 if (fCanSetIPOnly && !pEECM->IsGcSafe(&codeInfo, offFrom))
2009 hrReturn = WORST_HR(hrReturn, CORDBG_E_SET_IP_IMPOSSIBLE);
2012 if (fCanSetIPOnly && !pEECM->IsGcSafe(&codeInfo, offTo))
2014 hrReturn = WORST_HR(hrReturn, CORDBG_E_SET_IP_IMPOSSIBLE);
2017 if ((hr = DestinationIsValid(pDji, offTo, pEHRT)) != S_OK
2020 hrReturn = WORST_HR(hrReturn,hr);
2023 // The basic approach is this: We'll start with the most specific (smallest)
2024 // EHClause that contains the starting address. We'll 'back out', to larger
2025 // and larger ranges, until we either find an EHClause that contains both
2026 // the from and to addresses, or until we reach the root EHRangeTreeNode,
2027 // which contains all addresses within it. At each step, we check/do work
2028 // that the various transitions (from inside to outside a catch, etc).
2029 // At that point, we do the reverse process - we go from the EHClause that
2030 // encompasses both from and to, and narrow down to the smallest EHClause that
2031 // encompasses the to point. We use our nifty data structure to manage
2032 // the tree structure inherent in this process.
2034 // NOTE: We do this process twice, once to check that we're not doing an
2035 // overall illegal transition, such as ultimately set the IP into
2036 // a catch, which is never allowed. We're doing this because VS
2037 // calls SetIP without calling CanSetIP first, and so we should be able
2038 // to return an error code and have the stack in the same condition
2039 // as the start of the call, and so we shouldn't back out of clauses
2040 // or move into them until we're sure that can be done.
2044 EHRangeTreeNode *node;
2045 EHRangeTreeNode *nodeNext;
2046 node = pEHRT->FindMostSpecificContainer(offFrom);
2048 while (!node->Contains(offTo))
2050 hr = IsLegalTransition(pThread,
2064 hrReturn = WORST_HR(hrReturn,hr);
2067 node = node->GetContainer();
2068 // m_root prevents node from ever being NULL.
2071 if (node != pEHRT->m_root)
2073 hr = IsLegalTransition(pThread,
2087 hrReturn = WORST_HR(hrReturn,hr);
2091 nodeNext = pEHRT->FindNextMostSpecificContainer(node,
2094 while(nodeNext != node)
2096 hr = IsLegalTransition(pThread,
2110 hrReturn = WORST_HR(hrReturn, hr);
2114 nodeNext = pEHRT->FindNextMostSpecificContainer(node,
2118 // If it was the intention to actually set the IP and the above transition checks succeeded,
2119 // then go back and do it all again but this time widen and narrow the thread's actual scope
2120 if (!fCanSetIPOnly && fCheckOnly && SUCCEEDED(hrReturn))
2123 goto retryForCommit;
2127 } // HRESULT SetIPFromSrcToDst()
2129 // This function should only be called if the thread is suspended and sitting in jitted code
2130 BOOL IsInFirstFrameOfHandler(Thread *pThread, IJitManager *pJitManager, const METHODTOKEN& MethodToken, DWORD offset)
2141 // if don't have a throwable the aren't processing an exception
2142 if (IsHandleNullUnchecked(pThread->GetThrowableAsHandle()))
2145 EH_CLAUSE_ENUMERATOR pEnumState;
2146 unsigned EHCount = pJitManager->InitializeEHEnumeration(MethodToken, &pEnumState);
2148 for(ULONG i=0; i < EHCount; i++)
2150 EE_ILEXCEPTION_CLAUSE EHClause;
2151 pJitManager->GetNextEHClause(&pEnumState, &EHClause);
2152 _ASSERTE(IsValidClause(&EHClause));
2154 if ( offset >= EHClause.HandlerStartPC && offset < EHClause.HandlerEndPC)
2157 // check if it's in the filter itself if we're not in the handler
2158 if (IsFilterHandler(&EHClause) && offset >= EHClause.FilterOffset && offset < EHClause.HandlerStartPC)
2162 } // BOOL IsInFirstFrameOfHandler()
2165 #if !defined(WIN64EXCEPTIONS)
2167 //******************************************************************************
2168 // LookForHandler -- search for a function that will handle the exception.
2169 //******************************************************************************
2170 LFH LookForHandler( // LFH return types
2171 const EXCEPTION_POINTERS *pExceptionPointers, // The ExceptionRecord and ExceptionContext
2172 Thread *pThread, // Thread on which to look (always current?)
2173 ThrowCallbackType *tct) // Structure to pass back to callback functions.
2175 // We don't want to use a runtime contract here since this codepath is used during
2176 // the processing of a hard SO. Contracts use a significant amount of stack
2177 // which we can't afford for those cases.
2178 STATIC_CONTRACT_THROWS;
2179 STATIC_CONTRACT_GC_TRIGGERS;
2180 STATIC_CONTRACT_MODE_COOPERATIVE;
2182 // go through to find if anyone handles the exception
2183 StackWalkAction action = pThread->StackWalkFrames((PSTACKWALKFRAMESCALLBACK)COMPlusThrowCallback,
2185 0, //can't use FUNCTIONSONLY because the callback uses non-function frames to stop the walk
2188 // If someone handles it, the action will be SWA_ABORT with pFunc and dHandler indicating the
2189 // function and handler that is handling the exception. Debugger can put a hook in here.
2190 if (action == SWA_ABORT && tct->pFunc != NULL)
2193 // nobody is handling it
2194 return LFH_NOT_FOUND;
2195 } // LFH LookForHandler()
2197 StackWalkAction COMPlusUnwindCallback (CrawlFrame *pCf, ThrowCallbackType *pData);
2199 //******************************************************************************
2201 //******************************************************************************
2202 void UnwindFrames( // No return value.
2203 Thread *pThread, // Thread to unwind.
2204 ThrowCallbackType *tct) // Structure to pass back to callback function.
2206 STATIC_CONTRACT_THROWS;
2207 STATIC_CONTRACT_GC_NOTRIGGER;
2208 STATIC_CONTRACT_MODE_COOPERATIVE;
2210 if (pThread->IsExceptionInProgress())
2212 pThread->GetExceptionState()->GetFlags()->SetUnwindHasStarted();
2215 #ifdef DEBUGGING_SUPPORTED
2217 // If a debugger is attached, notify it that unwinding is going on.
2219 if (CORDebuggerAttached())
2221 g_pDebugInterface->ManagedExceptionUnwindBegin(pThread);
2223 #endif // DEBUGGING_SUPPORTED
2225 LOG((LF_EH, LL_INFO1000, "UnwindFrames: going to: pFunc:%#X, pStack:%#X\n",
2226 tct->pFunc, tct->pStack));
2228 pThread->StackWalkFrames((PSTACKWALKFRAMESCALLBACK)COMPlusUnwindCallback,
2232 } // void UnwindFrames()
2234 #endif // !defined(WIN64EXCEPTIONS)
2236 void StackTraceInfo::SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable, BOOL bReplaceStack, BOOL bSkipLastElement)
2246 // Do not save stacktrace to preallocated exception. These are shared.
2247 if (CLRException::IsPreallocatedExceptionHandle(hThrowable))
2249 // Preallocated exceptions will never have this flag set. However, its possible
2250 // that after this flag is set for a regular exception but before we throw, we have an async
2251 // exception like a RudeThreadAbort, which will replace the exception
2252 // containing the restored stack trace.
2254 // In such a case, we should clear the flag as the throwable representing the
2255 // preallocated exception will not have the restored (or any) stack trace.
2256 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
2257 pCurTES->ResetRaisingForeignException();
2262 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::SaveStackTrace (%p), alloc = %d, replace = %d, skiplast = %d\n", this, bAllowAllocMem, bReplaceStack, bSkipLastElement));
2264 // if have bSkipLastElement, must also keep the stack
2265 _ASSERTE(! bSkipLastElement || ! bReplaceStack);
2267 bool fSuccess = false;
2268 MethodTable* pMT = ObjectFromHandle(hThrowable)->GetTrueMethodTable();
2270 // Check if the flag indicating foreign exception raise has been setup or not,
2271 // and then reset it so that subsequent processing of managed frames proceeds
2273 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
2274 BOOL fRaisingForeignException = pCurTES->IsRaisingForeignException();
2275 pCurTES->ResetRaisingForeignException();
2277 if (bAllowAllocMem && m_dFrameCount != 0)
2281 // Only save stack trace info on exceptions
2282 _ASSERTE(IsException(pMT)); // what is the pathway here?
2283 if (!IsException(pMT))
2289 // If the stack trace contains DynamicMethodDescs, we need to save the corrosponding
2290 // System.Resolver objects in the Exception._dynamicMethods field. Failing to do that
2291 // will cause an AV in the runtime when we try to visit those MethodDescs in the
2292 // Exception._stackTrace field, because they have been recycled or destroyed.
2293 unsigned iNumDynamics = 0;
2295 // How many DynamicMethodDescs do we need to keep alive?
2296 for (unsigned iElement=0; iElement < m_dFrameCount; iElement++)
2298 MethodDesc *pMethod = m_pStackTrace[iElement].pFunc;
2301 if (pMethod->IsLCGMethod())
2303 // Increment the number of new dynamic methods we have found
2307 if (pMethod->GetMethodTable()->Collectible())
2315 StackTraceArray stackTrace;
2316 StackTraceArray stackTraceTemp;
2317 PTRARRAYREF dynamicMethodsArrayTemp;
2318 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
2319 PTRARRAYREF pOrigDynamicArray;
2324 , dynamicMethodsArrayTemp(static_cast<PTRArray *>(NULL))
2325 , dynamicMethodsArray(static_cast<PTRArray *>(NULL))
2326 , pOrigDynamicArray(static_cast<PTRArray *>(NULL))
2331 GCPROTECT_BEGIN(gc);
2333 // If the flag indicating foreign exception raise has been setup, then check
2334 // if the exception object has stacktrace or not. If we have an async non-preallocated
2335 // exception after setting this flag but before we throw, then the new
2336 // exception will not have any stack trace set and thus, we should behave as if
2337 // the flag was not setup.
2338 if (fRaisingForeignException)
2340 // Get the reference to stack trace and reset our flag if applicable.
2341 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->GetStackTrace(gc.stackTraceTemp);
2342 if (gc.stackTraceTemp.Size() == 0)
2344 fRaisingForeignException = FALSE;
2348 // Replace stack (i.e. build a new stack trace) only if we are not raising a foreign exception.
2349 // If we are, then we will continue to extend the existing stack trace.
2351 && (!fRaisingForeignException)
2354 // Cleanup previous info
2355 gc.stackTrace.Append(m_pStackTrace, m_pStackTrace + m_dFrameCount);
2359 // Adjust the allocation size of the array, if required
2360 if (iNumDynamics > m_cDynamicMethodItems)
2362 S_UINT32 cNewSize = S_UINT32(2) * S_UINT32(iNumDynamics);
2363 if (cNewSize.IsOverflow())
2365 // Overflow here implies we cannot allocate memory anymore
2366 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot calculate initial resolver array size due to overflow!\n"));
2370 m_cDynamicMethodItems = cNewSize.Value();
2373 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems, g_pObjectClass);
2374 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - allocated dynamic array for first frame of size %lu\n",
2375 m_cDynamicMethodItems));
2378 m_dCurrentDynamicIndex = 0;
2382 // Fetch the stacktrace and the dynamic method array
2383 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->GetStackTrace(gc.stackTrace, &gc.pOrigDynamicArray);
2385 if (fRaisingForeignException)
2387 // Just before we append to the stack trace, mark the last recorded frame to be from
2388 // the foreign thread so that we can insert an annotation indicating so when building
2389 // the stack trace string.
2390 size_t numCurrentFrames = gc.stackTrace.Size();
2391 if (numCurrentFrames > 0)
2393 // "numCurrentFrames" can be zero if the user created an EDI using
2394 // an unthrown exception.
2395 StackTraceElement & refLastElementFromForeignStackTrace = gc.stackTrace[numCurrentFrames - 1];
2396 refLastElementFromForeignStackTrace.fIsLastFrameFromForeignStackTrace = TRUE;
2400 if (!bSkipLastElement)
2401 gc.stackTrace.Append(m_pStackTrace, m_pStackTrace + m_dFrameCount);
2403 //////////////////////////////
2405 unsigned cOrigDynamic = 0; // number of objects in the old array
2406 if (gc.pOrigDynamicArray != NULL)
2408 cOrigDynamic = gc.pOrigDynamicArray->GetNumComponents();
2412 // Since there is no dynamic method array, reset the corresponding state variables
2413 m_dCurrentDynamicIndex = 0;
2414 m_cDynamicMethodItems = 0;
2417 if ((gc.pOrigDynamicArray != NULL)
2418 || (fRaisingForeignException)
2421 // Since we have just restored the dynamic method array as well,
2422 // calculate the dynamic array index which would be the total
2423 // number of dynamic methods present in the stack trace.
2425 // In addition to the ForeignException scenario, we need to reset these
2426 // values incase the exception object in question is being thrown by
2427 // multiple threads in parallel and thus, could have potentially different
2428 // dynamic method array contents/size as opposed to the current state of
2431 unsigned iStackTraceElements = (unsigned)gc.stackTrace.Size();
2432 m_dCurrentDynamicIndex = 0;
2433 for (unsigned iIndex = 0; iIndex < iStackTraceElements; iIndex++)
2435 MethodDesc *pMethod = gc.stackTrace[iIndex].pFunc;
2438 if ((pMethod->IsLCGMethod()) || (pMethod->GetMethodTable()->Collectible()))
2440 // Increment the number of new dynamic methods we have found
2441 m_dCurrentDynamicIndex++;
2446 // Total number of elements in the dynamic method array should also be
2447 // reset based upon the restored array size.
2448 m_cDynamicMethodItems = cOrigDynamic;
2451 // Make the dynamic Array field reference the original array we got from the
2452 // Exception object. If, below, we have to add new entries, we will add it to the
2453 // array if it is allocated, or else, we will allocate it before doing so.
2454 gc.dynamicMethodsArray = gc.pOrigDynamicArray;
2456 // Create an object array if we have new dynamic method entries AND
2457 // if we are at the (or went past) the current size limit
2458 if (iNumDynamics > 0)
2460 // Reallocate the array if we are at the (or went past) the current size limit
2461 unsigned cTotalDynamicMethodCount = m_dCurrentDynamicIndex;
2463 S_UINT32 cNewSum = S_UINT32(cTotalDynamicMethodCount) + S_UINT32(iNumDynamics);
2464 if (cNewSum.IsOverflow())
2466 // If the current size is already the UINT32 max size, then we
2467 // cannot go further. Overflow here implies we cannot allocate memory anymore.
2468 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot calculate resolver array size due to overflow!\n"));
2472 cTotalDynamicMethodCount = cNewSum.Value();
2474 if (cTotalDynamicMethodCount > m_cDynamicMethodItems)
2476 // Double the current limit of the array.
2477 S_UINT32 cNewSize = S_UINT32(2) * S_UINT32(cTotalDynamicMethodCount);
2478 if (cNewSize.IsOverflow())
2480 // Overflow here implies that we cannot allocate any more memory
2481 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - Cannot resize resolver array beyond max size due to overflow!\n"));
2485 m_cDynamicMethodItems = cNewSize.Value();
2486 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems,
2489 _ASSERTE(!(cOrigDynamic && !gc.pOrigDynamicArray));
2491 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - resized dynamic array to size %lu\n",
2492 m_cDynamicMethodItems));
2494 // Copy previous entries if there are any, and update iCurDynamic to point
2495 // to the following index.
2496 if (cOrigDynamic && (gc.pOrigDynamicArray != NULL))
2498 memmoveGCRefs(gc.dynamicMethodsArray->GetDataPtr(),
2499 gc.pOrigDynamicArray->GetDataPtr(),
2500 cOrigDynamic * sizeof(Object *));
2502 // m_dCurrentDynamicIndex is already referring to the correct index
2503 // at which the next resolver object will be saved
2508 // We are adding objects to the existing array.
2510 // We have new dynamic method entries for which
2511 // resolver objects need to be saved. Ensure
2512 // that we have the array to store them
2513 if (gc.dynamicMethodsArray == NULL)
2515 _ASSERTE(m_cDynamicMethodItems > 0);
2517 gc.dynamicMethodsArray = (PTRARRAYREF)AllocateObjectArray(m_cDynamicMethodItems,
2519 m_dCurrentDynamicIndex = 0;
2520 LOG((LF_EH, LL_INFO100, "StackTraceInfo::SaveStackTrace - allocated dynamic array of size %lu\n",
2521 m_cDynamicMethodItems));
2525 // The array exists for storing resolver objects.
2526 // Simply set the index at which the next resolver
2527 // will be stored in it.
2533 // Update _dynamicMethods field
2536 // At this point, we should be having a valid array for storage
2537 _ASSERTE(gc.dynamicMethodsArray != NULL);
2539 // Assert that we are in valid range of the array in which resolver objects will be saved.
2540 // We subtract 1 below since storage will start from m_dCurrentDynamicIndex onwards and not
2541 // from (m_dCurrentDynamicIndex + 1).
2542 _ASSERTE((m_dCurrentDynamicIndex + iNumDynamics - 1) < gc.dynamicMethodsArray->GetNumComponents());
2544 for (unsigned i=0; i < m_dFrameCount; i++)
2546 MethodDesc *pMethod = m_pStackTrace[i].pFunc;
2549 if (pMethod->IsLCGMethod())
2551 // We need to append the corresponding System.Resolver for
2552 // this DynamicMethodDesc to keep it alive.
2553 DynamicMethodDesc *pDMD = (DynamicMethodDesc *) pMethod;
2554 OBJECTREF pResolver = pDMD->GetLCGMethodResolver()->GetManagedResolver();
2556 _ASSERTE(pResolver != NULL);
2558 // Store Resolver information in the array
2559 gc.dynamicMethodsArray->SetAt(m_dCurrentDynamicIndex++, pResolver);
2562 if (pMethod->GetMethodTable()->Collectible())
2564 OBJECTREF pLoaderAllocator = pMethod->GetMethodTable()->GetLoaderAllocator()->GetExposedObject();
2565 _ASSERTE(pLoaderAllocator != NULL);
2566 gc.dynamicMethodsArray->SetAt (m_dCurrentDynamicIndex++, pLoaderAllocator);
2571 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
2573 // Update _stackTraceString field.
2574 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->SetStackTraceString(NULL);
2577 GCPROTECT_END(); // gc
2583 EX_END_CATCH(SwallowAllExceptions)
2592 _ASSERTE(IsException(pMT)); // what is the pathway here?
2593 if (bReplaceStack && IsException(pMT))
2594 ((EXCEPTIONREF)ObjectFromHandle(hThrowable))->ClearStackTraceForThrow();
2600 EX_END_CATCH(SwallowAllExceptions);
2604 // Copy a context record, being careful about whether or not the target
2605 // is large enough to support CONTEXT_EXTENDED_REGISTERS.
2607 // NOTE: this function can ONLY be used when a filter function will return
2608 // EXCEPTION_CONTINUE_EXECUTION. On AMD64, replacing the CONTEXT in any other
2609 // situation may break exception unwinding.
2611 // NOTE: this function MUST be used on AMD64. During exception handling,
2612 // parts of the CONTEXT struct must not be modified.
2615 // High 2 bytes are machine type. Low 2 bytes are register subset.
2616 #define CONTEXT_EXTENDED_BIT (CONTEXT_EXTENDED_REGISTERS & 0xffff)
2619 ReplaceExceptionContextRecord(CONTEXT *pTarget, CONTEXT *pSource)
2621 LIMITED_METHOD_CONTRACT;
2626 #if defined(_TARGET_X86_)
2628 // @TODO IA64: CONTEXT_DEBUG_REGISTERS not defined on IA64, may need updated SDK
2631 // Want CONTROL, INTEGER, SEGMENTS. If we have Floating Point, fine.
2632 _ASSERTE((pSource->ContextFlags & CONTEXT_FULL) == CONTEXT_FULL);
2633 #endif // _TARGET_X86_
2635 #ifdef CONTEXT_EXTENDED_REGISTERS
2637 if (pSource->ContextFlags & CONTEXT_EXTENDED_BIT)
2639 if (pTarget->ContextFlags & CONTEXT_EXTENDED_BIT)
2640 { // Source and Target have EXTENDED bit set.
2641 *pTarget = *pSource;
2644 { // Source has but Target doesn't have EXTENDED bit set. (Target is shorter than Source.)
2645 // Copy non-extended part of the struct, and reset the bit on the Target, as it was.
2646 memcpy(pTarget, pSource, offsetof(CONTEXT, ExtendedRegisters));
2647 pTarget->ContextFlags &= ~CONTEXT_EXTENDED_BIT; // Target was short. Reset the extended bit.
2651 { // Source does not have EXTENDED bit. Copy only non-extended part of the struct.
2652 memcpy(pTarget, pSource, offsetof(CONTEXT, ExtendedRegisters));
2654 STRESS_LOG3(LF_SYNC, LL_INFO1000, "ReSet thread context EIP = %p ESP = %p EBP = %p\n",
2655 GetIP((CONTEXT*)pTarget), GetSP((CONTEXT*)pTarget), GetFP((CONTEXT*)pTarget));
2657 #else // !CONTEXT_EXTENDED_REGISTERS
2659 // Everything that's left
2660 *pTarget = *pSource;
2662 #endif // !CONTEXT_EXTENDED_REGISTERS
2665 VOID FixupOnRethrow(Thread* pCurThread, EXCEPTION_POINTERS* pExceptionPointers)
2667 WRAPPER_NO_CONTRACT;
2669 ThreadExceptionState* pExState = pCurThread->GetExceptionState();
2671 #ifdef FEATURE_INTERPRETER
2672 // Abort if we don't have any state from the original exception.
2673 if (!pExState->IsExceptionInProgress())
2677 #endif // FEATURE_INTERPRETER
2679 // Don't allow rethrow of a STATUS_STACK_OVERFLOW -- it's a new throw of the COM+ exception.
2680 if (pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW)
2685 // For COMPLUS exceptions, we don't need the original context for our rethrow.
2686 if (!(pExState->IsComPlusException()))
2688 _ASSERTE(pExState->GetExceptionRecord());
2690 // don't copy parm args as have already supplied them on the throw
2691 memcpy((void*)pExceptionPointers->ExceptionRecord,
2692 (void*)pExState->GetExceptionRecord(),
2693 offsetof(EXCEPTION_RECORD, ExceptionInformation));
2695 // Replacing the exception context breaks unwinding on AMD64. It also breaks exception dispatch on IA64.
2696 // The info saved by pExState will be given to exception filters.
2697 #ifndef WIN64EXCEPTIONS
2698 // Restore original context if available.
2699 if (pExState->GetContextRecord())
2701 ReplaceExceptionContextRecord(pExceptionPointers->ContextRecord,
2702 pExState->GetContextRecord());
2704 #endif // !WIN64EXCEPTIONS
2707 pExState->GetFlags()->SetIsRethrown();
2710 struct RaiseExceptionFilterParam
2715 LONG RaiseExceptionFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
2717 STATIC_CONTRACT_NOTHROW;
2718 STATIC_CONTRACT_GC_NOTRIGGER;
2719 STATIC_CONTRACT_MODE_ANY;
2721 RaiseExceptionFilterParam *pParam = (RaiseExceptionFilterParam *) pv;
2723 if (1 == pParam->isRethrown)
2725 // need to reset the EH info back to the original thrown exception
2726 FixupOnRethrow(GetThread(), ep);
2727 #ifdef WIN64EXCEPTIONS
2728 // only do this once
2729 pParam->isRethrown++;
2730 #endif // WIN64EXCEPTIONS
2734 CONSISTENCY_CHECK((2 == pParam->isRethrown) || (0 == pParam->isRethrown));
2737 return EXCEPTION_CONTINUE_SEARCH;
2740 //==========================================================================
2742 //==========================================================================
2743 VOID DECLSPEC_NORETURN RaiseTheException(OBJECTREF throwable, BOOL rethrow
2744 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2745 , CorruptionSeverity severity
2746 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2749 STATIC_CONTRACT_THROWS;
2750 STATIC_CONTRACT_GC_TRIGGERS;
2751 STATIC_CONTRACT_MODE_COOPERATIVE;
2753 LOG((LF_EH, LL_INFO100, "RealCOMPlusThrow throwing %s\n",
2754 throwable->GetTrueMethodTable()->GetDebugClassName()));
2756 if (throwable == NULL)
2758 _ASSERTE(!"RealCOMPlusThrow(OBJECTREF) called with NULL argument. Somebody forgot to post an exception!");
2759 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
2762 if (g_CLRPolicyRequested &&
2763 throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
2765 // We depends on UNINSTALL_UNWIND_AND_CONTINUE_HANDLER to handle out of memory escalation.
2766 // We should throw c++ exception instead.
2769 #ifdef FEATURE_STACK_PROBE
2770 else if (throwable == CLRException::GetPreallocatedStackOverflowException())
2772 ThrowStackOverflow();
2775 _ASSERTE(throwable != CLRException::GetPreallocatedStackOverflowException());
2778 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
2779 if (!g_pConfig->LegacyCorruptedStateExceptionsPolicy())
2781 // This is Scenario 3 described in clrex.h around the definition of SET_CE_RETHROW_FLAG_FOR_EX_CATCH macro.
2783 // We are here because the VM is attempting to throw a managed exception. It is posssible this exception
2784 // may not be seen by CLR's exception handler for managed code (e.g. there maybe an EX_CATCH up the stack
2785 // that will swallow or rethrow this exception). In the following scenario:
2787 // [VM1 - RethrowCSE] -> [VM2 - RethrowCSE] -> [VM3 - RethrowCSE] -> <managed code>
2789 // When managed code throws a CSE (e.g. TargetInvocationException flagged as CSE), [VM3] will rethrow it and we will
2790 // enter EX_CATCH in VM2 which is supposed to rethrow it as well. Two things can happen:
2792 // 1) The implementation of EX_CATCH in VM2 throws a new managed exception *before* rethrow policy is applied and control
2793 // will reach EX_CATCH in VM1, OR
2795 // 2) EX_CATCH in VM2 swallows the exception, comes out of the catch block and later throws a new managed exception that
2796 // will be caught by EX_CATCH in VM1.
2798 // In either of the cases, rethrow in VM1 should be on the basis of the new managed exception's corruption severity.
2800 // To support this scenario, we set corruption severity of the managed exception VM is throwing. If its a rethrow,
2801 // it implies we are rethrowing the last exception that was seen by CLR's managed code exception handler. In such a case,
2802 // we will copy over the corruption severity of that exception.
2804 // If throwable indicates corrupted state, forcibly set the severity.
2805 if (CEHelper::IsProcessCorruptedStateException(throwable))
2807 severity = ProcessCorrupting;
2810 // No one should have passed us an invalid severity.
2811 _ASSERTE(severity > NotSet);
2813 if (severity == NotSet)
2815 severity = NotCorrupting;
2818 // Update the corruption severity of the exception being thrown by the VM.
2819 GetThread()->GetExceptionState()->SetLastActiveExceptionCorruptionSeverity(severity);
2821 // Exception's corruption severity should be reused in reraise if this exception leaks out from the VM
2822 // into managed code
2823 CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse();
2825 LOG((LF_EH, LL_INFO100, "RaiseTheException - Set VM thrown managed exception severity to %d.\n", severity));
2828 #endif // FEATURE_CORRUPTING_EXCEPTIONS
2830 RaiseTheExceptionInternalOnly(throwable,rethrow);
2833 HRESULT GetHRFromThrowable(OBJECTREF throwable)
2835 STATIC_CONTRACT_THROWS;
2836 STATIC_CONTRACT_GC_TRIGGERS;
2837 STATIC_CONTRACT_MODE_ANY;
2839 HRESULT hr = E_FAIL;
2840 MethodTable *pMT = throwable->GetTrueMethodTable();
2842 // Only Exception objects have a HResult field
2843 // So don't fetch the field unless we have an exception
2845 _ASSERTE(IsException(pMT)); // what is the pathway here?
2846 if (IsException(pMT))
2848 hr = ((EXCEPTIONREF)throwable)->GetHResult();
2855 VOID DECLSPEC_NORETURN RaiseTheExceptionInternalOnly(OBJECTREF throwable, BOOL rethrow, BOOL fForStackOverflow)
2857 STATIC_CONTRACT_THROWS;
2858 STATIC_CONTRACT_GC_TRIGGERS;
2859 STATIC_CONTRACT_MODE_COOPERATIVE;
2861 STRESS_LOG3(LF_EH, LL_INFO100, "******* MANAGED EXCEPTION THROWN: Object thrown: %p MT %pT rethrow %d\n",
2862 OBJECTREFToObject(throwable), (throwable!=0)?throwable->GetMethodTable():0, rethrow);
2865 // Any object could have been thrown, but System.Exception objects have useful information for the stress log
2866 if (!NingenEnabled() && throwable == CLRException::GetPreallocatedStackOverflowException())
2868 // if are handling an SO, don't try to get all that other goop. It isn't there anyway,
2869 // and it could cause us to take another SO.
2870 STRESS_LOG1(LF_EH, LL_INFO100, "Exception HRESULT = 0x%x \n", COR_E_STACKOVERFLOW);
2872 else if (throwable != 0)
2874 _ASSERTE(IsException(throwable->GetMethodTable()));
2876 int hr = ((EXCEPTIONREF)throwable)->GetHResult();
2877 STRINGREF message = ((EXCEPTIONREF)throwable)->GetMessage();
2878 OBJECTREF innerEH = ((EXCEPTIONREF)throwable)->GetInnerException();
2880 STRESS_LOG4(LF_EH, LL_INFO100, "Exception HRESULT = 0x%x Message String 0x%p (db will display) InnerException %p MT %pT\n",
2881 hr, OBJECTREFToObject(message), OBJECTREFToObject(innerEH), (innerEH!=0)?innerEH->GetMethodTable():0);
2885 struct Param : RaiseExceptionFilterParam
2887 OBJECTREF throwable;
2888 BOOL fForStackOverflow;
2889 ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
2891 ThreadExceptionState* pExState;
2893 param.isRethrown = rethrow ? 1 : 0; // normalize because we use it as a count in RaiseExceptionFilter
2894 param.throwable = throwable;
2895 param.fForStackOverflow = fForStackOverflow;
2896 param.pThread = GetThread();
2898 _ASSERTE(param.pThread);
2899 param.pExState = param.pThread->GetExceptionState();
2901 if (param.pThread->IsRudeAbortInitiated())
2903 // Nobody should be able to swallow rude thread abort.
2904 param.throwable = CLRException::GetPreallocatedRudeThreadAbortException();
2908 // TODO: enable this after we change RealCOMPlusThrow
2910 // If ThreadAbort exception is thrown, the thread should be marked with AbortRequest.
2911 // If not, we may see unhandled exception.
2912 if (param.throwable->GetTrueMethodTable() == g_pThreadAbortExceptionClass)
2914 _ASSERTE(GetThread()->IsAbortRequested()
2917 GetFirstCOMPlusSEHRecord(this) == EXCEPTION_CHAIN_END
2925 PAL_TRY(Param *, pParam, ¶m)
2927 //_ASSERTE(! pParam->isRethrown || pParam->pExState->m_pExceptionRecord);
2928 ULONG_PTR *args = NULL;
2933 // Always save the current object in the handle so on rethrow we can reuse it. This is important as it
2934 // contains stack trace info.
2936 // Note: we use SafeSetLastThrownObject, which will try to set the throwable and if there are any problems,
2937 // it will set the throwable to something appropiate (like OOM exception) and return the new
2938 // exception. Thus, the user's exception object can be replaced here.
2939 pParam->throwable = NingenEnabled() ? NULL : pParam->pThread->SafeSetLastThrownObject(pParam->throwable);
2941 if (!pParam->isRethrown ||
2942 #ifdef FEATURE_INTERPRETER
2943 !pParam->pExState->IsExceptionInProgress() ||
2944 #endif // FEATURE_INTERPRETER
2945 pParam->pExState->IsComPlusException() ||
2946 (pParam->pExState->GetExceptionCode() == STATUS_STACK_OVERFLOW))
2948 ULONG_PTR hr = NingenEnabled() ? E_FAIL : GetHRFromThrowable(pParam->throwable);
2950 args = pParam->exceptionArgs;
2951 argCount = MarkAsThrownByUs(args, hr);
2952 flags = EXCEPTION_NONCONTINUABLE;
2953 code = EXCEPTION_COMPLUS;
2957 // Exception code should be consistent.
2958 _ASSERTE((DWORD)(pParam->pExState->GetExceptionRecord()->ExceptionCode) == pParam->pExState->GetExceptionCode());
2960 args = pParam->pExState->GetExceptionRecord()->ExceptionInformation;
2961 argCount = pParam->pExState->GetExceptionRecord()->NumberParameters;
2962 flags = pParam->pExState->GetExceptionRecord()->ExceptionFlags;
2963 code = pParam->pExState->GetExceptionRecord()->ExceptionCode;
2966 if (pParam->pThread->IsAbortInitiated () && IsExceptionOfType(kThreadAbortException,&pParam->throwable))
2968 pParam->pThread->ResetPreparingAbort();
2970 if (pParam->pThread->GetFrame() == FRAME_TOP)
2972 // There is no more managed code on stack.
2973 pParam->pThread->EEResetAbort(Thread::TAR_ALL);
2977 // Can't access the exception object when are in pre-emptive, so find out before
2979 BOOL fIsStackOverflow = IsExceptionOfType(kStackOverflowException, &pParam->throwable);
2981 if (fIsStackOverflow || pParam->fForStackOverflow)
2983 // Don't probe if we're already handling an SO. Just throw the exception.
2984 RaiseException(code, flags, argCount, args);
2987 // Probe for sufficient stack.
2988 PUSH_STACK_PROBE_FOR_THROW(pParam->pThread);
2990 #ifndef STACK_GUARDS_DEBUG
2991 // This needs to be both here and inside the handler below
2992 // enable preemptive mode before call into OS
2993 GCX_PREEMP_NO_DTOR();
2995 // In non-debug, we can just raise the exception once we've probed.
2996 RaiseException(code, flags, argCount, args);
2999 // In a debug build, we need to unwind our probe structure off the stack.
3000 BaseStackGuard *pThrowGuard = NULL;
3001 // Stach away the address of the guard we just pushed above in PUSH_STACK_PROBE_FOR_THROW
3002 SAVE_ADDRESS_OF_STACK_PROBE_FOR_THROW(pThrowGuard);
3004 // Add the stack guard reference to the structure below so that it can be accessed within
3012 BaseStackGuard *pGuard;
3015 param.flags = flags;
3016 param.argCount = argCount;
3018 param.pGuard = pThrowGuard;
3020 PAL_TRY(ParamInner *, pParam, ¶m)
3022 // enable preemptive mode before call into OS
3023 GCX_PREEMP_NO_DTOR();
3025 RaiseException(pParam->code, pParam->flags, pParam->argCount, pParam->args);
3027 // We never return from RaiseException, so shouldn't have to call SetNoException.
3028 // However, in the debugger we can, and if we don't call SetNoException we get
3029 // a short-circuit return assert.
3030 RESET_EXCEPTION_FROM_STACK_PROBE_FOR_THROW(pParam->pGuard);
3034 // pop the guard that we pushed above in PUSH_STACK_PROBE_FOR_THROW
3035 POP_STACK_PROBE_FOR_THROW(pThrowGuard);
3040 PAL_EXCEPT_FILTER (RaiseExceptionFilter)
3044 _ASSERTE(!"Cannot continue after COM+ exception"); // Debugger can bring you here.
3046 // Debugger breaks in due to second chance exception (unhandled)
3048 // Then debugger can bring us here.
3049 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
3053 // INSTALL_COMPLUS_EXCEPTION_HANDLER has a filter, so must put the call in a separate fcn
3054 static VOID DECLSPEC_NORETURN RealCOMPlusThrowWorker(OBJECTREF throwable, BOOL rethrow
3055 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3056 , CorruptionSeverity severity
3057 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3059 STATIC_CONTRACT_THROWS;
3060 STATIC_CONTRACT_GC_TRIGGERS;
3061 STATIC_CONTRACT_MODE_ANY;
3063 // RaiseTheException will throw C++ OOM and SO, so that our escalation policy can kick in.
3064 // Unfortunately, COMPlusFrameHandler installed here, will try to create managed exception object.
3065 // We may hit a recursion.
3067 if (g_CLRPolicyRequested &&
3068 throwable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
3070 // We depends on UNINSTALL_UNWIND_AND_CONTINUE_HANDLER to handle out of memory escalation.
3071 // We should throw c++ exception instead.
3074 #ifdef FEATURE_STACK_PROBE
3075 else if (throwable == CLRException::GetPreallocatedStackOverflowException())
3077 ThrowStackOverflow();
3080 _ASSERTE(throwable != CLRException::GetPreallocatedStackOverflowException());
3083 // TODO: Do we need to install COMPlusFrameHandler here?
3084 INSTALL_COMPLUS_EXCEPTION_HANDLER();
3085 RaiseTheException(throwable, rethrow
3086 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3088 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3090 UNINSTALL_COMPLUS_EXCEPTION_HANDLER();
3094 VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable, BOOL rethrow
3095 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3096 , CorruptionSeverity severity
3097 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3099 STATIC_CONTRACT_THROWS;
3100 STATIC_CONTRACT_GC_TRIGGERS;
3101 STATIC_CONTRACT_MODE_ANY;
3102 GCPROTECT_BEGIN(throwable);
3104 _ASSERTE(IsException(throwable->GetMethodTable()));
3106 // This may look a bit odd, but there is an explaination. The rethrow boolean
3107 // means that an actual RaiseException(EXCEPTION_COMPLUS,...) is being re-thrown,
3108 // and that the exception context saved on the Thread object should replace
3109 // the exception context from the upcoming RaiseException(). There is logic
3110 // in the stack trace code to preserve MOST of the stack trace, but to drop the
3111 // last element of the stack trace (has to do with having the address of the rethrow
3112 // instead of the address of the original call in the stack trace. That is
3113 // controversial itself, but we won't get into that here.)
3114 // However, if this is not re-raising that original exception, but rather a new
3115 // os exception for what may be an existing exception object, it is generally
3116 // a good thing to preserve the stack trace.
3119 ExceptionPreserveStackTrace(throwable);
3122 RealCOMPlusThrowWorker(throwable, rethrow
3123 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3125 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3131 VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable
3132 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3133 , CorruptionSeverity severity
3134 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3145 RealCOMPlusThrow(throwable, FALSE
3146 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3148 #endif // FEATURE_CORRUPTING_EXCEPTIONS
3152 // this function finds the managed callback to get a resource
3153 // string from the then current local domain and calls it
3154 // this could be a lot of work
3155 STRINGREF GetResourceStringFromManaged(STRINGREF key)
3162 PRECONDITION(key != NULL);
3174 // The standard probe isn't good enough here. It's possible that we only have ~14 pages of stack
3175 // left. By the time we transition to the default domain and start fetching this resource string,
3176 // another 12 page probe could fail.
3177 // This failing probe would cause us to unload the default appdomain, which would cause us
3178 // to take down the process.
3180 // Instead, let's probe for a lots more stack to make sure that doesn' happen.
3182 // We need to have enough stack to survive 2 more probes... the original entrypoint back
3183 // into mscorwks after we go into managed code, and a "large" probe that protects the GC
3185 INTERIOR_STACK_PROBE_FOR(GetThread(), DEFAULT_ENTRY_PROBE_AMOUNT * 2);
3186 GCPROTECT_BEGIN(gc);
3188 MethodDescCallSite getResourceStringLocal(METHOD__ENVIRONMENT__GET_RESOURCE_STRING_LOCAL);
3190 // Call Environment::GetResourceStringLocal(String name). Returns String value (or maybe null)
3192 ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD);
3194 // Don't need to GCPROTECT pArgs, since it's not used after the function call.
3196 ARG_SLOT pArgs[1] = { ObjToArgSlot(gc.key) };
3197 gc.ret = getResourceStringLocal.Call_RetSTRINGREF(pArgs);
3199 END_DOMAIN_TRANSITION;
3203 END_INTERIOR_STACK_PROBE;
3209 // This function does poentially a LOT of work (loading possibly 50 classes).
3210 // The return value is an un-GC-protected string ref, or possibly NULL.
3211 void ResMgrGetString(LPCWSTR wszResourceName, STRINGREF * ppMessage)
3221 _ASSERTE(ppMessage != NULL);
3223 if (wszResourceName == NULL || *wszResourceName == W('\0'))
3229 // this function never looks at name again after
3230 // calling the helper so no need to GCPROTECT it
3231 STRINGREF name = StringObject::NewString(wszResourceName);
3233 if (wszResourceName != NULL)
3235 STRINGREF value = GetResourceStringFromManaged(name);
3237 _ASSERTE(value!=NULL || !"Resource string lookup failed - possible misspelling or .resources missing or out of date?");
3242 // GetResourceFromDefault
3243 // transition to the default domain and get a resource there
3244 FCIMPL1(Object*, GetResourceFromDefault, StringObject* keyUnsafe)
3248 STRINGREF ret = NULL;
3249 STRINGREF key = (STRINGREF)keyUnsafe;
3251 HELPER_METHOD_FRAME_BEGIN_RET_2(ret, key);
3253 ret = GetResourceStringFromManaged(key);
3255 HELPER_METHOD_FRAME_END();
3257 return OBJECTREFToObject(ret);
3261 void FreeExceptionData(ExceptionData *pedata)
3271 _ASSERTE(pedata != NULL);
3273 // <TODO>@NICE: At one point, we had the comment:
3274 // (DM) Remove this when shutdown works better.</TODO>
3275 // This test may no longer be necessary. Remove at own peril.
3276 Thread *pThread = GetThread();
3280 if (pedata->bstrSource)
3281 SysFreeString(pedata->bstrSource);
3282 if (pedata->bstrDescription)
3283 SysFreeString(pedata->bstrDescription);
3284 if (pedata->bstrHelpFile)
3285 SysFreeString(pedata->bstrHelpFile);
3286 #ifdef FEATURE_COMINTEROP
3287 if (pedata->bstrRestrictedError)
3288 SysFreeString(pedata->bstrRestrictedError);
3289 if (pedata->bstrReference)
3290 SysFreeString(pedata->bstrReference);
3291 if (pedata->bstrCapabilitySid)
3292 SysFreeString(pedata->bstrCapabilitySid);
3293 if (pedata->pRestrictedErrorInfo)
3295 ULONG cbRef = SafeRelease(pedata->pRestrictedErrorInfo);
3296 LogInteropRelease(pedata->pRestrictedErrorInfo, cbRef, "IRestrictedErrorInfo");
3298 #endif // FEATURE_COMINTEROP
3301 void GetExceptionForHR(HRESULT hr, IErrorInfo* pErrInfo, bool fUseCOMException, OBJECTREF* pProtectedThrowable, IRestrictedErrorInfo *pResErrorInfo, BOOL bHasLangRestrictedErrInfo)
3308 PRECONDITION(IsProtectedByGCFrame(pProtectedThrowable));
3313 *pProtectedThrowable = NULL;
3315 #if defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
3316 if (pErrInfo != NULL)
3318 // If this represents a managed object...
3319 // ...then get the managed exception object and also check if it is a __ComObject...
3320 if (IsManagedObject(pErrInfo))
3322 GetObjectRefFromComIP(pProtectedThrowable, pErrInfo);
3323 if ((*pProtectedThrowable) != NULL)
3325 // ...if it is, then we'll just default to an exception based on the IErrorInfo.
3326 if ((*pProtectedThrowable)->GetMethodTable()->IsComObjectType())
3328 (*pProtectedThrowable) = NULL;
3332 // We have created an exception. Release the IErrorInfo
3333 ULONG cbRef = SafeRelease(pErrInfo);
3334 LogInteropRelease(pErrInfo, cbRef, "IErrorInfo release");
3340 // If we got here and we don't have an exception object, we have a native IErrorInfo or
3341 // a managed __ComObject based IErrorInfo, so we'll just create an exception based on
3342 // the native IErrorInfo.
3343 if ((*pProtectedThrowable) == NULL)
3345 EECOMException ex(hr, pErrInfo, fUseCOMException, pResErrorInfo, bHasLangRestrictedErrInfo COMMA_INDEBUG(FALSE));
3346 (*pProtectedThrowable) = ex.GetThrowable();
3349 #endif // defined(FEATURE_COMINTEROP) && !defined(CROSSGEN_COMPILE)
3351 // If we made it here and we don't have an exception object, we didn't have a valid IErrorInfo
3352 // so we'll create an exception based solely on the hresult.
3353 if ((*pProtectedThrowable) == NULL)
3355 EEMessageException ex(hr, fUseCOMException);
3356 (*pProtectedThrowable) = ex.GetThrowable();
3360 void GetExceptionForHR(HRESULT hr, IErrorInfo* pErrInfo, OBJECTREF* pProtectedThrowable)
3362 WRAPPER_NO_CONTRACT;
3364 GetExceptionForHR(hr, pErrInfo, true, pProtectedThrowable);
3367 void GetExceptionForHR(HRESULT hr, OBJECTREF* pProtectedThrowable)
3372 GC_TRIGGERS; // because of IErrorInfo
3377 // Get an IErrorInfo if one is available.
3378 IErrorInfo *pErrInfo = NULL;
3379 #ifndef CROSSGEN_COMPILE
3380 if (SafeGetErrorInfo(&pErrInfo) != S_OK)
3384 GetExceptionForHR(hr, pErrInfo, true, pProtectedThrowable);
3389 // Maps a Win32 fault to a COM+ Exception enumeration code
3391 DWORD MapWin32FaultToCOMPlusException(EXCEPTION_RECORD *pExceptionRecord)
3393 WRAPPER_NO_CONTRACT;
3395 switch (pExceptionRecord->ExceptionCode)
3397 case STATUS_FLOAT_INEXACT_RESULT:
3398 case STATUS_FLOAT_INVALID_OPERATION:
3399 case STATUS_FLOAT_STACK_CHECK:
3400 case STATUS_FLOAT_UNDERFLOW:
3401 return (DWORD) kArithmeticException;
3402 case STATUS_FLOAT_OVERFLOW:
3403 case STATUS_INTEGER_OVERFLOW:
3404 return (DWORD) kOverflowException;
3406 case STATUS_FLOAT_DIVIDE_BY_ZERO:
3407 case STATUS_INTEGER_DIVIDE_BY_ZERO:
3408 return (DWORD) kDivideByZeroException;
3410 case STATUS_FLOAT_DENORMAL_OPERAND:
3411 return (DWORD) kFormatException;
3413 case STATUS_ACCESS_VIOLATION:
3415 // We have a config key, InsecurelyTreatAVsAsNullReference, that ensures we always translate to
3416 // NullReferenceException instead of doing the new AV translation logic.
3417 if ((g_pConfig != NULL) && !g_pConfig->LegacyNullReferenceExceptionPolicy())
3419 #if defined(FEATURE_HIJACK) && !defined(PLATFORM_UNIX)
3420 // If we got the exception on a redirect function it means the original exception happened in managed code:
3421 if (Thread::IsAddrOfRedirectFunc(pExceptionRecord->ExceptionAddress))
3422 return (DWORD) kNullReferenceException;
3424 if (pExceptionRecord->ExceptionAddress == (LPVOID)GetEEFuncEntryPoint(THROW_CONTROL_FOR_THREAD_FUNCTION))
3426 return (DWORD) kNullReferenceException;
3428 #endif // FEATURE_HIJACK && !PLATFORM_UNIX
3430 // If the IP of the AV is not in managed code, then its an AccessViolationException.
3431 if (!ExecutionManager::IsManagedCode((PCODE)pExceptionRecord->ExceptionAddress))
3433 return (DWORD) kAccessViolationException;
3436 // If the address accessed is above 64k (Windows) or page size (PAL), then its an AccessViolationException.
3437 // Note: Win9x is a little different... it never gives you the proper address of the read or write that caused
3438 // the fault. It always gives -1, so we can't use it as part of the decision... just give
3439 // NullReferenceException instead.
3440 if (pExceptionRecord->ExceptionInformation[1] >= NULL_AREA_SIZE)
3442 return (DWORD) kAccessViolationException;
3446 return (DWORD) kNullReferenceException;
3449 case STATUS_ARRAY_BOUNDS_EXCEEDED:
3450 return (DWORD) kIndexOutOfRangeException;
3452 case STATUS_NO_MEMORY:
3453 return (DWORD) kOutOfMemoryException;
3455 case STATUS_STACK_OVERFLOW:
3456 return (DWORD) kStackOverflowException;
3459 case STATUS_DATATYPE_MISALIGNMENT:
3460 return (DWORD) kDataMisalignedException;
3461 #endif // ALIGN_ACCESS
3464 return kSEHException;
3469 #ifndef WIN64EXCEPTIONS
3470 // check if anyone has written to the stack above the handler which would wipe out the EH registration
3471 void CheckStackBarrier(EXCEPTION_REGISTRATION_RECORD *exRecord)
3473 LIMITED_METHOD_CONTRACT;
3475 if (exRecord->Handler != (PEXCEPTION_ROUTINE)COMPlusFrameHandler)
3478 DWORD *stackOverwriteBarrier = (DWORD *)((BYTE*)exRecord - offsetof(FrameHandlerExRecordWithBarrier, m_ExRecord));
3479 for (int i =0; i < STACK_OVERWRITE_BARRIER_SIZE; i++) {
3480 if (*(stackOverwriteBarrier+i) != STACK_OVERWRITE_BARRIER_VALUE) {
3481 // to debug this error, you must determine who erroneously overwrote the stack
3482 _ASSERTE(!"Fatal error: the stack has been overwritten");
3486 #endif // WIN64EXCEPTIONS
3489 //-------------------------------------------------------------------------
3490 // A marker for JIT -> EE transition when we know we're in preemptive
3491 // gc mode. As we leave the EE, we fix a few things:
3493 // - the gc state must be set back to preemptive-operative
3494 // - the COM+ frame chain must be rewound to what it was on entry
3495 // - ExInfo()->m_pSearchBoundary must be adjusted
3496 // if we popped the frame that is identified as begnning the next
3498 //-------------------------------------------------------------------------
3500 void COMPlusCooperativeTransitionHandler(Frame* pFrame)
3510 LOG((LF_EH, LL_INFO1000, "COMPlusCooprativeTransitionHandler unwinding\n"));
3513 Thread* pThread = GetThread();
3515 // Restore us to cooperative gc mode.
3518 // Pop the frame chain.
3519 UnwindFrameChain(pThread, pFrame);
3520 CONSISTENCY_CHECK(pFrame == pThread->GetFrame());
3522 #ifndef WIN64EXCEPTIONS
3523 // An exception is being thrown through here. The COM+ exception
3524 // info keeps a pointer to a frame that is used by the next
3525 // COM+ Exception Handler as the starting point of its crawl.
3526 // We may have popped this marker -- in which case, we need to
3527 // update it to the current frame.
3529 ThreadExceptionState* pExState = pThread->GetExceptionState();
3530 Frame* pSearchBoundary = NULL;
3532 if (pThread->IsExceptionInProgress())
3534 pSearchBoundary = pExState->m_currentExInfo.m_pSearchBoundary;
3537 if (pSearchBoundary && pSearchBoundary < pFrame)
3539 LOG((LF_EH, LL_INFO1000, "\tpExInfo->m_pSearchBoundary = %08x\n", (void*)pFrame));
3540 pExState->m_currentExInfo.m_pSearchBoundary = pFrame;
3542 #endif // WIN64EXCEPTIONS
3545 // Restore us to preemptive gc mode.
3546 GCX_PREEMP_NO_DTOR();
3551 void StackTraceInfo::Init()
3563 LOG((LF_EH, LL_INFO10000, "StackTraceInfo::Init (%p)\n", this));
3565 m_pStackTrace = NULL;
3568 m_cDynamicMethodItems = 0;
3569 m_dCurrentDynamicIndex = 0;
3572 void StackTraceInfo::FreeStackTrace()
3586 delete [] m_pStackTrace;
3587 m_pStackTrace = NULL;
3590 m_cDynamicMethodItems = 0;
3591 m_dCurrentDynamicIndex = 0;
3595 BOOL StackTraceInfo::IsEmpty()
3597 LIMITED_METHOD_CONTRACT;
3599 return 0 == m_dFrameCount;
3602 void StackTraceInfo::ClearStackTrace()
3604 LIMITED_METHOD_CONTRACT;
3606 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::ClearStackTrace (%p)\n", this));
3610 // allocate stack trace info. As each function is found in the stack crawl, it will be added
3611 // to this list. If the list is too small, it is reallocated.
3612 void StackTraceInfo::AllocateStackTrace()
3614 STATIC_CONTRACT_NOTHROW;
3615 STATIC_CONTRACT_GC_NOTRIGGER;
3616 STATIC_CONTRACT_MODE_ANY;
3617 STATIC_CONTRACT_FORBID_FAULT;
3619 LOG((LF_EH, LL_INFO1000, "StackTraceInfo::AllocateStackTrace (%p)\n", this));
3624 unsigned int allocSize = 2; // make small to exercise realloc
3626 unsigned int allocSize = 30;
3629 SCAN_IGNORE_FAULT; // A fault of new is okay here. The rest of the system is cool if we don't have enough
3630 // memory to remember the stack as we run our first pass.
3631 m_pStackTrace = new (nothrow) StackTraceElement[allocSize];
3633 if (m_pStackTrace != NULL)
3635 // Remember how much we allocated.
3636 m_cStackTrace = allocSize;
3637 m_cDynamicMethodItems = allocSize;
3642 m_cDynamicMethodItems = 0;
3648 // Returns true if it appended the element, false otherwise.
3650 BOOL StackTraceInfo::AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf)
3659 LOG((LF_EH, LL_INFO10000, "StackTraceInfo::AppendElement (%p), IP = %p, SP = %p, %s::%s\n", this, currentIP, currentSP, pFunc ? pFunc->m_pszDebugClassName : "", pFunc ? pFunc->m_pszDebugMethodName : "" ));
3660 BOOL bRetVal = FALSE;
3662 if (pFunc != NULL && pFunc->IsILStub())
3665 // Save this function in the stack trace array, which we only build on the first pass. We'll try to expand the
3666 // stack trace array if we don't have enough room. Note that we only try to expand if we're allowed to allocate
3667 // memory (bAllowAllocMem).
3668 if (bAllowAllocMem && (m_dFrameCount >= m_cStackTrace))
3670 StackTraceElement* pTempElement = new (nothrow) StackTraceElement[m_cStackTrace*2];
3672 if (pTempElement != NULL)
3674 memcpy(pTempElement, m_pStackTrace, m_cStackTrace * sizeof(StackTraceElement));
3675 delete [] m_pStackTrace;
3676 m_pStackTrace = pTempElement;
3681 // Add the function to the stack trace array if there's room.
3682 if (m_dFrameCount < m_cStackTrace)
3684 StackTraceElement* pStackTraceElem;
3686 // If we get in here, we'd better have a stack trace array.
3687 CONSISTENCY_CHECK(m_pStackTrace != NULL);
3689 pStackTraceElem = &(m_pStackTrace[m_dFrameCount]);
3691 pStackTraceElem->pFunc = pFunc;
3693 pStackTraceElem->ip = currentIP;
3694 pStackTraceElem->sp = currentSP;
3696 // When we are building stack trace as we encounter managed frames during exception dispatch,
3697 // then none of those frames represent a stack trace from a foreign exception (as they represent
3698 // the current exception). Hence, set the corresponding flag to FALSE.
3699 pStackTraceElem->fIsLastFrameFromForeignStackTrace = FALSE;
3701 // This is a workaround to fix the generation of stack traces from exception objects so that
3702 // they point to the line that actually generated the exception instead of the line
3704 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && pStackTraceElem->ip != 0)
3706 pStackTraceElem->ip -= 1;
3711 COUNTER_ONLY(GetPerfCounters().m_Excep.cThrowToCatchStackDepth++);
3714 #ifndef FEATURE_PAL // Watson is supported on Windows only
3715 Thread *pThread = GetThread();
3718 if (pThread && (currentIP != 0))
3720 // Setup the watson bucketing details for the initial throw
3721 // callback only if we dont already have them.
3722 ThreadExceptionState *pExState = pThread->GetExceptionState();
3723 if (!pExState->GetFlags()->GotWatsonBucketDetails())
3725 // Adjust the IP if necessary.
3726 UINT_PTR adjustedIp = currentIP;
3727 // This is a workaround copied from above.
3728 if (!(pCf->HasFaulted() || pCf->IsIPadjusted()) && adjustedIp != 0)
3733 // Setup the bucketing details for the initial throw
3734 SetupInitialThrowBucketDetails(adjustedIp);
3737 #endif // !FEATURE_PAL
3742 void StackTraceInfo::GetLeafFrameInfo(StackTraceElement* pStackTraceElement)
3744 LIMITED_METHOD_CONTRACT;
3746 if (NULL == m_pStackTrace)
3750 _ASSERTE(NULL != pStackTraceElement);
3752 *pStackTraceElement = m_pStackTrace[0];
3756 void UnwindFrameChain(Thread* pThread, LPVOID pvLimitSP)
3761 DISABLED(GC_TRIGGERS); // some Frames' ExceptionUnwind methods trigger :(
3767 // @todo - Remove this and add a hard SO probe as can't throw from here.
3768 CONTRACT_VIOLATION(SOToleranceViolation);
3770 Frame* pFrame = pThread->m_pFrame;
3771 if (pFrame < pvLimitSP)
3773 GCX_COOP_THREAD_EXISTS(pThread);
3776 // call ExceptionUnwind with the Frame chain intact
3778 pFrame = pThread->NotifyFrameChainOfExceptionUnwind(pFrame, pvLimitSP);
3781 // now pop the frames off by trimming the Frame chain
3783 pThread->SetFrame(pFrame);
3787 BOOL IsExceptionOfType(RuntimeExceptionKind reKind, Exception *pException)
3789 STATIC_CONTRACT_NOTHROW;
3790 STATIC_CONTRACT_GC_TRIGGERS;
3791 STATIC_CONTRACT_MODE_ANY;
3792 STATIC_CONTRACT_FORBID_FAULT;
3794 if (pException->IsType(reKind))
3797 if (pException->IsType(CLRException::GetType()))
3799 // Since we're going to be holding onto the Throwable object we
3800 // need to be in COOPERATIVE.
3803 OBJECTREF Throwable=((CLRException*)pException)->GetThrowable();
3806 if (IsExceptionOfType(reKind, &Throwable))
3812 BOOL IsExceptionOfType(RuntimeExceptionKind reKind, OBJECTREF *pThrowable)
3814 STATIC_CONTRACT_NOTHROW;
3815 STATIC_CONTRACT_GC_NOTRIGGER;
3816 STATIC_CONTRACT_MODE_COOPERATIVE;
3817 STATIC_CONTRACT_FORBID_FAULT;
3819 _ASSERTE(pThrowable != NULL);
3821 if (*pThrowable == NULL)
3824 MethodTable *pThrowableMT = (*pThrowable)->GetTrueMethodTable();
3826 // IsExceptionOfType is supported for mscorlib exception types only
3827 _ASSERTE(reKind <= kLastExceptionInMscorlib);
3828 return MscorlibBinder::IsException(pThrowableMT, reKind);
3831 BOOL IsAsyncThreadException(OBJECTREF *pThrowable) {
3832 STATIC_CONTRACT_NOTHROW;
3833 STATIC_CONTRACT_GC_NOTRIGGER;
3834 STATIC_CONTRACT_MODE_COOPERATIVE;
3835 STATIC_CONTRACT_FORBID_FAULT;
3837 if ( (GetThread() && GetThread()->IsRudeAbort() && GetThread()->IsRudeAbortInitiated())
3838 ||IsExceptionOfType(kThreadAbortException, pThrowable)
3839 ||IsExceptionOfType(kThreadInterruptedException, pThrowable)) {
3846 BOOL IsUncatchable(OBJECTREF *pThrowable)
3856 _ASSERTE(pThrowable != NULL);
3858 Thread *pThread = GetThread();
3862 if (pThread->IsAbortInitiated())
3865 if (OBJECTREFToObject(*pThrowable)->GetMethodTable() == g_pExecutionEngineExceptionClass)
3868 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
3869 // Corrupting exceptions are also uncatchable
3870 if (CEHelper::IsProcessCorruptedStateException(*pThrowable))
3874 #endif //FEATURE_CORRUPTING_EXCEPTIONS
3880 BOOL IsStackOverflowException(Thread* pThread, EXCEPTION_RECORD* pExceptionRecord)
3882 if (IsSOExceptionCode(pExceptionRecord->ExceptionCode))
3887 if (IsComPlusException(pExceptionRecord) &&
3888 pThread->IsLastThrownObjectStackOverflowException())
3898 BOOL IsValidClause(EE_ILEXCEPTION_CLAUSE *EHClause)
3900 LIMITED_METHOD_CONTRACT;
3903 DWORD valid = COR_ILEXCEPTION_CLAUSE_FILTER | COR_ILEXCEPTION_CLAUSE_FINALLY |
3904 COR_ILEXCEPTION_CLAUSE_FAULT | COR_ILEXCEPTION_CLAUSE_CACHED_CLASS;
3906 // <TODO>@NICE: enable this when VC stops generatng a bogus 0x8000.</TODO>
3907 if (EHClause->Flags & ~valid)
3910 if (EHClause->TryStartPC > EHClause->TryEndPC)
3917 #ifdef DEBUGGING_SUPPORTED
3918 LONG NotifyDebuggerLastChance(Thread *pThread,
3919 EXCEPTION_POINTERS *pExceptionInfo,
3920 BOOL jitAttachRequested)
3922 STATIC_CONTRACT_NOTHROW;
3923 STATIC_CONTRACT_GC_TRIGGERS;
3924 STATIC_CONTRACT_MODE_ANY;
3926 LONG retval = EXCEPTION_CONTINUE_SEARCH;
3928 // Debugger does func-evals inside this call, which may take nested exceptions. We need a nested exception
3929 // handler to allow this.
3930 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
3932 EXCEPTION_POINTERS dummy;
3933 dummy.ExceptionRecord = NULL;
3934 dummy.ContextRecord = NULL;
3936 if (NULL == pExceptionInfo)
3938 pExceptionInfo = &dummy;
3940 else if (NULL != pExceptionInfo->ExceptionRecord && NULL == pExceptionInfo->ContextRecord)
3942 // In a soft stack overflow, we have an exception record but not a context record.
3943 // Debugger::LastChanceManagedException requires that both ExceptionRecord and
3944 // ContextRecord be valid or both be NULL.
3945 pExceptionInfo = &dummy;
3948 if (g_pDebugInterface && g_pDebugInterface->LastChanceManagedException(pExceptionInfo,
3950 jitAttachRequested) == ExceptionContinueExecution)
3952 retval = EXCEPTION_CONTINUE_EXECUTION;
3955 UNINSTALL_NESTED_EXCEPTION_HANDLER();
3957 #ifdef DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
3960 // if the debugger wants to intercept the unhandled exception then we immediately unwind without returning
3961 // If there is a problem with this function unwinding here it could be separated out however
3962 // we need to be very careful. Previously we had the opposite problem in that we notified the debugger
3963 // of an unhandled exception and then either:
3964 // a) never gave the debugger a chance to intercept later, or
3965 // b) code changed more process state unaware that the debugger would be handling the exception
3966 if ((pThread->IsExceptionInProgress()) && pThread->GetExceptionState()->GetFlags()->DebuggerInterceptInfo())
3968 // The debugger wants to intercept this exception. It may return in a failure case, in which case we want
3969 // to continue thru this path.
3970 ClrDebuggerDoUnwindAndIntercept(X86_FIRST_ARG(EXCEPTION_CHAIN_END) pExceptionInfo->ExceptionRecord);
3973 EX_CATCH // if we fail to intercept just continue as is
3976 EX_END_CATCH(SwallowAllExceptions);
3977 #endif // DEBUGGER_EXCEPTION_INTERCEPTION_SUPPORTED
3983 //----------------------------------------------------------------------------
3985 // DoReportFault - wrapper for ReportFault in FaultRep.dll, which also handles
3986 // debugger launch synchronization if the user chooses to launch
3990 // pExceptionInfo - pointer to exception info
3993 // The returned EFaultRepRetVal value from ReportFault
3997 //----------------------------------------------------------------------------
3998 EFaultRepRetVal DoReportFault(EXCEPTION_POINTERS * pExceptionInfo)
4000 LIMITED_METHOD_CONTRACT;
4002 HINSTANCE hmod = WszLoadLibrary(W("FaultRep.dll"));
4003 EFaultRepRetVal r = frrvErr;
4006 pfn_REPORTFAULT pfnReportFault = (pfn_REPORTFAULT)GetProcAddress(hmod, "ReportFault");
4009 r = pfnReportFault(pExceptionInfo, 0);
4014 if (r == frrvLaunchDebugger)
4016 // Wait until the pending managed debugger attach is completed
4017 if (g_pDebugInterface != NULL)
4019 g_pDebugInterface->WaitForDebuggerAttach();
4025 //----------------------------------------------------------------------------
4027 // DisableOSWatson - Set error mode to disable OS Watson
4035 // Note: SetErrorMode changes the process wide error mode, which can be overridden by other threads
4036 // in a race. The solution is to use new Win7 per thread error mode APIs, which take precedence
4037 // over process wide error mode. However, we shall not use per thread error mode if the runtime
4038 // is being hosted because with per thread error mode being used the OS will ignore the process
4039 // wide error mode set by the host.
4041 //----------------------------------------------------------------------------
4042 void DisableOSWatson(void)
4044 LIMITED_METHOD_CONTRACT;
4046 // When a debugger is attached (or will be attaching), we need to disable the OS GPF dialog.
4047 // If we don't, an unhandled managed exception will launch the OS watson dialog even when
4048 // the debugger is attached.
4049 const UINT lastErrorMode = SetErrorMode(0);
4050 SetErrorMode(lastErrorMode | SEM_NOGPFAULTERRORBOX);
4051 LOG((LF_EH, LL_INFO100, "DisableOSWatson: SetErrorMode = 0x%x\n", lastErrorMode | SEM_NOGPFAULTERRORBOX));
4054 #endif // !FEATURE_PAL
4056 //------------------------------------------------------------------------------
4057 // This function is called on an unhandled exception, via the runtime's
4058 // Unhandled Exception Filter (Hence the name, "last chance", because this
4059 // is the last chance to see the exception. When running under a native
4060 // debugger, that won't generally happen, because the OS notifies the debugger
4061 // instead of calling the application's registered UEF; the debugger will
4062 // show the exception as second chance.)
4063 // The function is also called sometimes for the side effects, which are
4064 // to possibly invoke Watson and to possibly notify the managed debugger.
4065 // If running in a debugger already, either native or managed, we shouldn't
4067 // If not running under a managed debugger, we shouldn't try to send a debugger
4069 //------------------------------------------------------------------------------
4070 LONG WatsonLastChance( // EXCEPTION_CONTINUE_SEARCH, _CONTINUE_EXECUTION
4071 Thread *pThread, // Thread object.
4072 EXCEPTION_POINTERS *pExceptionInfo,// Information about reported exception.
4073 TypeOfReportedError tore) // Just what kind of error is reported?
4075 STATIC_CONTRACT_NOTHROW;
4076 STATIC_CONTRACT_GC_TRIGGERS;
4077 STATIC_CONTRACT_MODE_ANY;
4079 // If allocation fails, we may not produce watson dump. But this is not fatal.
4080 CONTRACT_VIOLATION(AllViolation);
4081 LOG((LF_EH, LL_INFO10, "D::WLC: Enter WatsonLastChance\n"));
4084 static DWORD fDisableWatson = -1;
4085 if (fDisableWatson == -1)
4087 fDisableWatson = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DisableWatsonForManagedExceptions);
4090 if (fDisableWatson && (tore.GetType() == TypeOfReportedError::UnhandledException))
4093 LOG((LF_EH, LL_INFO10, "D::WLC: OS Watson is disabled for an managed unhandled exception\n"));
4094 return EXCEPTION_CONTINUE_SEARCH;
4096 #endif // !FEATURE_PAL
4098 // We don't want to launch Watson if a debugger is already attached to
4100 BOOL shouldNotifyDebugger = FALSE; // Assume we won't debug.
4102 // VS debugger team requested the Whidbey experience, which is no Watson when the debugger thread detects
4103 // that the debugger process is abruptly terminated, and triggers a failfast error. In this particular
4104 // scenario CORDebuggerAttached() will be TRUE, but IsDebuggerPresent() will be FALSE because from OS
4105 // perspective the native debugger has been detached from the debuggee, but CLR has not yet marked the
4106 // managed debugger as detached. Therefore, CORDebuggerAttached() is checked, so Watson will not pop up
4107 // when a debugger is abruptly terminated. It also prevents a debugger from being launched on a helper
4109 BOOL alreadyDebugging = CORDebuggerAttached() || IsDebuggerPresent();
4111 BOOL jitAttachRequested = !alreadyDebugging; // Launch debugger if not already running.
4114 // If BreakOnUnCaughtException is set, we may be using a native debugger to debug this stuff
4115 BOOL BreakOnUnCaughtException = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
4116 if(!alreadyDebugging || (!CORDebuggerAttached() && BreakOnUnCaughtException) )
4118 if (!alreadyDebugging)
4121 LOG((LF_EH, LL_INFO10, "WatsonLastChance: Debugger not attached at sp %p ...\n", GetCurrentSP()));
4124 FaultReportResult result = FaultReportResultQuit;
4126 BOOL fSOException = FALSE;
4128 if ((pExceptionInfo != NULL) &&
4129 (pExceptionInfo->ExceptionRecord != NULL) &&
4130 (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW))
4132 fSOException = TRUE;
4135 if (g_pDebugInterface)
4137 // we are about to let the OS trigger jit attach, however we need to synchronize with our
4138 // own jit attach that we might be doing on another thread
4139 // PreJitAttach races this thread against any others which might be attaching and if some other
4140 // thread is doing it then we wait for its attach to complete first
4141 g_pDebugInterface->PreJitAttach(TRUE, FALSE, FALSE);
4144 // Let unhandled excpetions except stack overflow go to the OS
4145 if (tore.IsUnhandledException() && !fSOException)
4147 return EXCEPTION_CONTINUE_SEARCH;
4149 else if (tore.IsUserBreakpoint())
4151 DoReportFault(pExceptionInfo);
4155 BOOL fWatsonAlreadyLaunched = FALSE;
4156 if (FastInterlockCompareExchange(&g_watsonAlreadyLaunched, 1, 0) != 0)
4158 fWatsonAlreadyLaunched = TRUE;
4161 // Logic to avoid double prompt if more than one threads calling into WatsonLastChance
4162 if (!fWatsonAlreadyLaunched)
4164 // EEPolicy::HandleFatalStackOverflow pushes a FaultingExceptionFrame on the stack after SO
4165 // exception. Our hijack code runs in the exception context, and overwrites the stack space
4166 // after SO excpetion, so we need to pop up this frame before invoking RaiseFailFast.
4167 // This cumbersome code should be removed once SO synchronization is moved to be completely
4169 if (fSOException && pThread && pThread->GetFrame() != FRAME_TOP)
4171 GCX_COOP(); // Must be cooperative to modify frame chain.
4172 pThread->GetFrame()->Pop(pThread);
4175 LOG((LF_EH, LL_INFO10, "D::WLC: Call RaiseFailFastExceptionOnWin7\n"));
4177 // enable preemptive mode before call into OS to allow runtime suspend to finish
4180 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: About to call RaiseFailFastException\n");
4181 RaiseFailFastException(pExceptionInfo == NULL ? NULL : pExceptionInfo->ExceptionRecord,
4182 pExceptionInfo == NULL ? NULL : pExceptionInfo->ContextRecord,
4184 STRESS_LOG0(LF_CORDB, LL_INFO10, "D::RFFE: Return from RaiseFailFastException\n");
4188 if (g_pDebugInterface)
4190 // if execution resumed here then we may or may not be attached
4191 // either way we need to end the attach process and unblock any other
4192 // threads which were waiting for the attach here to complete
4193 g_pDebugInterface->PostJitAttach();
4197 if (IsDebuggerPresent())
4199 result = FaultReportResultDebug;
4200 jitAttachRequested = FALSE;
4205 case FaultReportResultAbort:
4207 // We couldn't launch watson properly. First fall-back to OS error-reporting
4208 // so that we don't break native apps.
4209 EFaultRepRetVal r = frrvErr;
4211 if (pExceptionInfo != NULL)
4215 if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
4217 r = DoReportFault(pExceptionInfo);
4221 // Since the StackOverflow handler also calls us, we must keep our stack budget
4222 // to a minimum. Thus, we will launch a thread to do the actual work.
4223 FaultReportInfo fri;
4224 fri.m_fDoReportFault = TRUE;
4225 fri.m_pExceptionInfo = pExceptionInfo;
4226 // DoFaultCreateThreadReportCallback will overwrite this - if it doesn't, we'll assume it failed.
4227 fri.m_faultRepRetValResult = frrvErr;
4229 // Stack overflow case - we don't have enough stack on our own thread so let the debugger
4230 // helper thread do the work.
4231 if (!g_pDebugInterface || FAILED(g_pDebugInterface->RequestFavor(DoFaultReportDoFavorCallback, &fri)))
4233 // If we can't initialize the debugger helper thread or we are running on the debugger helper
4234 // thread, give it up. We don't have enough stack space.
4237 r = fri.m_faultRepRetValResult;
4241 if ((r == frrvErr) || (r == frrvErrNoDW) || (r == frrvErrTimeout))
4243 // If we don't have an exception record, or otherwise can't use OS error
4244 // reporting then offer the old "press OK to terminate, cancel to debug"
4245 // dialog as a futher fallback.
4246 if (g_pDebugInterface && g_pDebugInterface->FallbackJITAttachPrompt())
4248 // User requested to launch the debugger
4249 shouldNotifyDebugger = TRUE;
4252 else if (r == frrvLaunchDebugger)
4254 // User requested to launch the debugger
4255 shouldNotifyDebugger = TRUE;
4259 case FaultReportResultQuit:
4260 // No debugger, just exit normally
4262 case FaultReportResultDebug:
4263 // JIT attach a debugger here.
4264 shouldNotifyDebugger = TRUE;
4267 UNREACHABLE_MSG("Unknown FaultReportResult");
4271 // When the debugger thread detects that the debugger process is abruptly terminated, and triggers
4272 // a failfast error, CORDebuggerAttached() will be TRUE, but IsDebuggerPresent() will be FALSE.
4273 // If IsDebuggerPresent() is FALSE, do not try to notify the deubgger.
4274 else if (CORDebuggerAttached() && IsDebuggerPresent())
4277 else if (CORDebuggerAttached())
4278 #endif // !FEATURE_PAL
4280 // Already debugging with a managed debugger. Should let that debugger know.
4281 LOG((LF_EH, LL_INFO100, "WatsonLastChance: Managed debugger already attached at sp %p ...\n", GetCurrentSP()));
4283 // The managed EH subsystem ignores native breakpoints and single step exceptions. These exceptions are
4284 // not considered managed, and the managed debugger should not be notified. Moreover, we won't have
4285 // created a managed exception object at this point.
4286 if (tore.GetType() != TypeOfReportedError::NativeBreakpoint)
4288 shouldNotifyDebugger = TRUE;
4294 #endif // !FEATURE_PAL
4296 if (!shouldNotifyDebugger)
4298 LOG((LF_EH, LL_INFO100, "WatsonLastChance: should not notify debugger. Returning EXCEPTION_CONTINUE_SEARCH\n"));
4299 return EXCEPTION_CONTINUE_SEARCH;
4302 // If no debugger interface, we can't notify the debugger.
4303 if (g_pDebugInterface == NULL)
4305 LOG((LF_EH, LL_INFO100, "WatsonLastChance: No debugger interface. Returning EXCEPTION_CONTINUE_SEARCH\n"));
4306 return EXCEPTION_CONTINUE_SEARCH;
4309 LOG((LF_EH, LL_INFO10, "WatsonLastChance: Notifying debugger\n"));
4311 switch (tore.GetType())
4313 case TypeOfReportedError::FatalError:
4314 #ifdef MDA_SUPPORTED
4316 MdaFatalExecutionEngineError * pMDA = MDA_GET_ASSISTANT_EX(FatalExecutionEngineError);
4318 if ((pMDA != NULL) && (pExceptionInfo != NULL) && (pExceptionInfo->ExceptionRecord != NULL))
4320 TADDR addr = (TADDR) pExceptionInfo->ExceptionRecord->ExceptionAddress;
4321 HRESULT hrError = pExceptionInfo->ExceptionRecord->ExceptionCode;
4322 pMDA->ReportFEEE(addr, hrError);
4325 #endif // MDA_SUPPORTED
4327 if (pThread != NULL)
4329 NotifyDebuggerLastChance(pThread, pExceptionInfo, jitAttachRequested);
4331 // If the registed debugger is not a managed debugger, we need to stop the debugger here.
4332 if (!CORDebuggerAttached() && IsDebuggerPresent())
4339 g_pDebugInterface->LaunchDebuggerForUser(GetThread(), pExceptionInfo, FALSE, FALSE);
4342 return EXCEPTION_CONTINUE_SEARCH;
4344 case TypeOfReportedError::UnhandledException:
4345 case TypeOfReportedError::NativeBreakpoint:
4346 // Notify the debugger only if this is a managed thread.
4347 if (pThread != NULL)
4349 return NotifyDebuggerLastChance(pThread, pExceptionInfo, jitAttachRequested);
4353 g_pDebugInterface->JitAttach(pThread, pExceptionInfo, FALSE, FALSE);
4355 // return EXCEPTION_CONTINUE_SEARCH, so OS's UEF will reraise the unhandled exception for debuggers
4356 return EXCEPTION_CONTINUE_SEARCH;
4359 case TypeOfReportedError::UserBreakpoint:
4360 g_pDebugInterface->LaunchDebuggerForUser(pThread, pExceptionInfo, TRUE, FALSE);
4362 return EXCEPTION_CONTINUE_EXECUTION;
4364 case TypeOfReportedError::NativeThreadUnhandledException:
4365 g_pDebugInterface->JitAttach(pThread, pExceptionInfo, FALSE, FALSE);
4367 // return EXCEPTION_CONTINUE_SEARCH, so OS's UEF will reraise the unhandled exception for debuggers
4368 return EXCEPTION_CONTINUE_SEARCH;
4371 _ASSERTE(!"Unknown case in WatsonLastChance");
4372 return EXCEPTION_CONTINUE_SEARCH;
4376 } // LONG WatsonLastChance()
4378 //---------------------------------------------------------------------------------------
4380 // This is just a simple helper to do some basic checking to see if an exception is intercepted.
4381 // It checks that we are on a managed thread and that an exception is indeed in progress.
4384 // true iff we are on a managed thread and an exception is in flight
4387 bool CheckThreadExceptionStateForInterception()
4389 LIMITED_METHOD_CONTRACT;
4391 Thread* pThread = GetThread();
4393 if (pThread == NULL)
4398 if (!pThread->IsExceptionInProgress())
4407 //===========================================================================================
4409 // UNHANDLED EXCEPTION HANDLING
4412 static Volatile<BOOL> fReady = 0;
4413 static SpinLock initLock;
4415 void DECLSPEC_NORETURN RaiseDeadLockException()
4417 STATIC_CONTRACT_THROWS;
4418 STATIC_CONTRACT_SO_TOLERANT;
4420 // Disable the "initialization of static local vars is no thread safe" error
4422 #pragma warning(disable: 4640)
4424 CHECK_LOCAL_STATIC_VAR(static SString s);
4426 #pragma warning(default : 4640)
4435 hr = UtilLoadStringRC(IDS_EE_THREAD_DEADLOCK_VICTIM, name, sizeof(name)/sizeof(WCHAR), 1);
4437 initLock.Init(LOCK_TYPE_DEFAULT);
4438 SpinLockHolder __spinLockHolder(&initLock);
4453 ThrowHR(HOST_E_DEADLOCK, s);
4456 //******************************************************************************
4458 // ExceptionIsAlwaysSwallowed
4460 // Determine whether an exception is of a type that it should always
4461 // be swallowed, even when exceptions otherwise are left to go unhandled.
4462 // (For Whidbey, ThreadAbort, RudeThreadAbort, or AppDomainUnload exception)
4465 // pExceptionInfo EXCEPTION_POINTERS for current exception
4468 // true If the exception is of a type that is always swallowed.
4470 bool ExceptionIsAlwaysSwallowed(EXCEPTION_POINTERS *pExceptionInfo)
4472 bool isSwallowed = false;
4474 // The exception code must be ours, if it is one of our Exceptions.
4475 if (IsComPlusException(pExceptionInfo->ExceptionRecord))
4477 // Our exception code. Get the current exception from the thread.
4478 Thread *pThread = GetThread();
4481 OBJECTREF throwable;
4484 if ((throwable = pThread->GetThrowable()) == NULL)
4486 throwable = pThread->LastThrownObject();
4488 //@todo: could throwable be NULL here?
4489 isSwallowed = IsExceptionOfType(kThreadAbortException, &throwable) ||
4490 IsExceptionOfType(kAppDomainUnloadedException, &throwable);
4495 } // BOOL ExceptionIsAlwaysSwallowed()
4498 // UserBreakpointFilter is used to ensure that we get a popup on user breakpoints (DebugBreak(), hard-coded int 3,
4499 // etc.) as soon as possible.
4501 LONG UserBreakpointFilter(EXCEPTION_POINTERS* pEP)
4513 #ifdef DEBUGGING_SUPPORTED
4514 // Invoke the unhandled exception filter, bypassing any further first pass exception processing and treating
4515 // user breakpoints as if they're unhandled exceptions right away.
4517 // @todo: The InternalUnhandledExceptionFilter can trigger.
4518 CONTRACT_VIOLATION(GCViolation | ThrowsViolation | ModeViolation | FaultViolation | FaultNotFatal);
4521 int result = COMUnhandledExceptionFilter(pEP);
4523 int result = UnhandledExceptionFilter(pEP);
4526 if (result == EXCEPTION_CONTINUE_SEARCH)
4528 // A debugger got attached. Instead of allowing the exception to continue up, and hope for the
4529 // second-chance, we cause it to happen again. The debugger snags all int3's on first-chance. NOTE: the
4530 // InternalUnhandledExceptionFilter allowed GC's to occur, but it may be the case that some managed frames
4531 // may have been unprotected. Therefore, you may have GC holes if you attempt to continue execution from
4533 return EXCEPTION_CONTINUE_EXECUTION;
4535 #endif // DEBUGGING_SUPPORTED
4537 if(ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PRIVATE_PROVIDER_Context, FailFast))
4539 // Fire an ETW FailFast event
4540 FireEtwFailFast(W("StatusBreakpoint"),
4541 (const PVOID)((pEP && pEP->ContextRecord) ? GetIP(pEP->ContextRecord) : 0),
4542 ((pEP && pEP->ExceptionRecord) ? pEP->ExceptionRecord->ExceptionCode : 0),
4544 GetClrInstanceId());
4547 // Otherwise, we termintate the process.
4548 TerminateProcess(GetCurrentProcess(), STATUS_BREAKPOINT);
4550 // Shouldn't get here ...
4551 return EXCEPTION_CONTINUE_EXECUTION;
4552 } // LONG UserBreakpointFilter()
4554 //******************************************************************************
4556 // DefaultCatchFilter
4558 // The old default except filter (v1.0/v1.1) . For user breakpoints, call out to UserBreakpointFilter()
4559 // but otherwise return EXCEPTION_EXECUTE_HANDLER, to swallow the exception.
4562 // pExceptionInfo EXCEPTION_POINTERS for current exception
4563 // pv A constant as an INT_PTR. Must be COMPLUS_EXCEPTION_EXECUTE_HANDLER.
4566 // EXCEPTION_EXECUTE_HANDLER Generally returns this to swallow the exception.
4568 // IMPORTANT!! READ ME!!
4570 // This filter is very similar to DefaultCatchNoSwallowFilter, except when unhandled
4571 // exception policy/config dictate swallowing the exception.
4572 // If you make any changes to this function, look to see if the other one also needs
4575 LONG DefaultCatchFilter(EXCEPTION_POINTERS *ep, PVOID pv)
4588 // @TODO: this seems like a strong candidate for elimination due to duplication with
4589 // our vectored exception handler.
4592 DefaultCatchFilterParam *pParam;
4593 pParam = (DefaultCatchFilterParam *) pv;
4595 // the only valid parameter for DefaultCatchFilter so far
4596 _ASSERTE(pParam->pv == COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4598 PEXCEPTION_RECORD er = ep->ExceptionRecord;
4599 DWORD code = er->ExceptionCode;
4601 if (code == STATUS_SINGLE_STEP || code == STATUS_BREAKPOINT)
4603 return UserBreakpointFilter(ep);
4606 // return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4607 return EXCEPTION_EXECUTE_HANDLER;
4608 } // LONG DefaultCatchFilter()
4611 //******************************************************************************
4613 // DefaultCatchNoSwallowFilter
4615 // The new default except filter (v2.0). For user breakpoints, call out to UserBreakpointFilter().
4616 // Otherwise consults host policy and config file to return EXECUTE_HANDLER / CONTINUE_SEARCH.
4619 // pExceptionInfo EXCEPTION_POINTERS for current exception
4620 // pv A constant as an INT_PTR. Must be COMPLUS_EXCEPTION_EXECUTE_HANDLER.
4623 // EXCEPTION_CONTINUE_SEARCH Generally returns this to let the exception go unhandled.
4624 // EXCEPTION_EXECUTE_HANDLER May return this to swallow the exception.
4626 // IMPORTANT!! READ ME!!
4628 // This filter is very similar to DefaultCatchFilter, except when unhandled
4629 // exception policy/config dictate swallowing the exception.
4630 // If you make any changes to this function, look to see if the other one also needs
4633 LONG DefaultCatchNoSwallowFilter(EXCEPTION_POINTERS *ep, PVOID pv)
4643 DefaultCatchFilterParam *pParam; pParam = (DefaultCatchFilterParam *) pv;
4645 // the only valid parameter for DefaultCatchFilter so far
4646 _ASSERTE(pParam->pv == COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4648 PEXCEPTION_RECORD er = ep->ExceptionRecord;
4649 DWORD code = er->ExceptionCode;
4651 if (code == STATUS_SINGLE_STEP || code == STATUS_BREAKPOINT)
4653 return UserBreakpointFilter(ep);
4656 // If host policy or config file says "swallow"...
4657 if (SwallowUnhandledExceptions())
4658 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4659 return EXCEPTION_EXECUTE_HANDLER;
4662 // If the exception is of a type that is always swallowed (ThreadAbort, AppDomainUnload)...
4663 if (ExceptionIsAlwaysSwallowed(ep))
4664 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
4665 return EXCEPTION_EXECUTE_HANDLER;
4668 // Otherwise, continue search. i.e. let the exception go unhandled (at least for now).
4669 return EXCEPTION_CONTINUE_SEARCH;
4670 } // LONG DefaultCatchNoSwallowFilter()
4672 // Note: This is used only for CoreCLR on WLC.
4674 // We keep a pointer to the previous unhandled exception filter. After we install, we use
4675 // this to call the previous guy. When we un-install, we put them back. Putting them back
4676 // is a bug -- we have no guarantee that the DLL unload order matches the DLL load order -- we
4677 // may in fact be putting back a pointer to a DLL that has been unloaded.
4680 // initialize to -1 because NULL won't detect difference between us not having installed our handler
4681 // yet and having installed it but the original handler was NULL.
4682 static LPTOP_LEVEL_EXCEPTION_FILTER g_pOriginalUnhandledExceptionFilter = (LPTOP_LEVEL_EXCEPTION_FILTER)-1;
4683 #define FILTER_NOT_INSTALLED (LPTOP_LEVEL_EXCEPTION_FILTER) -1
4686 BOOL InstallUnhandledExceptionFilter() {
4687 STATIC_CONTRACT_NOTHROW;
4688 STATIC_CONTRACT_GC_NOTRIGGER;
4689 STATIC_CONTRACT_MODE_ANY;
4690 STATIC_CONTRACT_FORBID_FAULT;
4693 // We will be here only for CoreCLR on WLC since we dont
4694 // register UEF for SL.
4695 if (g_pOriginalUnhandledExceptionFilter == FILTER_NOT_INSTALLED) {
4697 #pragma prefast(push)
4698 #pragma prefast(suppress:28725, "Calling to SetUnhandledExceptionFilter is intentional in this case.")
4699 g_pOriginalUnhandledExceptionFilter = SetUnhandledExceptionFilter(COMUnhandledExceptionFilter);
4700 #pragma prefast(pop)
4702 // make sure is set (ie. is not our special value to indicate unset)
4703 LOG((LF_EH, LL_INFO10, "InstallUnhandledExceptionFilter registered UEF with OS for CoreCLR!\n"));
4705 _ASSERTE(g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED);
4706 #endif // !FEATURE_PAL
4708 // All done - successfully!
4712 void UninstallUnhandledExceptionFilter() {
4713 STATIC_CONTRACT_NOTHROW;
4714 STATIC_CONTRACT_GC_NOTRIGGER;
4715 STATIC_CONTRACT_MODE_ANY;
4716 STATIC_CONTRACT_FORBID_FAULT;
4719 // We will be here only for CoreCLR on WLC or on Mac SL.
4720 if (g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED) {
4722 #pragma prefast(push)
4723 #pragma prefast(suppress:28725, "Calling to SetUnhandledExceptionFilter is intentional in this case.")
4724 SetUnhandledExceptionFilter(g_pOriginalUnhandledExceptionFilter);
4725 #pragma prefast(pop)
4727 g_pOriginalUnhandledExceptionFilter = FILTER_NOT_INSTALLED;
4728 LOG((LF_EH, LL_INFO10, "UninstallUnhandledExceptionFilter unregistered UEF from OS for CoreCLR!\n"));
4730 #endif // !FEATURE_PAL
4734 // Update the current throwable on the thread if necessary. If we're looking at one of our exceptions, and if the
4735 // current throwable on the thread is NULL, then we'll set it to something more useful based on the
4736 // LastThrownObject.
4738 BOOL UpdateCurrentThrowable(PEXCEPTION_RECORD pExceptionRecord)
4740 STATIC_CONTRACT_THROWS;
4741 STATIC_CONTRACT_MODE_ANY;
4742 STATIC_CONTRACT_GC_TRIGGERS;
4744 BOOL useLastThrownObject = FALSE;
4746 Thread* pThread = GetThread();
4748 // GetThrowable needs cooperative.
4751 if ((pThread->GetThrowable() == NULL) && (pThread->LastThrownObject() != NULL))
4753 // If GetThrowable is NULL and LastThrownObject is not, use lastThrownObject.
4754 // In current (June 05) implementation, this is only used to pass to
4755 // NotifyAppDomainsOfUnhandledException, which needs to get a throwable
4756 // from somewhere, with which to notify the AppDomains.
4757 useLastThrownObject = TRUE;
4759 if (IsComPlusException(pExceptionRecord))
4761 #ifndef WIN64EXCEPTIONS
4762 OBJECTREF oThrowable = pThread->LastThrownObject();
4764 // @TODO: we have a problem on Win64 where we won't have any place to
4765 // store the throwable on an unhandled exception. Currently this
4766 // only effects the managed debugging services as they will try
4767 // to inspect the thread to see what the throwable is on an unhandled
4768 // exception.. (but clearly it needs to be fixed asap)
4769 // We have the same problem in EEPolicy::LogFatalError().
4770 LOG((LF_EH, LL_INFO100, "UpdateCurrentThrowable: setting throwable to %s\n", (oThrowable == NULL) ? "NULL" : oThrowable->GetTrueMethodTable()->GetDebugClassName()));
4771 pThread->SafeSetThrowables(oThrowable);
4772 #endif // WIN64EXCEPTIONS
4776 return useLastThrownObject;
4780 // COMUnhandledExceptionFilter is used to catch all unhandled exceptions.
4781 // The debugger will either handle the exception, attach a debugger, or
4782 // notify an existing attached debugger.
4785 struct SaveIPFilterParam
4790 LONG SaveIPFilter(EXCEPTION_POINTERS* ep, LPVOID pv)
4792 WRAPPER_NO_CONTRACT;
4794 SaveIPFilterParam *pParam = (SaveIPFilterParam *) pv;
4795 pParam->ExceptionEIP = (SLOT)GetIP(ep->ContextRecord);
4796 DefaultCatchFilterParam param(COMPLUS_EXCEPTION_EXECUTE_HANDLER);
4797 return DefaultCatchFilter(ep, ¶m);
4800 //------------------------------------------------------------------------------
4802 // Does not call any previous UnhandledExceptionFilter. The assumption is that
4803 // either it is inappropriate to call it (because we have elected to rip the
4804 // process without transitioning completely to the base of the thread), or
4805 // the caller has already consulted the previously installed UnhandledExceptionFilter.
4807 // So we know we are ripping and Watson is appropriate.
4810 // This is a stack-sensitive function if we have an unhandled SO.
4811 // Do not allocate more than a few bytes on the stack or we risk taking an
4812 // AV while trying to throw up Watson.
4815 // pExceptionInfo -- information about the exception that caused the error.
4816 // If the error is not the result of an exception, pass NULL for this
4820 // EXCEPTION_CONTINUE_SEARCH -- we've done anything we will with the exception.
4821 // As far as the runtime is concerned, the process is doomed.
4822 // EXCEPTION_CONTINUE_EXECUTION -- means a debugger "caught" the exception and
4823 // wants to continue running.
4824 // EXCEPTION_EXECUTE_HANDLER -- CoreCLR only, and only when not running as a UEF.
4825 // Returned only if the host has asked us to swallow unhandled exceptions on
4826 // managed threads in an AD they (the host) creates.
4827 //------------------------------------------------------------------------------
4828 LONG InternalUnhandledExceptionFilter_Worker(
4829 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception
4831 STATIC_CONTRACT_THROWS;
4832 STATIC_CONTRACT_GC_TRIGGERS;
4833 STATIC_CONTRACT_MODE_ANY;
4836 static int fBreakOnUEF = -1;
4837 if (fBreakOnUEF==-1) fBreakOnUEF = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUEF);
4838 _ASSERTE(!fBreakOnUEF);
4841 STRESS_LOG2(LF_EH, LL_INFO10, "In InternalUnhandledExceptionFilter_Worker, Exception = %x, sp = %p\n",
4842 pExceptionInfo->ExceptionRecord->ExceptionCode, GetCurrentSP());
4844 // If we can't enter the EE, done.
4845 if (g_fForbidEnterEE)
4847 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: g_fForbidEnterEE is TRUE\n"));
4848 return EXCEPTION_CONTINUE_SEARCH;
4852 if (GetEEPolicy()->GetActionOnFailure(FAIL_FatalRuntime) == eDisableRuntime)
4854 ETaskType type = ::GetCurrentTaskType();
4855 if (type != TT_UNKNOWN && type != TT_USER)
4857 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: calling EEPolicy::HandleFatalError\n"));
4858 EEPolicy::HandleFatalError(COR_E_EXECUTIONENGINE, (UINT_PTR)GetIP(pExceptionInfo->ContextRecord), NULL, pExceptionInfo);
4862 // We don't do anything when this is called from an unmanaged thread.
4863 Thread *pThread = GetThread();
4866 static bool bBreakOnUncaught = false;
4867 static int fBreakOnUncaught = 0;
4869 if (!bBreakOnUncaught)
4871 fBreakOnUncaught = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
4872 bBreakOnUncaught = true;
4874 if (fBreakOnUncaught != 0)
4876 if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW)
4878 // if we've got an uncaught SO, we don't have enough stack to pop a debug break. So instead,
4879 // loop infinitely and we can attach a debugger at that point and break in.
4880 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Infinite loop on uncaught SO\n"));
4887 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: ASSERTING on uncaught\n"));
4888 _ASSERTE(!"BreakOnUnCaughtException");
4893 #ifdef _DEBUG_ADUNLOAD
4894 printf("%x InternalUnhandledExceptionFilter_Worker: Called for %x\n",
4895 ((pThread == NULL) ? NULL : pThread->GetThreadId()), pExceptionInfo->ExceptionRecord->ExceptionCode);
4899 // This shouldn't be possible, but MSVC re-installs us... for now, just bail if this happens.
4900 if (g_fNoExceptions)
4902 return EXCEPTION_CONTINUE_SEARCH;
4905 // Are we looking at a stack overflow here?
4906 if ((pThread != NULL) && !pThread->DetermineIfGuardPagePresent())
4908 g_fForbidEnterEE = true;
4911 #ifdef DEBUGGING_SUPPORTED
4913 // Mark that this exception has gone unhandled. At the moment only the debugger will
4914 // ever look at this flag. This should come before any user-visible side effect of an exception
4915 // being unhandled as seen from managed code or from a debugger. These include the
4916 // managed unhandled notification callback, execution of catch/finally clauses,
4917 // receiving the managed debugger unhandled exception event,
4918 // the OS sending the debugger 2nd pass native exception notification, etc.
4920 // This needs to be done before the check for TSNC_ProcessedUnhandledException because it is perfectly
4921 // legitimate (though rare) for the debugger to be inspecting exceptions which are nested in finally
4922 // clauses that run after an unhandled exception has already occurred on the thread
4923 if ((pThread != NULL) && pThread->IsExceptionInProgress())
4925 LOG((LF_EH, LL_INFO1000, "InternalUnhandledExceptionFilter_Worker: Set unhandled exception flag at %p\n",
4926 pThread->GetExceptionState()->GetFlags() ));
4927 pThread->GetExceptionState()->GetFlags()->SetUnhandled();
4931 // If we have already done unhandled exception processing for this thread, then
4932 // simply return back. See comment in threads.h for details for the flag
4935 if (pThread && (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException) || pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled)))
4937 // This assert shouldnt be hit in CoreCLR since:
4939 // 1) It has no concept of managed entry point that is invoked by the shim. You can
4940 // only run managed code via hosting APIs that will run code in non-default domains.
4942 // 2) Managed threads cannot be created in DefaultDomain since no user code executes
4943 // in default domain.
4945 // So, if this is hit, something is not right!
4946 if (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
4948 _ASSERTE(!"How come a thread with TSNC_ProcessedUnhandledException state entered the UEF on CoreCLR?");
4951 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: have already processed unhandled exception for this thread.\n"));
4952 return EXCEPTION_CONTINUE_SEARCH;
4955 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Handling\n"));
4957 struct Param : SaveIPFilterParam
4959 EXCEPTION_POINTERS *pExceptionInfo;
4965 param.ExceptionEIP = 0;
4966 param.pExceptionInfo = pExceptionInfo;
4967 param.pThread = pThread;
4968 param.retval = EXCEPTION_CONTINUE_SEARCH; // Result of UEF filter.
4970 // Is this a particular kind of exception that we'd like to ignore?
4971 param.fIgnore = ((param.pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
4972 (param.pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP));
4974 PAL_TRY(Param *, pParam, ¶m)
4976 // If fIgnore, then this is some sort of breakpoint, not a "normal" unhandled exception. But, the
4977 // breakpoint is due to an int3 or debugger step instruction, not due to calling Debugger.Break()
4978 TypeOfReportedError tore = pParam->fIgnore ? TypeOfReportedError::NativeBreakpoint : TypeOfReportedError::UnhandledException;
4981 // If this exception is on a thread without managed code, then report this as a NativeThreadUnhandledException
4983 // The thread object may exist if there was once managed code on the stack, but if the exception never
4984 // bubbled thru managed code, ie no managed code is on its stack, then this is a native unhandled exception
4986 // Ignore breakpoints and single-step.
4987 if (!pParam->fIgnore)
4988 { // Possibly interesting exception. Is there no Thread at all? Or, is there a Thread,
4989 // but with no exception at all on it?
4990 if ((pParam->pThread == NULL) ||
4991 (pParam->pThread->IsThrowableNull() && pParam->pThread->IsLastThrownObjectNull()) )
4992 { // Whatever this exception is, we don't know about it. Treat as Native.
4993 tore = TypeOfReportedError::NativeThreadUnhandledException;
4997 // If there is no throwable on the thread, go ahead and update from the last thrown exception if possible.
4998 // Note: don't do this for exceptions that we're going to ignore below anyway...
4999 BOOL useLastThrownObject = FALSE;
5000 if (!pParam->fIgnore && (pParam->pThread != NULL))
5002 useLastThrownObject = UpdateCurrentThrowable(pParam->pExceptionInfo->ExceptionRecord);
5005 #ifdef DEBUGGING_SUPPORTED
5007 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Notifying Debugger...\n"));
5009 // If we are using the throwable in LastThrownObject, mark that it is now unhandled
5010 if ((pParam->pThread != NULL) && useLastThrownObject)
5012 LOG((LF_EH, LL_INFO1000, "InternalUnhandledExceptionFilter_Worker: Set lto is unhandled\n"));
5013 pParam->pThread->MarkLastThrownObjectUnhandled();
5017 // We don't want the managed debugger to try to "intercept" breakpoints
5018 // or singlestep exceptions.
5019 // TODO: why does the exception handling code need to set this? Shouldn't the debugger code
5020 // be able to determine what it can/should intercept?
5021 if ((pParam->pThread != NULL) && pParam->pThread->IsExceptionInProgress() && pParam->fIgnore)
5023 pParam->pThread->GetExceptionState()->GetFlags()->SetDebuggerInterceptNotPossible();
5027 if (pParam->pThread != NULL)
5029 BOOL fIsProcessTerminating = TRUE;
5031 // In CoreCLR, we can be asked to not let an exception go unhandled on managed threads in a given AppDomain.
5032 // If the exception reaches the top of the thread's stack, we simply deliver AppDomain's UnhandledException event and
5033 // return back to the filter, instead of letting the process terminate because of unhandled exception.
5035 // Below is how we perform the check:
5037 // 1) The flag is specified on the AD when it is created by the host and all managed threads created
5038 // in such an AD will inherit the flag. For non-finalizer and non-threadpool threads, we check the flag against the thread.
5039 // 2) The finalizer thread always switches to the AD of the object that is going to be finalized. Thus,
5040 // while it wont have the flag specified, the AD it switches to will.
5041 // 3) The threadpool thread also switches to the correct AD before executing the request. The thread wont have the
5042 // flag specified, but the AD it switches to will.
5044 // This code must only be exercised when running as a normal filter; returning
5045 // EXCEPTION_EXECUTE_HANDLER is not valid if this code is being invoked from
5047 // Fortunately, we should never get into this case, since the thread flag about
5048 // ignoring unhandled exceptions cannot be set on the default domain.
5050 if (IsFinalizerThread() || (pParam->pThread->IsThreadPoolThread()))
5051 fIsProcessTerminating = !(pParam->pThread->GetDomain()->IgnoreUnhandledExceptions());
5053 fIsProcessTerminating = !(pParam->pThread->HasThreadStateNC(Thread::TSNC_IgnoreUnhandledExceptions));
5056 // Setup the watson bucketing details for UE processing.
5057 // do this before notifying appdomains of the UE so if an AD attempts to
5058 // retrieve the bucket params in the UE event handler it gets the correct data.
5059 SetupWatsonBucketsForUEF(useLastThrownObject);
5060 #endif // !FEATURE_PAL
5062 // Send notifications to the AppDomains.
5063 NotifyAppDomainsOfUnhandledException(pParam->pExceptionInfo, NULL, useLastThrownObject, fIsProcessTerminating /*isTerminating*/);
5065 // If the process is not terminating, then return back to the filter and ask it to execute
5066 if (!fIsProcessTerminating)
5068 pParam->retval = EXCEPTION_EXECUTE_HANDLER;
5074 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Not collecting bucket information as thread object does not exist\n"));
5077 // AppDomain.UnhandledException event could have thrown an exception that would have gone unhandled in managed code.
5078 // The runtime swallows all such exceptions. Hence, if we are not using LastThrownObject and the current LastThrownObject
5079 // is not the same as the one in active exception tracker (if available), then update the last thrown object.
5080 if ((pParam->pThread != NULL) && (!useLastThrownObject))
5084 OBJECTREF oThrowable = pParam->pThread->GetThrowable();
5085 if ((oThrowable != NULL) && (pParam->pThread->LastThrownObject() != oThrowable))
5087 pParam->pThread->SafeSetLastThrownObject(oThrowable);
5088 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Resetting the LastThrownObject as it appears to have changed.\n"));
5091 GCX_COOP_NO_DTOR_END();
5094 // Launch Watson and see if we want to debug the process
5096 // Note that we need to do this before "ignoring" exceptions like
5097 // breakpoints and single step exceptions
5100 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Launching Watson at sp %p ...\n", GetCurrentSP()));
5102 if (WatsonLastChance(pParam->pThread, pParam->pExceptionInfo, tore) == EXCEPTION_CONTINUE_EXECUTION)
5104 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: debugger ==> EXCEPTION_CONTINUE_EXECUTION\n"));
5105 pParam->retval = EXCEPTION_CONTINUE_EXECUTION;
5109 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: ... returned.\n"));
5110 #endif // DEBUGGING_SUPPORTED
5114 // Except for notifying debugger, ignore exception if unmanaged, or
5115 // if it's a debugger-generated exception or user breakpoint exception.
5117 if (tore.GetType() == TypeOfReportedError::NativeThreadUnhandledException)
5119 pParam->retval = EXCEPTION_CONTINUE_SEARCH;
5120 #if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5121 DoReportForUnhandledNativeException(pParam->pExceptionInfo);
5126 if (pParam->fIgnore)
5128 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker, ignoring the exception\n"));
5129 pParam->retval = EXCEPTION_CONTINUE_SEARCH;
5130 #if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5131 DoReportForUnhandledNativeException(pParam->pExceptionInfo);
5136 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter_Worker: Calling DefaultCatchHandler\n"));
5138 // Call our default catch handler to do the managed unhandled exception work.
5139 DefaultCatchHandler(pParam->pExceptionInfo, NULL, useLastThrownObject,
5140 TRUE /*isTerminating*/, FALSE /*isThreadBaseFIlter*/, FALSE /*sendAppDomainEvents*/, TRUE /* sendWindowsEventLog */);
5144 PAL_EXCEPT_FILTER (SaveIPFilter)
5146 // Should never get here.
5149 sprintf_s(buffer, 200, "\nInternal error: Uncaught exception was thrown from IP = %p in UnhandledExceptionFilter_Worker on thread 0x%08x\n",
5150 param.ExceptionEIP, ((GetThread() == NULL) ? NULL : GetThread()->GetThreadId()));
5151 PrintToStdErrA(buffer);
5152 _ASSERTE(!"Unexpected exception in UnhandledExceptionFilter_Worker");
5154 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE)
5158 //if (param.fIgnore)
5160 // VC's try/catch ignores breakpoint or single step exceptions. We can not continue running.
5161 // TerminateProcess(GetCurrentProcess(), pExceptionInfo->ExceptionRecord->ExceptionCode);
5164 return param.retval;
5165 } // LONG InternalUnhandledExceptionFilter_Worker()
5167 //------------------------------------------------------------------------------
5169 // Calls our InternalUnhandledExceptionFilter for Watson at the appropriate
5170 // place in the chain.
5172 // For non-side-by-side CLR's, we call everyone else's UEF first.
5174 // For side-by-side CLR's, we call our own filter first. This is primary
5175 // so Whidbey's UEF won't put up a second dialog box. In exchange,
5176 // side-by-side CLR's won't put up UI's unless the EH really came
5177 // from that instance's managed code.
5180 // pExceptionInfo -- information about the exception that caused the error.
5181 // If the error is not the result of an exception, pass NULL for this
5185 // EXCEPTION_CONTINUE_SEARCH -- we've done anything we will with the exception.
5186 // As far as the runtime is concerned, the process is doomed.
5187 // EXCEPTION_CONTINUE_EXECUTION -- means a debugger "caught" the exception and
5188 // wants to continue running.
5189 //------------------------------------------------------------------------------
5190 LONG InternalUnhandledExceptionFilter(
5191 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception
5193 STATIC_CONTRACT_THROWS;
5194 STATIC_CONTRACT_GC_TRIGGERS;
5195 STATIC_CONTRACT_MODE_ANY;
5196 // We don't need to be SO-robust for an unhandled exception
5197 SO_NOT_MAINLINE_FUNCTION;
5199 LOG((LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter: at sp %p.\n", GetCurrentSP()));
5201 // Side-by-side UEF: Calls ours first, then the rest (unless we put up a UI for
5204 LONG retval = InternalUnhandledExceptionFilter_Worker(pExceptionInfo); // Result of UEF filter.
5206 // Keep looking, or done?
5207 if (retval != EXCEPTION_CONTINUE_SEARCH)
5212 BOOL fShouldOurUEFDisplayUI = ShouldOurUEFDisplayUI(pExceptionInfo);
5214 // If this is a managed exception thrown by this instance of the CLR, the exception is no one's
5215 // business but ours (nudge, nudge: Whidbey). Break the UEF chain at this point.
5216 if (fShouldOurUEFDisplayUI)
5221 // Chaining back to previous UEF handler could be a potential security risk. See
5222 // http://uninformed.org/index.cgi?v=4&a=5&p=1 for details. We are not alone in
5223 // stopping the chain - CRT (as of Orcas) is also doing that.
5225 // The change below applies to a thread that starts in native mode and transitions to managed.
5227 // Let us assume the process loaded two CoreCLRs, C1 and C2, in that order. Thus, in the UEF chain
5228 // (assuming no other entity setup their UEF), C2?s UEF will be the topmost.
5230 // Now, assume the stack looks like the following (stack grows down):
5233 // Managed Frame (C1)
5234 // Managed Frame (C2)
5235 // Managed Frame (C1)
5236 // Managed Frame (C2)
5237 // Managed Frame (C1)
5239 // Suppose an exception is thrown in C1 instance in the last managed frame and it goes unhandled. Eventually
5240 // it will reach the OS which will invoke the UEF. Note that the topmost UEF belongs to C2 instance and it
5241 // will start processing the exception. C2?s UEF could return EXCEPTION_CONTINUE_SEARCH to indicate
5242 // that we should handoff the processing to the last installed UEF. In the example above, we would handoff
5243 // the control to the UEF of the CoreCLR instance that actually threw the exception today. In reality, it
5244 // could be some unknown code too.
5246 // Not chaining back to the last UEF, in the case of this example, would imply that certain notifications
5247 // (e.g. Unhandled Exception Notification to the AppDomain) specific to the instance that raised the exception
5248 // will not get fired. However, similar behavior can happen today if another UEF sits between
5249 // C1 and C2 and that may not callback to C1 or perhaps just terminate process.
5251 // For CoreCLR, this will not be an issue. See
5252 // http://sharepoint/sites/clros/Shared%20Documents/Design%20Documents/EH/Chaining%20in%20%20UEF%20-%20One%20Pager.docx
5255 // Note: Also see the conditional UEF registration with the OS in EEStartupHelper.
5257 // We would be here only on CoreCLR for WLC since we dont register
5258 // the UEF with the OS for SL.
5259 if (g_pOriginalUnhandledExceptionFilter != FILTER_NOT_INSTALLED
5260 && g_pOriginalUnhandledExceptionFilter != NULL)
5262 STRESS_LOG1(LF_EH, LL_INFO100, "InternalUnhandledExceptionFilter: Not chaining back to previous UEF at address %p on CoreCLR!\n", g_pOriginalUnhandledExceptionFilter);
5267 } // LONG InternalUnhandledExceptionFilter()
5269 // This filter is used to trigger unhandled exception processing for the entrypoint thread
5270 // incase an exception goes unhandled from it. This makes us independent of the OS
5271 // UEF mechanism to invoke our registered UEF to trigger CLR specific unhandled exception
5272 // processing since that can be skipped if another UEF registered over ours and not chain back.
5273 LONG EntryPointFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID _pData)
5286 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return EXCEPTION_CONTINUE_SEARCH;);
5288 // Invoke the UEF worker to perform unhandled exception processing
5289 ret = InternalUnhandledExceptionFilter_Worker (pExceptionInfo);
5291 Thread* pThread = GetThread();
5294 // Set the flag that we have done unhandled exception processing for this thread
5295 // so that we dont duplicate the effort in the UEF.
5297 // For details on this flag, refer to threads.h.
5298 LOG((LF_EH, LL_INFO100, "EntryPointFilter: setting TSNC_ProcessedUnhandledException\n"));
5299 pThread->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException);
5303 END_SO_INTOLERANT_CODE;
5308 //------------------------------------------------------------------------------
5310 // The actual UEF. Defers to InternalUnhandledExceptionFilter.
5312 // Updated to be in its own code segment named CLR_UEF_SECTION_NAME to prevent
5313 // "VirtualProtect" calls from affecting its pages and thus, its
5314 // invocation. For details, see the comment within the implementation of
5315 // CExecutionEngine::ClrVirtualProtect.
5318 // pExceptionInfo -- information about the exception
5321 // the result of calling InternalUnhandledExceptionFilter
5322 //------------------------------------------------------------------------------
5323 #if !defined(FEATURE_PAL)
5324 #pragma code_seg(push, uef, CLR_UEF_SECTION_NAME)
5325 #endif // !FEATURE_PAL
5326 LONG __stdcall COMUnhandledExceptionFilter( // EXCEPTION_CONTINUE_SEARCH or EXCEPTION_CONTINUE_EXECUTION
5327 EXCEPTION_POINTERS *pExceptionInfo) // Information about the exception.
5329 STATIC_CONTRACT_THROWS;
5330 STATIC_CONTRACT_GC_TRIGGERS;
5331 STATIC_CONTRACT_MODE_ANY;
5332 // We don't need to be SO-robust for an unhandled exception
5333 SO_NOT_MAINLINE_FUNCTION;
5335 LONG retVal = EXCEPTION_CONTINUE_SEARCH;
5337 // Incase of unhandled exceptions on managed threads, we kick in our UE processing at the thread base and also invoke
5338 // UEF callbacks that various runtimes have registered with us. Once the callbacks return, we return back to the OS
5339 // to give other registered UEFs a chance to do their custom processing.
5341 // If the topmost UEF registered with the OS belongs to mscoruef.dll (or someone chained back to its UEF callback),
5342 // it will start invoking the UEF callbacks (which is this function, COMUnhandledExceptionFiler) registered by
5343 // various runtimes again.
5345 // Thus, check if this UEF has already been invoked in context of this thread and runtime and if so, dont invoke it again.
5346 if (GetThread() && (GetThread()->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException) ||
5347 GetThread()->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled)))
5349 LOG((LF_EH, LL_INFO10, "Exiting COMUnhandledExceptionFilter since we have already done UE processing for this thread!\n"));
5354 retVal = InternalUnhandledExceptionFilter(pExceptionInfo);
5356 // If thread object exists, mark that this thread has done unhandled exception processing
5359 LOG((LF_EH, LL_INFO100, "COMUnhandledExceptionFilter: setting TSNC_ProcessedUnhandledException\n"));
5360 GetThread()->SetThreadStateNC(Thread::TSNC_ProcessedUnhandledException);
5364 } // LONG __stdcall COMUnhandledExceptionFilter()
5365 #if !defined(FEATURE_PAL)
5366 #pragma code_seg(pop, uef)
5367 #endif // !FEATURE_PAL
5369 void PrintStackTraceToStdout();
5371 static SString GetExceptionMessageWrapper(Thread* pThread, OBJECTREF throwable)
5373 STATIC_CONTRACT_THROWS;
5374 STATIC_CONTRACT_MODE_COOPERATIVE;
5375 STATIC_CONTRACT_GC_TRIGGERS;
5377 StackSString result;
5379 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
5380 GetExceptionMessage(throwable, result);
5381 UNINSTALL_NESTED_EXCEPTION_HANDLER();
5386 void STDMETHODCALLTYPE
5387 DefaultCatchHandlerExceptionMessageWorker(Thread* pThread,
5388 OBJECTREF throwable,
5389 __inout_ecount(buf_size) WCHAR *buf,
5391 BOOL sendWindowsEventLog)
5393 GCPROTECT_BEGIN(throwable);
5394 if (throwable != NULL)
5396 PrintToStdErrA("\n");
5398 if (FAILED(UtilLoadResourceString(CCompRC::Error, IDS_EE_UNHANDLED_EXCEPTION, buf, buf_size)))
5400 wcsncpy_s(buf, buf_size, SZ_UNHANDLED_EXCEPTION, SZ_UNHANDLED_EXCEPTION_CHARLEN);
5403 PrintToStdErrW(buf);
5404 PrintToStdErrA(" ");
5406 SString message = GetExceptionMessageWrapper(pThread, throwable);
5408 if (!message.IsEmpty())
5410 NPrintToStdErrW(message, message.GetCount());
5413 PrintToStdErrA("\n");
5415 #if defined(FEATURE_EVENT_TRACE) && !defined(FEATURE_PAL)
5416 // Send the log to Windows Event Log
5417 if (sendWindowsEventLog && ShouldLogInEventLog())
5421 EventReporter reporter(EventReporter::ERT_UnhandledException);
5423 if (IsException(throwable->GetMethodTable()))
5425 if (!message.IsEmpty())
5427 reporter.AddDescription(message);
5434 TypeString::AppendType(s, TypeHandle(throwable->GetMethodTable()), TypeString::FormatNamespace | TypeString::FormatFullInst);
5435 reporter.AddDescription(s);
5436 LogCallstackForEventReporter(reporter);
5442 EX_END_CATCH(SwallowAllExceptions);
5449 //******************************************************************************
5450 // DefaultCatchHandler -- common processing for otherwise uncaught exceptions.
5451 //******************************************************************************
5452 void STDMETHODCALLTYPE
5453 DefaultCatchHandler(PEXCEPTION_POINTERS pExceptionPointers,
5454 OBJECTREF *pThrowableIn,
5455 BOOL useLastThrownObject,
5457 BOOL isThreadBaseFilter,
5458 BOOL sendAppDomainEvents,
5459 BOOL sendWindowsEventLog)
5469 // <TODO> The strings in here should be translatable.</TODO>
5470 LOG((LF_EH, LL_INFO10, "In DefaultCatchHandler\n"));
5473 static bool bHaveInitialized_BreakOnUncaught = false;
5474 enum BreakOnUncaughtAction {
5475 breakOnNone = 0, // Default.
5476 breakOnAll = 1, // Always break.
5477 breakSelective = 2, // Break on exceptions application can catch,
5478 // but not ThreadAbort, AppdomainUnload
5481 static DWORD breakOnUncaught = breakOnNone;
5483 if (!bHaveInitialized_BreakOnUncaught)
5485 breakOnUncaught = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException);
5486 if (breakOnUncaught > breakOnMax)
5487 { // Could turn it off completely, or turn into legal value. Since it is debug code, be accommodating.
5488 breakOnUncaught = breakOnAll;
5490 bHaveInitialized_BreakOnUncaught = true;
5493 if (breakOnUncaught == breakOnAll)
5495 _ASSERTE(!"BreakOnUnCaughtException");
5498 int suppressSelectiveBreak = false; // to filter for the case where breakOnUncaught == "2"
5501 Thread *pThread = GetThread();
5503 // The following reduces a window for a race during shutdown.
5506 _ASSERTE(g_fEEShutDown);
5512 ThreadPreventAsyncHolder prevAsync;
5516 OBJECTREF throwable;
5518 if (pThrowableIn != NULL)
5520 throwable = *pThrowableIn;
5522 else if (useLastThrownObject)
5524 throwable = pThread->LastThrownObject();
5528 throwable = pThread->GetThrowable();
5531 // If we've got no managed object, then we can't send an event or print a message, so we just return.
5532 if (throwable == NULL)
5535 if (!pThread->IsRudeAbortInitiated())
5537 LOG((LF_EH, LL_INFO10, "Unhandled exception, throwable == NULL\n"));
5545 DWORD unbreakableLockCount = 0;
5546 // Do not care about lock check for unhandled exception.
5547 while (pThread->HasUnbreakableLock())
5549 pThread->DecUnbreakableLockCount();
5550 unbreakableLockCount ++;
5552 BOOL fOwnsSpinLock = pThread->HasThreadStateNC(Thread::TSNC_OwnsSpinLock);
5555 pThread->ResetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5559 GCPROTECT_BEGIN(throwable);
5560 //BOOL IsStackOverflow = (throwable->GetTrueMethodTable() == g_pStackOverflowExceptionClass);
5561 BOOL IsOutOfMemory = (throwable->GetTrueMethodTable() == g_pOutOfMemoryExceptionClass);
5563 // Notify the AppDomain that we have taken an unhandled exception. Can't notify of stack overflow -- guard
5564 // page is not yet reset.
5565 BOOL SentEvent = FALSE;
5567 // Send up the unhandled exception appdomain event.
5568 if (sendAppDomainEvents)
5570 SentEvent = NotifyAppDomainsOfUnhandledException(pExceptionPointers, &throwable, useLastThrownObject, isTerminating);
5573 const int buf_size = 128;
5574 WCHAR buf[buf_size] = {0};
5576 // See detailed explanation of this flag in threads.cpp. But the basic idea is that we already
5577 // reported the exception in the AppDomain where it went unhandled, so we don't need to report
5578 // it at the process level.
5579 // Print the unhandled exception message.
5580 if (!pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled))
5586 // If this isn't ThreadAbortException, we want to print a stack trace to indicate why this thread abruptly
5587 // terminated. Exceptions kill threads rarely enough that an uncached name check is reasonable.
5590 if (/*IsStackOverflow ||*/
5591 !pThread->DetermineIfGuardPagePresent() ||
5594 // We have to be very careful. If we walk off the end of the stack, the process will just
5595 // die. e.g. IsAsyncThreadException() and Exception.ToString both consume too much stack -- and can't
5598 PrintToStdErrA("\n");
5600 if (FAILED(UtilLoadStringRC(IDS_EE_UNHANDLED_EXCEPTION, buf, buf_size)))
5602 wcsncpy_s(buf, COUNTOF(buf), SZ_UNHANDLED_EXCEPTION, SZ_UNHANDLED_EXCEPTION_CHARLEN);
5605 PrintToStdErrW(buf);
5609 PrintToStdErrA(" OutOfMemoryException.\n");
5613 PrintToStdErrA(" StackOverflowException.\n");
5616 else if (!CanRunManagedCode(LoaderLockCheck::None))
5618 // Well, if we can't enter the runtime, we very well can't get the exception message.
5621 else if (SentEvent || IsAsyncThreadException(&throwable))
5623 // We don't print anything on async exceptions, like ThreadAbort.
5625 INDEBUG(suppressSelectiveBreak=TRUE);
5627 else if (isThreadBaseFilter && IsExceptionOfType(kAppDomainUnloadedException, &throwable))
5629 // AppdomainUnloadedException is also a special case.
5631 INDEBUG(suppressSelectiveBreak=TRUE);
5634 // Finally, should we print the message?
5637 // this is stack heavy because of the CQuickWSTRBase, so we break it out
5638 // and don't have to carry the weight through our other code paths.
5639 DefaultCatchHandlerExceptionMessageWorker(pThread, throwable, buf, buf_size, sendWindowsEventLog);
5644 LOG((LF_EH, LL_INFO10, "Exception occurred while processing uncaught exception\n"));
5645 UtilLoadStringRC(IDS_EE_EXCEPTION_TOSTRING_FAILED, buf, buf_size);
5646 PrintToStdErrA("\n ");
5647 PrintToStdErrW(buf);
5648 PrintToStdErrA("\n");
5650 EX_END_CATCH(SwallowAllExceptions);
5653 { // If we got here, we can't even print the localized error message. Print non-localized.
5654 LOG((LF_EH, LL_INFO10, "Exception occurred while logging processing uncaught exception\n"));
5655 PrintToStdErrA("\n Error: Can't print exception string because Exception.ToString() failed.\n");
5657 EX_END_CATCH(SwallowAllExceptions);
5661 if ((breakOnUncaught == breakSelective) && !suppressSelectiveBreak)
5663 _ASSERTE(!"BreakOnUnCaughtException");
5665 #endif // defined(_DEBUG)
5667 FlushLogging(); // Flush any logging output
5671 // Do not care about lock check for unhandled exception.
5672 while (unbreakableLockCount)
5674 pThread->IncUnbreakableLockCount();
5675 unbreakableLockCount --;
5679 pThread->SetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5682 } // DefaultCatchHandler()
5685 //******************************************************************************
5686 // NotifyAppDomainsOfUnhandledException -- common processing for otherwise uncaught exceptions.
5687 //******************************************************************************
5688 BOOL NotifyAppDomainsOfUnhandledException(
5689 PEXCEPTION_POINTERS pExceptionPointers,
5690 OBJECTREF *pThrowableIn,
5691 BOOL useLastThrownObject,
5703 static int fBreakOnNotify = -1;
5704 if (fBreakOnNotify==-1) fBreakOnNotify = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnNotify);
5705 _ASSERTE(!fBreakOnNotify);
5708 BOOL SentEvent = FALSE;
5710 LOG((LF_EH, LL_INFO10, "In NotifyAppDomainsOfUnhandledException\n"));
5712 Thread *pThread = GetThread();
5714 // The following reduces a window for a race during shutdown.
5717 _ASSERTE(g_fEEShutDown);
5721 // See detailed explanation of this flag in threads.cpp. But the basic idea is that we already
5722 // reported the exception in the AppDomain where it went unhandled, so we don't need to report
5723 // it at the process level.
5724 if (pThread->HasThreadStateNC(Thread::TSNC_AppDomainContainUnhandled))
5727 ThreadPreventAsyncHolder prevAsync;
5731 OBJECTREF throwable;
5733 if (pThrowableIn != NULL)
5735 throwable = *pThrowableIn;
5737 else if (useLastThrownObject)
5739 throwable = pThread->LastThrownObject();
5743 throwable = pThread->GetThrowable();
5746 // If we've got no managed object, then we can't send an event, so we just return.
5747 if (throwable == NULL)
5753 DWORD unbreakableLockCount = 0;
5754 // Do not care about lock check for unhandled exception.
5755 while (pThread->HasUnbreakableLock())
5757 pThread->DecUnbreakableLockCount();
5758 unbreakableLockCount ++;
5760 BOOL fOwnsSpinLock = pThread->HasThreadStateNC(Thread::TSNC_OwnsSpinLock);
5763 pThread->ResetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5767 GCPROTECT_BEGIN(throwable);
5768 //BOOL IsStackOverflow = (throwable->GetTrueMethodTable() == g_pStackOverflowExceptionClass);
5770 // Notify the AppDomain that we have taken an unhandled exception. Can't notify of stack overflow -- guard
5771 // page is not yet reset.
5773 // Send up the unhandled exception appdomain event.
5775 // If we can't run managed code, we can't deliver the event. Nor do we attempt to delieve the event in stack
5776 // overflow or OOM conditions.
5777 if (/*!IsStackOverflow &&*/
5778 pThread->DetermineIfGuardPagePresent() &&
5779 CanRunManagedCode(LoaderLockCheck::None))
5783 #if !defined(WIN64EXCEPTIONS)
5784 // If the Thread object's exception state's exception pointers
5785 // is null, use the passed-in pointer.
5786 BOOL bSetPointers = FALSE;
5788 ThreadExceptionState* pExceptionState = pThread->GetExceptionState();
5790 if (pExceptionState->GetExceptionPointers() == NULL)
5792 bSetPointers = TRUE;
5793 pExceptionState->SetExceptionPointers(pExceptionPointers);
5796 #endif // !defined(WIN64EXCEPTIONS)
5798 INSTALL_NESTED_EXCEPTION_HANDLER(pThread->GetFrame());
5800 // This guy will never throw, but it will need a spot to store
5801 // any nested exceptions it might find.
5802 SentEvent = AppDomain::OnUnhandledException(&throwable, isTerminating);
5804 UNINSTALL_NESTED_EXCEPTION_HANDLER();
5806 #if !defined(WIN64EXCEPTIONS)
5810 pExceptionState->SetExceptionPointers(NULL);
5813 #endif // !defined(WIN64EXCEPTIONS)
5820 // Do not care about lock check for unhandled exception.
5821 while (unbreakableLockCount)
5823 pThread->IncUnbreakableLockCount();
5824 unbreakableLockCount --;
5828 pThread->SetThreadStateNC(Thread::TSNC_OwnsSpinLock);
5834 } // NotifyAppDomainsOfUnhandledException()
5837 //******************************************************************************
5839 // ThreadBaseExceptionFilter_Worker
5841 // The return from the function can be EXCEPTION_CONTINUE_SEARCH to let an
5842 // exception go unhandled. This is the default behaviour (starting in v2.0),
5843 // but can be overridden by hosts or by config file.
5844 // When the behaviour is overridden, the return will be EXCEPTION_EXECUTE_HANDLER
5845 // to swallow the exception.
5846 // Note that some exceptions are always swallowed: ThreadAbort, and AppDomainUnload.
5849 // pExceptionInfo EXCEPTION_POINTERS for current exception
5850 // _location A constant as an INT_PTR. Tells the context from whence called.
5851 // swallowing Are we swallowing unhandled exceptions based on policy?
5854 // EXCEPTION_CONTINUE_SEARCH Generally returns this to let the exception go unhandled.
5855 // EXCEPTION_EXECUTE_HANDLER May return this to swallow the exception.
5857 static LONG ThreadBaseExceptionFilter_Worker(PEXCEPTION_POINTERS pExceptionInfo,
5869 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: Enter\n"));
5871 ThreadBaseExceptionFilterParam *pParam = (ThreadBaseExceptionFilterParam *) pvParam;
5872 UnhandledExceptionLocation location = pParam->location;
5874 _ASSERTE(!g_fNoExceptions);
5876 Thread* pThread = GetThread();
5880 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException) &&
5881 !(swallowing && (SwallowUnhandledExceptions() || ExceptionIsAlwaysSwallowed(pExceptionInfo))) &&
5882 !(location == ClassInitUnhandledException && pThread->IsRudeAbortInitiated()))
5883 _ASSERTE(!"BreakOnUnCaughtException");
5886 BOOL doDefault = ((location != ClassInitUnhandledException) &&
5887 (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_BREAKPOINT) &&
5888 (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_SINGLE_STEP));
5892 // The default handling for versions v1.0 and v1.1 was to swallow unhandled exceptions.
5893 // With v2.0, the default is to let them go unhandled. Hosts & config files can modify the default
5894 // to retain the v1.1 behaviour.
5895 // Should we swallow this exception, or let it continue up and be unhandled?
5896 if (!SwallowUnhandledExceptions())
5898 // No, don't swallow unhandled exceptions...
5900 // ...except if the exception is of a type that is always swallowed (ThreadAbort, AppDomainUnload)...
5901 if (ExceptionIsAlwaysSwallowed(pExceptionInfo))
5902 { // ...return EXCEPTION_EXECUTE_HANDLER to swallow the exception anyway.
5903 return EXCEPTION_EXECUTE_HANDLER;
5907 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnUncaughtException))
5908 _ASSERTE(!"BreakOnUnCaughtException");
5911 // ...so, continue search. i.e. let the exception go unhandled.
5912 return EXCEPTION_CONTINUE_SEARCH;
5916 #ifdef DEBUGGING_SUPPORTED
5917 // If there's a debugger (and not doing a thread abort), give the debugger a shot at the exception.
5918 // If the debugger is going to try to continue the exception, it will return ContinueException (which
5919 // we see here as EXCEPTION_CONTINUE_EXECUTION).
5920 if (!pThread->IsAbortRequested())
5922 // TODO: do we really need this check? I don't think we do
5923 if(CORDebuggerAttached())
5925 if (NotifyDebuggerLastChance(pThread, pExceptionInfo, FALSE) == EXCEPTION_CONTINUE_EXECUTION)
5927 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: EXCEPTION_CONTINUE_EXECUTION\n"));
5928 return EXCEPTION_CONTINUE_EXECUTION;
5932 #endif // DEBUGGING_SUPPORTED
5934 // Do default handling, but ignore breakpoint exceptions and class init exceptions
5937 LOG((LF_EH, LL_INFO100, "ThreadBaseExceptionFilter_Worker: Calling DefaultCatchHandler\n"));
5939 BOOL useLastThrownObject = UpdateCurrentThrowable(pExceptionInfo->ExceptionRecord);
5941 DefaultCatchHandler(pExceptionInfo,
5943 useLastThrownObject,
5945 location == ManagedThread || location == ThreadPoolThread || location == FinalizerThread);
5948 // Return EXCEPTION_EXECUTE_HANDLER to swallow the exception.
5950 ? EXCEPTION_EXECUTE_HANDLER
5951 : EXCEPTION_CONTINUE_SEARCH);
5952 } // LONG ThreadBaseExceptionFilter_Worker()
5955 // This is the filter for new managed threads, for threadpool threads, and for
5956 // running finalizer methods.
5957 LONG ThreadBaseExceptionSwallowingFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam)
5959 return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/true);
5962 // This was the filter for new managed threads in v1.0 and v1.1. Now used
5963 // for delegate invoke, various things in the thread pool, and the
5964 // class init handler.
5965 LONG ThreadBaseExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pvParam)
5967 return ThreadBaseExceptionFilter_Worker(pExceptionInfo, pvParam, /*swallowing=*/false);
5971 // This is the filter that we install when transitioning an AppDomain at the base of a managed
5972 // thread. Nothing interesting will get swallowed after us. So we never decide to continue
5973 // the search. Instead, we let it go unhandled and get the Watson report and debugging
5974 // experience before the AD transition has an opportunity to catch/rethrow and lose all the
5975 // relevant information.
5976 LONG ThreadBaseExceptionAppDomainFilter(EXCEPTION_POINTERS *pExceptionInfo, PVOID pvParam)
5978 LONG ret = ThreadBaseExceptionSwallowingFilter(pExceptionInfo, pvParam);
5980 if (ret != EXCEPTION_CONTINUE_SEARCH)
5983 // Consider the exception to be unhandled
5984 return InternalUnhandledExceptionFilter_Worker(pExceptionInfo);
5987 // Filter for calls out from the 'vm' to native code, if there's a possibility of SEH exceptions
5988 // in the native code.
5989 LONG CallOutFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pv)
5991 CallOutFilterParam *pParam = static_cast<CallOutFilterParam *>(pv);
5993 _ASSERTE(pParam->OneShot && (pParam->OneShot == TRUE || pParam->OneShot == FALSE));
5995 if (pParam->OneShot == TRUE)
5997 pParam->OneShot = FALSE;
5999 // Replace whatever SEH exception is in flight, with an SEHException derived from
6000 // CLRException. But if the exception already looks like one of ours, let it
6001 // go past since LastThrownObject should already represent it.
6002 if ((!IsComPlusException(pExceptionInfo->ExceptionRecord)) &&
6003 (pExceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_MSVC))
6004 PAL_CPP_THROW(SEHException *, new SEHException(pExceptionInfo->ExceptionRecord,
6005 pExceptionInfo->ContextRecord));
6007 return EXCEPTION_CONTINUE_SEARCH;
6011 //==========================================================================
6012 // Convert the format string used by sprintf to the format used by String.Format.
6013 // Using the managed formatting routine avoids bogus access violations
6014 // that happen for long strings in Win32's FormatMessage.
6016 // Note: This is not general purpose routine. It handles only cases found
6017 // in TypeLoadException and FileLoadException.
6018 //==========================================================================
6019 static BOOL GetManagedFormatStringForResourceID(CCompRC::ResourceCategory eCategory, UINT32 resId, SString & converted)
6021 STANDARD_VM_CONTRACT;
6024 if (!temp.LoadResource(eCategory, resId))
6027 SString::Iterator itr = temp.Begin();
6035 if (fmt >= '1' && fmt <= '9') {
6036 converted.Append(W("{"));
6037 converted.Append(fmt - 1); // the managed args start at 0
6038 converted.Append(W("}"));
6042 converted.Append(W("%"));
6045 _ASSERTE(!"Unexpected formating string: %s");
6050 converted.Append(W("{{"));
6053 converted.Append(W("}}"));
6056 converted.Append(c);
6063 //==========================================================================
6064 // Private helper for TypeLoadException.
6065 //==========================================================================
6066 void QCALLTYPE GetTypeLoadExceptionMessage(UINT32 resId, QCall::StringHandleOnStack retString)
6072 StackSString format;
6073 GetManagedFormatStringForResourceID(CCompRC::Error, resId ? resId : IDS_CLASSLOAD_GENERAL, format);
6074 retString.Set(format);
6081 //==========================================================================
6082 // Private helper for FileLoadException and FileNotFoundException.
6083 //==========================================================================
6085 void QCALLTYPE GetFileLoadExceptionMessage(UINT32 hr, QCall::StringHandleOnStack retString)
6091 StackSString format;
6092 GetManagedFormatStringForResourceID(CCompRC::Error, GetResourceIDForFileLoadExceptionHR(hr), format);
6093 retString.Set(format);
6098 //==========================================================================
6099 // Private helper for FileLoadException and FileNotFoundException.
6100 //==========================================================================
6101 void QCALLTYPE FileLoadException_GetMessageForHR(UINT32 hresult, QCall::StringHandleOnStack retString)
6107 BOOL bNoGeekStuff = FALSE;
6108 switch ((HRESULT)hresult)
6110 // These are not usually app errors - as long
6111 // as the message is reasonably clear, we can live without the hex code stuff.
6112 case COR_E_FILENOTFOUND:
6113 case __HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND):
6114 case __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND):
6115 case __HRESULT_FROM_WIN32(ERROR_INVALID_NAME):
6116 case __HRESULT_FROM_WIN32(ERROR_BAD_NET_NAME):
6117 case __HRESULT_FROM_WIN32(ERROR_BAD_NETPATH):
6118 case __HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND):
6119 case CTL_E_FILENOTFOUND:
6120 case COR_E_DLLNOTFOUND:
6121 case COR_E_PATHTOOLONG:
6122 case E_ACCESSDENIED:
6123 case COR_E_BADIMAGEFORMAT:
6124 case COR_E_NEWER_RUNTIME:
6125 case COR_E_ASSEMBLYEXPECTED:
6126 bNoGeekStuff = TRUE;
6131 GetHRMsg((HRESULT)hresult, s, bNoGeekStuff);
6138 #define ValidateSigBytes(_size) do { if ((_size) > csig) COMPlusThrow(kArgumentException, W("Argument_BadSigFormat")); csig -= (_size); } while (false)
6140 //==========================================================================
6141 // Unparses an individual type.
6142 //==========================================================================
6143 const BYTE *UnparseType(const BYTE *pType, DWORD& csig, StubLinker *psl)
6150 INJECT_FAULT(ThrowOutOfMemory();); // Emitting data to the StubLinker can throw OOM.
6154 LPCUTF8 pName = NULL;
6156 ValidateSigBytes(sizeof(BYTE));
6157 switch ( (CorElementType) *(pType++) ) {
6158 case ELEMENT_TYPE_VOID:
6159 psl->EmitUtf8("void");
6162 case ELEMENT_TYPE_BOOLEAN:
6163 psl->EmitUtf8("boolean");
6166 case ELEMENT_TYPE_CHAR:
6167 psl->EmitUtf8("char");
6170 case ELEMENT_TYPE_U1:
6171 psl->EmitUtf8("unsigned ");
6173 case ELEMENT_TYPE_I1:
6174 psl->EmitUtf8("byte");
6177 case ELEMENT_TYPE_U2:
6178 psl->EmitUtf8("unsigned ");
6180 case ELEMENT_TYPE_I2:
6181 psl->EmitUtf8("short");
6184 case ELEMENT_TYPE_U4:
6185 psl->EmitUtf8("unsigned ");
6187 case ELEMENT_TYPE_I4:
6188 psl->EmitUtf8("int");
6191 case ELEMENT_TYPE_I:
6192 psl->EmitUtf8("native int");
6194 case ELEMENT_TYPE_U:
6195 psl->EmitUtf8("native unsigned");
6198 case ELEMENT_TYPE_U8:
6199 psl->EmitUtf8("unsigned ");
6201 case ELEMENT_TYPE_I8:
6202 psl->EmitUtf8("long");
6206 case ELEMENT_TYPE_R4:
6207 psl->EmitUtf8("float");
6210 case ELEMENT_TYPE_R8:
6211 psl->EmitUtf8("double");
6214 case ELEMENT_TYPE_STRING:
6215 psl->EmitUtf8(g_StringName);
6218 case ELEMENT_TYPE_VAR:
6219 case ELEMENT_TYPE_OBJECT:
6220 psl->EmitUtf8(g_ObjectName);
6223 case ELEMENT_TYPE_PTR:
6224 pType = UnparseType(pType, csig, psl);
6228 case ELEMENT_TYPE_BYREF:
6229 pType = UnparseType(pType, csig, psl);
6233 case ELEMENT_TYPE_VALUETYPE:
6234 case ELEMENT_TYPE_CLASS:
6235 pName = (LPCUTF8)pType;
6237 ValidateSigBytes(sizeof(CHAR));
6238 if (*(pType++) == '\0')
6241 psl->EmitUtf8(pName);
6244 case ELEMENT_TYPE_SZARRAY:
6246 pType = UnparseType(pType, csig, psl);
6247 psl->EmitUtf8("[]");
6251 case ELEMENT_TYPE_ARRAY:
6253 pType = UnparseType(pType, csig, psl);
6254 ValidateSigBytes(sizeof(DWORD));
6255 DWORD rank = GET_UNALIGNED_VAL32(pType);
6256 pType += sizeof(DWORD);
6259 ValidateSigBytes(sizeof(UINT32));
6260 UINT32 nsizes = GET_UNALIGNED_VAL32(pType); // Get # of sizes
6261 ValidateSigBytes(nsizes * sizeof(UINT32));
6262 pType += 4 + nsizes*4;
6263 ValidateSigBytes(sizeof(UINT32));
6264 UINT32 nlbounds = GET_UNALIGNED_VAL32(pType); // Get # of lower bounds
6265 ValidateSigBytes(nlbounds * sizeof(UINT32));
6266 pType += 4 + nlbounds*4;
6270 psl->EmitUtf8("[]");
6278 case ELEMENT_TYPE_TYPEDBYREF:
6282 case ELEMENT_TYPE_FNPTR:
6283 psl->EmitUtf8("ftnptr");
6296 //==========================================================================
6297 // Helper for MissingMemberException.
6298 //==========================================================================
6299 static STRINGREF MissingMemberException_FormatSignature_Internal(I1ARRAYREF* ppPersistedSig)
6306 INJECT_FAULT(ThrowOutOfMemory(););
6310 STRINGREF pString = NULL;
6313 const BYTE *psig = 0;
6314 StubLinker *psl = NULL;
6315 StubHolder<Stub> pstub;
6317 if ((*ppPersistedSig) != NULL)
6318 csig = (*ppPersistedSig)->GetNumComponents();
6322 return StringObject::NewString("Unknown signature");
6325 psig = (const BYTE*)_alloca(csig);
6326 CopyMemory((BYTE*)psig,
6327 (*ppPersistedSig)->GetDirectPointerToNonObjectElements(),
6337 ValidateSigBytes(sizeof(UINT32));
6338 UINT32 cconv = GET_UNALIGNED_VAL32(psig);
6341 if (cconv == IMAGE_CEE_CS_CALLCONV_FIELD) {
6342 psig = UnparseType(psig, csig, psl);
6344 ValidateSigBytes(sizeof(UINT32));
6345 UINT32 nargs = GET_UNALIGNED_VAL32(psig);
6348 // Unparse return type
6349 psig = UnparseType(psig, csig, psl);
6352 psig = UnparseType(psig, csig, psl);
6354 psl->EmitUtf8(", ");
6360 pstub = psl->Link();
6363 pString = StringObject::NewString( (LPCUTF8)(pstub->GetEntryPoint()) );
6367 FCIMPL1(Object*, MissingMemberException_FormatSignature, I1Array* pPersistedSigUNSAFE)
6371 STRINGREF pString = NULL;
6372 I1ARRAYREF pPersistedSig = (I1ARRAYREF) pPersistedSigUNSAFE;
6373 HELPER_METHOD_FRAME_BEGIN_RET_1(pPersistedSig);
6375 pString = MissingMemberException_FormatSignature_Internal(&pPersistedSig);
6377 HELPER_METHOD_FRAME_END();
6378 return OBJECTREFToObject(pString);
6382 // Check if the Win32 Error code is an IO error.
6383 BOOL IsWin32IOError(SCODE scode)
6385 LIMITED_METHOD_CONTRACT;
6389 case ERROR_FILE_NOT_FOUND:
6390 case ERROR_PATH_NOT_FOUND:
6391 case ERROR_TOO_MANY_OPEN_FILES:
6392 case ERROR_ACCESS_DENIED:
6393 case ERROR_INVALID_HANDLE:
6394 case ERROR_INVALID_DRIVE:
6395 case ERROR_WRITE_PROTECT:
6396 case ERROR_NOT_READY:
6397 case ERROR_WRITE_FAULT:
6398 case ERROR_SHARING_VIOLATION:
6399 case ERROR_LOCK_VIOLATION:
6400 case ERROR_SHARING_BUFFER_EXCEEDED:
6401 case ERROR_HANDLE_DISK_FULL:
6402 case ERROR_BAD_NETPATH:
6403 case ERROR_DEV_NOT_EXIST:
6404 case ERROR_FILE_EXISTS:
6405 case ERROR_CANNOT_MAKE:
6406 case ERROR_NET_WRITE_FAULT:
6407 case ERROR_DRIVE_LOCKED:
6408 case ERROR_OPEN_FAILED:
6409 case ERROR_BUFFER_OVERFLOW:
6410 case ERROR_DISK_FULL:
6411 case ERROR_INVALID_NAME:
6412 case ERROR_FILENAME_EXCED_RANGE:
6413 case ERROR_IO_DEVICE:
6414 case ERROR_DISK_OPERATION_FAILED:
6423 // Check if there is a pending exception or the thread is already aborting. Returns 0 if yes.
6424 // Otherwise, sets the thread up for generating an abort and returns address of ThrowControlForThread
6425 // It is the caller's responsibility to set up Thread::m_OSContext prior to this call. This is used as
6426 // the context for checking if a ThreadAbort is allowed, and also as the context for the ThreadAbortException
6428 LPVOID COMPlusCheckForAbort(UINT_PTR uTryCatchResumeAddress)
6439 // Initialize the return address
6440 LPVOID pRetAddress = 0;
6442 Thread* pThread = GetThread();
6444 if ((!pThread->IsAbortRequested()) || // if no abort has been requested
6445 (!pThread->IsRudeAbort() &&
6446 (pThread->GetThrowable() != NULL)) ) // or if there is a pending exception
6451 // Reverse COM interop IL stubs map all exceptions to HRESULTs and must not propagate Thread.Abort
6452 // to their unmanaged callers.
6453 if (uTryCatchResumeAddress != NULL)
6455 MethodDesc * pMDResumeMethod = ExecutionManager::GetCodeMethodDesc((PCODE)uTryCatchResumeAddress);
6456 if (pMDResumeMethod->IsILStub())
6460 // else we must produce an abort
6461 if ((pThread->GetThrowable() == NULL) &&
6462 (pThread->IsAbortInitiated()))
6464 // Oops, we just swallowed an abort, must restart the process
6465 pThread->ResetAbortInitiated();
6468 // Question: Should we also check for (pThread->m_PreventAsync == 0)
6470 #if !defined(WIN64EXCEPTIONS) && defined(FEATURE_STACK_PROBE)
6471 // On Win64, this function is called by our exception handling code which has probed.
6472 // But on X86, this is called from JIT code directly. We probe here so that
6473 // we can restore the state of the thread below.
6474 if (GetEEPolicy()->GetActionOnFailure(FAIL_StackOverflow) == eRudeUnloadAppDomain)
6476 // In case of SO, we will skip the managed code.
6477 CONTRACT_VIOLATION(ThrowsViolation);
6478 RetailStackProbe(ADJUST_PROBE(DEFAULT_ENTRY_PROBE_AMOUNT), pThread);
6480 #endif // !WIN64EXCEPTIONS && FEATURE_STACK_PROBE
6482 pThread->SetThrowControlForThread(Thread::InducedThreadRedirectAtEndOfCatch);
6483 if (!pThread->ReadyForAbort())
6485 pThread->ResetThrowControlForThread();
6488 pThread->SetThrowControlForThread(Thread::InducedThreadStop);
6490 pRetAddress = (LPVOID)THROW_CONTROL_FOR_THREAD_FUNCTION;
6496 // Only proceed if Watson is enabled - CoreCLR may have it disabled.
6497 if (IsWatsonEnabled())
6499 BOOL fClearUEWatsonBucketTracker = TRUE;
6500 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
6502 if (pRetAddress && pThread->IsAbortRequested())
6504 // Since we are going to reraise the thread abort exception, we would like to assert that
6505 // the buckets present in the UE tracker are the ones which were setup TAE was first raised.
6507 // However, these buckets could come from across AD transition as well and thus, would be
6508 // marked for "Captured at AD transition". Thus, we cannot just assert them to be only from
6511 // We try to preserve buckets incase there is another catch that may catch the exception we reraise
6512 // and it attempts to FailFast using the TA exception object. In such a case,
6513 // we should maintain the original exception point's bucket details.
6514 if (pUEWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL)
6516 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
6517 fClearUEWatsonBucketTracker = FALSE;
6522 // If we are here and UE Watson bucket tracker is empty,
6523 // then it is possible that a thread abort was signalled when the catch was executing
6524 // and thus, hijack for TA from here is not a reraise but an initial raise.
6526 // However, if we have partial details, then something is really not right.
6527 if (!((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) &&
6528 (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)))
6530 _ASSERTE(!"How come TA is being [re]raised and we have incomplete watson bucket details?");
6536 if (fClearUEWatsonBucketTracker)
6538 // Clear the UE watson bucket tracker for future use since it does not have anything
6539 // useful for us right now.
6540 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
6541 LOG((LF_EH, LL_INFO100, "COMPlusCheckForAbort - Cleared UE watson bucket tracker since TAE was not being reraised.\n"));
6545 #endif // !FEATURE_PAL
6551 BOOL IsThreadHijackedForThreadStop(Thread* pThread, EXCEPTION_RECORD* pExceptionRecord)
6563 if (IsComPlusException(pExceptionRecord))
6565 if (pThread->ThrewControlForThread() == Thread::InducedThreadStop)
6567 LOG((LF_EH, LL_INFO100, "Asynchronous Thread Stop or Abort\n"));
6571 else if (IsStackOverflowException(pThread, pExceptionRecord))
6573 // SO happens before we are able to change the state to InducedThreadStop, but
6574 // we are still in our hijack routine.
6575 if (pThread->ThrewControlForThread() == Thread::InducedThreadRedirect)
6577 LOG((LF_EH, LL_INFO100, "Asynchronous Thread Stop or Abort caused by SO\n"));
6584 // We sometimes move a thread's execution so it will throw an exception for us.
6585 // But then we have to treat the exception as if it came from the instruction
6586 // the thread was originally running.
6588 // NOTE: This code depends on the fact that there are no register-based data dependencies
6589 // between a try block and a catch, fault, or finally block. If there were, then we need
6590 // to preserve more of the register context.
6592 void AdjustContextForThreadStop(Thread* pThread,
6605 _ASSERTE(pThread->m_OSContext);
6607 #ifndef WIN64EXCEPTIONS
6608 SetIP(pContext, GetIP(pThread->m_OSContext));
6609 SetSP(pContext, (GetSP(pThread->m_OSContext)));
6611 if (GetFP(pThread->m_OSContext) != 0) // ebp = 0 implies that we got here with the right values for ebp
6613 SetFP(pContext, GetFP(pThread->m_OSContext));
6616 // We might have been interrupted execution at a point where the jit has roots in
6617 // registers. We just need to store a "safe" value in here so that the collector
6618 // doesn't trap. We're not going to use these objects after the exception.
6620 // Only callee saved registers are going to be reported by the faulting excepiton frame.
6621 #if defined(_TARGET_X86_)
6622 // Ebx,esi,edi are important. Eax,ecx,edx are not.
6627 PORTABILITY_ASSERT("AdjustContextForThreadStop");
6630 #else // !WIN64EXCEPTIONS
6631 CopyOSContext(pContext, pThread->m_OSContext);
6632 #if defined(_TARGET_ARM_) && defined(_DEBUG)
6633 // Make sure that the thumb bit is set on the IP of the original abort context we just restored.
6634 PCODE controlPC = GetIP(pContext);
6635 _ASSERTE(controlPC & THUMB_CODE);
6636 #endif // _TARGET_ARM_
6637 #endif // !WIN64EXCEPTIONS
6639 pThread->ResetThrowControlForThread();
6641 // Should never get here if we're already throwing an exception.
6642 _ASSERTE(!pThread->IsExceptionInProgress() || pThread->IsRudeAbort());
6644 // Should never get here if we're already abort initiated.
6645 _ASSERTE(!pThread->IsAbortInitiated() || pThread->IsRudeAbort());
6647 if (pThread->IsAbortRequested())
6649 pThread->SetAbortInitiated(); // to prevent duplicate aborts
6653 // Create a COM+ exception , stick it in the thread.
6655 CreateCOMPlusExceptionObject(Thread *pThread, EXCEPTION_RECORD *pExceptionRecord, BOOL bAsynchronousThreadStop)
6667 _ASSERTE(GetThread() == pThread);
6669 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
6671 OBJECTREF result = 0;
6673 DWORD COMPlusExceptionCode = (bAsynchronousThreadStop
6674 ? kThreadAbortException
6675 : MapWin32FaultToCOMPlusException(pExceptionRecord));
6677 if (exceptionCode == STATUS_NO_MEMORY)
6679 result = CLRException::GetBestOutOfMemoryException();
6681 else if (IsStackOverflowException(pThread, pExceptionRecord))
6683 result = CLRException::GetPreallocatedStackOverflowException();
6685 else if (bAsynchronousThreadStop && pThread->IsAbortRequested() && pThread->IsRudeAbort())
6687 result = CLRException::GetPreallocatedRudeThreadAbortException();
6693 // We need to disable the backout stack validation at this point since CreateThrowable can
6694 // take arbitrarily large amounts of stack for different exception types; however we know
6695 // for a fact that we will never go through this code path if the exception is a stack
6696 // overflow exception since we already handled that case above with the pre-allocated SO exception.
6697 DISABLE_BACKOUT_STACK_VALIDATION;
6701 ThreadPreventAsyncHolder preventAsync;
6702 ResetProcessorStateHolder procState;
6704 INSTALL_UNWIND_AND_CONTINUE_HANDLER;
6706 GCPROTECT_BEGIN(result)
6708 EEException e((RuntimeExceptionKind)COMPlusExceptionCode);
6709 result = e.CreateThrowable();
6711 // EEException is "one size fits all". But AV needs some more information.
6712 if (COMPlusExceptionCode == kAccessViolationException)
6714 SetExceptionAVParameters(result, pExceptionRecord);
6719 UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
6723 // If we get an exception trying to build the managed exception object, then go ahead and return the
6724 // thrown object as the result of this function. This is preferable to letting the exception try to
6725 // percolate up through the EH code, and it effectively replaces the thrown exception with this
6727 result = GET_THROWABLE();
6729 EX_END_CATCH(SwallowAllExceptions);
6735 LONG FilterAccessViolation(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
6746 if (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
6747 return EXCEPTION_EXECUTE_HANDLER;
6749 return EXCEPTION_CONTINUE_SEARCH;
6753 * IsContinuableException
6755 * Returns whether this is an exception the EE knows how to intercept and continue from.
6758 * pThread - The thread the exception occurred on.
6761 * TRUE if the exception on the thread is interceptable or not.
6764 * Conditions for an interceptable exception:
6765 * 1) must be on a managed thread
6766 * 2) an exception must be in progress
6767 * 3) a managed exception object must have been created
6768 * 4) the thread must not be aborting
6769 * 5) the exception must not be a breakpoint, a single step, or a stack overflow
6770 * 6) the exception dispatch must be in the first pass
6771 * 7) the exception must not be a fatal error, as determined by the EE policy (see LogFatalError())
6773 bool IsInterceptableException(Thread *pThread)
6783 return ((pThread != NULL) &&
6784 (!pThread->IsAbortRequested()) &&
6785 (pThread->IsExceptionInProgress()) &&
6786 (!pThread->IsThrowableNull())
6788 #ifdef DEBUGGING_SUPPORTED
6790 pThread->GetExceptionState()->IsDebuggerInterceptable()
6796 // Determines whether we hit an DO_A_GC_HERE marker in JITted code, and returns the
6797 // appropriate exception code, or zero if the code is not a GC marker.
6798 DWORD GetGcMarkerExceptionCode(LPVOID ip)
6800 #if defined(HAVE_GCCOVER)
6801 WRAPPER_NO_CONTRACT;
6803 if (GCStress<cfg_any>::IsEnabled() && IsGcCoverageInterrupt(ip))
6805 return STATUS_CLR_GCCOVER_CODE;
6807 #else // defined(HAVE_GCCOVER)
6808 LIMITED_METHOD_CONTRACT;
6809 #endif // defined(HAVE_GCCOVER)
6813 // Did we hit an DO_A_GC_HERE marker in JITted code?
6814 bool IsGcMarker(DWORD exceptionCode, CONTEXT *pContext)
6817 WRAPPER_NO_CONTRACT;
6819 if (GCStress<cfg_any>::IsEnabled())
6822 // on x86 we can't suspend EE to update the GC marker instruction so
6823 // we update it directly without suspending. this can sometimes yield
6824 // a STATUS_ACCESS_VIOLATION instead of STATUS_CLR_GCCOVER_CODE. in
6825 // this case we let the AV through and retry the instruction. we'll
6826 // track the IP of the instruction that generated an AV so we don't
6827 // mix up a real AV with a "fake" AV.
6828 // see comments in function DoGcStress for more details on this race.
6829 // also make sure that the thread is actually in managed code since AVs
6830 // outside of of JIT code will never be potential GC markers
6831 Thread* pThread = GetThread();
6832 if (exceptionCode == STATUS_ACCESS_VIOLATION &&
6833 GCStress<cfg_instr>::IsEnabled() &&
6834 pThread->GetLastAVAddress() != (LPVOID)GetIP(pContext) &&
6835 pThread->PreemptiveGCDisabled() &&
6836 !IsIPInEE((LPVOID)GetIP(pContext)))
6838 pThread->SetLastAVAddress((LPVOID)GetIP(pContext));
6841 #endif // _TARGET_X86_
6843 if (exceptionCode == STATUS_CLR_GCCOVER_CODE)
6845 if (OnGcCoverageInterrupt(pContext))
6851 // ExecutionManager::IsManagedCode takes a spinlock. Since this is in a debug-only
6852 // check, we'll allow the lock.
6853 CONTRACT_VIOLATION(TakesLockViolation);
6855 // Should never be in managed code.
6856 CONSISTENCY_CHECK_MSG(!ExecutionManager::IsManagedCode(GetIP(pContext)), "hit privileged instruction!");
6861 LIMITED_METHOD_CONTRACT;
6862 #endif // HAVE_GCCOVER
6868 // Return true if the access violation is well formed (has two info parameters
6871 IsWellFormedAV(EXCEPTION_RECORD *pExceptionRecord)
6873 LIMITED_METHOD_CONTRACT;
6875 #define NUM_AV_PARAMS 2
6877 if (pExceptionRecord->NumberParameters == NUM_AV_PARAMS)
6888 IsDebuggerFault(EXCEPTION_RECORD *pExceptionRecord,
6890 DWORD exceptionCode,
6893 LIMITED_METHOD_CONTRACT;
6895 #ifdef DEBUGGING_SUPPORTED
6896 SO_NOT_MAINLINE_FUNCTION;
6899 // On ARM we don't have any reliable hardware support for single stepping so it is emulated in software.
6900 // The implementation will end up throwing an EXCEPTION_BREAKPOINT rather than an EXCEPTION_SINGLE_STEP
6901 // and leaves other aspects of the thread context in an invalid state. Therefore we use this opportunity
6902 // to fixup the state before any other part of the system uses it (we do it here since only the debugger
6903 // uses single step functionality).
6905 // First ask the emulation itself whether this exception occurred while single stepping was enabled. If so
6906 // it will fix up the context to be consistent again and return true. If so and the exception was
6907 // EXCEPTION_BREAKPOINT then we translate it to EXCEPTION_SINGLE_STEP (otherwise we leave it be, e.g. the
6908 // instruction stepped caused an access violation). since this is called from our VEH there might not
6909 // be a thread object so we must check pThread first.
6910 if ((pThread != NULL) && pThread->HandleSingleStep(pContext, exceptionCode) && (exceptionCode == EXCEPTION_BREAKPOINT))
6912 exceptionCode = EXCEPTION_SINGLE_STEP;
6913 pExceptionRecord->ExceptionCode = EXCEPTION_SINGLE_STEP;
6914 pExceptionRecord->ExceptionAddress = (PVOID)pContext->Pc;
6916 #endif // _TARGET_ARM_
6918 // Is this exception really meant for the COM+ Debugger? Note: we will let the debugger have a chance if there
6919 // is a debugger attached to any part of the process. It is incorrect to consider whether or not the debugger
6920 // is attached the the thread's current app domain at this point.
6922 // Even if a debugger is not attached, we must let the debugger handle the exception in case it's coming from a
6924 if ((!IsComPlusException(pExceptionRecord)) &&
6925 (GetThread() != NULL) &&
6926 (g_pDebugInterface != NULL) &&
6927 g_pDebugInterface->FirstChanceNativeException(pExceptionRecord,
6932 LOG((LF_EH | LF_CORDB, LL_INFO1000, "IsDebuggerFault - it's the debugger's fault\n"));
6935 #endif // DEBUGGING_SUPPORTED
6939 #endif // FEATURE_PAL
6941 #ifdef WIN64EXCEPTIONS
6943 #ifndef _TARGET_X86_
6944 EXTERN_C void JIT_MemSet_End();
6945 EXTERN_C void JIT_MemCpy_End();
6947 EXTERN_C void JIT_WriteBarrier_End();
6948 EXTERN_C void JIT_CheckedWriteBarrier_End();
6949 #endif // _TARGET_X86_
6951 #if defined(_TARGET_AMD64_) && defined(_DEBUG)
6952 EXTERN_C void JIT_WriteBarrier_Debug();
6953 EXTERN_C void JIT_WriteBarrier_Debug_End();
6957 EXTERN_C void FCallMemcpy_End();
6960 // Check if the passed in instruction pointer is in one of the
6961 // JIT helper functions.
6962 bool IsIPInMarkedJitHelper(UINT_PTR uControlPc)
6964 LIMITED_METHOD_CONTRACT;
6966 #define CHECK_RANGE(name) \
6967 if (GetEEFuncEntryPoint(name) <= uControlPc && uControlPc < GetEEFuncEntryPoint(name##_End)) return true;
6969 #ifndef _TARGET_X86_
6970 CHECK_RANGE(JIT_MemSet)
6971 CHECK_RANGE(JIT_MemCpy)
6973 CHECK_RANGE(JIT_WriteBarrier)
6974 CHECK_RANGE(JIT_CheckedWriteBarrier)
6977 CHECK_RANGE(JIT_WriteBarrierGroup)
6978 CHECK_RANGE(JIT_PatchedWriteBarrierGroup)
6979 #endif // FEATURE_PAL
6980 #endif // _TARGET_X86_
6982 #if defined(_TARGET_AMD64_) && defined(_DEBUG)
6983 CHECK_RANGE(JIT_WriteBarrier_Debug)
6987 CHECK_RANGE(FCallMemcpy)
6992 #endif // WIN64EXCEPTIONS
6994 // Returns TRUE if caller should resume execution.
6996 AdjustContextForWriteBarrier(
6997 EXCEPTION_RECORD *pExceptionRecord,
7000 WRAPPER_NO_CONTRACT;
7002 #if defined(_TARGET_X86_) && !defined(PLATFORM_UNIX)
7003 void* f_IP = (void *)GetIP(pContext);
7005 if (((f_IP >= (void *) JIT_WriteBarrierGroup) && (f_IP <= (void *) JIT_WriteBarrierGroup_End)) ||
7006 ((f_IP >= (void *) JIT_PatchedWriteBarrierGroup) && (f_IP <= (void *) JIT_PatchedWriteBarrierGroup_End)))
7008 // set the exception IP to be the instruction that called the write barrier
7009 void* callsite = (void *)GetAdjustedCallAddress(*dac_cast<PTR_PCODE>(GetSP(pContext)));
7010 pExceptionRecord->ExceptionAddress = callsite;
7011 SetIP(pContext, (PCODE)callsite);
7013 // put ESP back to what it was before the call.
7014 SetSP(pContext, PCODE((BYTE*)GetSP(pContext) + sizeof(void*)));
7017 #elif defined(WIN64EXCEPTIONS) // _TARGET_X86_ && !PLATFORM_UNIX
7018 void* f_IP = dac_cast<PTR_VOID>(GetIP(pContext));
7020 CONTEXT tempContext;
7021 CONTEXT* pExceptionContext = pContext;
7023 BOOL fExcluded = IsIPInMarkedJitHelper((UINT_PTR)f_IP);
7027 bool fShouldHandleManagedFault = false;
7029 if (pContext != &tempContext)
7031 tempContext = *pContext;
7032 pContext = &tempContext;
7035 Thread::VirtualUnwindToFirstManagedCallFrame(pContext);
7037 #if defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
7038 // We had an AV in the writebarrier that needs to be treated
7039 // as originating in managed code. At this point, the stack (growing
7040 // from left->right) looks like this:
7042 // ManagedFunc -> Native_WriteBarrierInVM -> AV
7044 // We just performed an unwind from the write-barrier
7045 // and now have the context in ManagedFunc. Since
7046 // ManagedFunc called into the write-barrier, the return
7047 // address in the unwound context corresponds to the
7048 // instruction where the call will return.
7050 // On ARM, just like we perform ControlPC adjustment
7051 // during exception dispatch (refer to ExceptionTracker::InitializeCrawlFrame),
7052 // we will need to perform the corresponding adjustment of IP
7053 // we got from unwind above, so as to indicate that the AV
7054 // happened "before" the call to the writebarrier and not at
7055 // the instruction at which the control will return.
7056 PCODE ControlPCPostAdjustment = GetIP(pContext) - STACKWALK_CONTROLPC_ADJUST_OFFSET;
7058 // Now we save the address back into the context so that it gets used
7059 // as the faulting address.
7060 SetIP(pContext, ControlPCPostAdjustment);
7061 #endif // _TARGET_ARM_ || _TARGET_ARM64_
7063 // Unwind the frame chain - On Win64, this is required since we may handle the managed fault and to do so,
7064 // we will replace the exception context with the managed context and "continue execution" there. Thus, we do not
7065 // want any explicit frames active below the resumption SP.
7067 // Question: Why do we unwind before determining whether we will handle the exception or not?
7068 UnwindFrameChain(GetThread(), (Frame*)GetSP(pContext));
7069 fShouldHandleManagedFault = ShouldHandleManagedFault(pExceptionRecord,pContext,
7070 NULL, // establisher frame (x86 only)
7071 NULL // pThread (x86 only)
7074 if (fShouldHandleManagedFault)
7076 ReplaceExceptionContextRecord(pExceptionContext, pContext);
7077 pExceptionRecord->ExceptionAddress = dac_cast<PTR_VOID>(GetIP(pContext));
7083 #else // WIN64EXCEPTIONS
7084 PORTABILITY_ASSERT("AdjustContextForWriteBarrier");
7089 #if defined(USE_FEF) && !defined(FEATURE_PAL)
7091 struct SavedExceptionInfo
7093 EXCEPTION_RECORD m_ExceptionRecord;
7094 CONTEXT m_ExceptionContext;
7097 void SaveExceptionRecord(EXCEPTION_RECORD *pExceptionRecord)
7099 LIMITED_METHOD_CONTRACT;
7100 size_t erSize = offsetof(EXCEPTION_RECORD, ExceptionInformation) +
7101 pExceptionRecord->NumberParameters * sizeof(pExceptionRecord->ExceptionInformation[0]);
7102 memcpy(&m_ExceptionRecord, pExceptionRecord, erSize);
7106 void SaveContext(CONTEXT *pContext)
7108 LIMITED_METHOD_CONTRACT;
7109 #ifdef CONTEXT_EXTENDED_REGISTERS
7111 size_t contextSize = offsetof(CONTEXT, ExtendedRegisters);
7112 if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
7113 contextSize += sizeof(pContext->ExtendedRegisters);
7114 memcpy(&m_ExceptionContext, pContext, contextSize);
7116 #else // !CONTEXT_EXTENDED_REGISTERS
7118 size_t contextSize = sizeof(CONTEXT);
7119 memcpy(&m_ExceptionContext, pContext, contextSize);
7121 #endif // !CONTEXT_EXTENDED_REGISTERS
7124 DEBUG_NOINLINE void Enter()
7126 WRAPPER_NO_CONTRACT;
7127 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
7131 DEBUG_NOINLINE void Leave()
7133 WRAPPER_NO_CONTRACT;
7134 ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
7140 WRAPPER_NO_CONTRACT;
7141 m_Crst.Init(CrstSavedExceptionInfo, CRST_UNSAFE_ANYMODE);
7145 SavedExceptionInfo g_SavedExceptionInfo; // Globals are guaranteed zero-init;
7147 void InitSavedExceptionInfo()
7149 g_SavedExceptionInfo.Init();
7152 EXTERN_C VOID FixContextForFaultingExceptionFrame (
7153 EXCEPTION_RECORD* pExceptionRecord,
7154 CONTEXT *pContextRecord)
7156 WRAPPER_NO_CONTRACT;
7158 // don't copy parm args as have already supplied them on the throw
7159 memcpy((void*) pExceptionRecord,
7160 (void*) &g_SavedExceptionInfo.m_ExceptionRecord,
7161 offsetof(EXCEPTION_RECORD, ExceptionInformation)
7164 ReplaceExceptionContextRecord(pContextRecord, &g_SavedExceptionInfo.m_ExceptionContext);
7166 g_SavedExceptionInfo.Leave();
7168 GetThread()->ResetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
7171 EXTERN_C VOID __fastcall
7172 LinkFrameAndThrow(FaultingExceptionFrame* pFrame)
7174 WRAPPER_NO_CONTRACT;
7176 *(TADDR*)pFrame = FaultingExceptionFrame::GetMethodFrameVPtr();
7177 *pFrame->GetGSCookiePtr() = GetProcessGSCookie();
7179 pFrame->InitAndLink(&g_SavedExceptionInfo.m_ExceptionContext);
7181 GetThread()->SetThreadStateNC(Thread::TSNC_DebuggerIsManagedException);
7183 ULONG argcount = g_SavedExceptionInfo.m_ExceptionRecord.NumberParameters;
7184 ULONG flags = g_SavedExceptionInfo.m_ExceptionRecord.ExceptionFlags;
7185 ULONG code = g_SavedExceptionInfo.m_ExceptionRecord.ExceptionCode;
7186 ULONG_PTR* args = &g_SavedExceptionInfo.m_ExceptionRecord.ExceptionInformation[0];
7188 RaiseException(code, flags, argcount, args);
7191 void SetNakedThrowHelperArgRegistersInContext(CONTEXT* pContext)
7193 #if defined(_TARGET_AMD64_)
7194 pContext->Rcx = (UINT_PTR)GetIP(pContext);
7195 #elif defined(_TARGET_ARM_) || defined(_TARGET_ARM64_)
7196 // Save the original IP in LR
7197 pContext->Lr = (DWORD)GetIP(pContext);
7199 PORTABILITY_WARNING("NakedThrowHelper argument not defined");
7203 EXTERN_C VOID STDCALL NakedThrowHelper(VOID);
7205 void HandleManagedFault(EXCEPTION_RECORD* pExceptionRecord,
7207 EXCEPTION_REGISTRATION_RECORD* pEstablisherFrame,
7210 WRAPPER_NO_CONTRACT;
7212 // Ok. Now we have a brand new fault in jitted code.
7213 g_SavedExceptionInfo.Enter();
7214 g_SavedExceptionInfo.SaveExceptionRecord(pExceptionRecord);
7215 g_SavedExceptionInfo.SaveContext(pContext);
7217 SetNakedThrowHelperArgRegistersInContext(pContext);
7219 SetIP(pContext, GetEEFuncEntryPoint(NakedThrowHelper));
7222 #else // USE_FEF && !FEATURE_PAL
7224 void InitSavedExceptionInfo()
7228 #endif // USE_FEF && !FEATURE_PAL
7233 void FaultingExceptionFrame::Init(CONTEXT *pContext)
7235 WRAPPER_NO_CONTRACT;
7236 #ifndef WIN64EXCEPTIONS
7238 CalleeSavedRegisters *pRegs = GetCalleeSavedRegisters();
7239 #define CALLEE_SAVED_REGISTER(regname) pRegs->regname = pContext->regname;
7240 ENUM_CALLEE_SAVED_REGISTERS();
7241 #undef CALLEE_SAVED_REGISTER
7242 m_ReturnAddress = ::GetIP(pContext);
7243 m_Esp = (DWORD)GetSP(pContext);
7244 #else // _TARGET_X86_
7245 PORTABILITY_ASSERT("FaultingExceptionFrame::Init");
7246 #endif // _TARGET_???_ (ELSE)
7247 #else // !WIN64EXCEPTIONS
7248 m_ReturnAddress = ::GetIP(pContext);
7249 CopyOSContext(&m_ctx, pContext);
7250 #endif // !WIN64EXCEPTIONS
7254 // Init and Link in a new frame
7256 void FaultingExceptionFrame::InitAndLink(CONTEXT *pContext)
7258 WRAPPER_NO_CONTRACT;
7266 bool ShouldHandleManagedFault(
7267 EXCEPTION_RECORD* pExceptionRecord,
7269 EXCEPTION_REGISTRATION_RECORD* pEstablisherFrame,
7281 // If we get a faulting instruction inside managed code, we're going to
7282 // 1. Allocate the correct exception object, store it in the thread.
7283 // 2. Save the EIP in the thread.
7284 // 3. Change the EIP to our throw helper
7285 // 4. Resume execution.
7287 // The helper will push a frame for us, and then throw the correct managed exception.
7289 // Is this exception really meant for the COM+ Debugger? Note: we will let the debugger have a chance if there is a
7290 // debugger attached to any part of the process. It is incorrect to consider whether or not the debugger is attached
7291 // the the thread's current app domain at this point.
7294 // A managed exception never comes from managed code, and we can ignore all breakpoint
7297 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
7298 if (IsComPlusException(pExceptionRecord)
7299 || exceptionCode == STATUS_BREAKPOINT
7300 || exceptionCode == STATUS_SINGLE_STEP)
7306 // This is a workaround, but it's debug-only as is gc stress 4.
7307 // The problem is that if we get an exception with this code that
7308 // didn't come from GCStress=4, then we won't push a FeF and will
7309 // end up with a gc hole and potential crash.
7310 if (exceptionCode == STATUS_CLR_GCCOVER_CODE)
7314 #ifndef WIN64EXCEPTIONS
7315 // If there's any frame below the ESP of the exception, then we can forget it.
7316 if (pThread->m_pFrame < dac_cast<PTR_VOID>(GetSP(pContext)))
7319 // If we're a subsequent handler forget it.
7320 EXCEPTION_REGISTRATION_RECORD* pBottomMostHandler = pThread->GetExceptionState()->m_currentExInfo.m_pBottomMostHandler;
7321 if (pBottomMostHandler != NULL && pEstablisherFrame > pBottomMostHandler)
7325 #endif // WIN64EXCEPTIONS
7328 // If it's not a fault in jitted code, forget it.
7330 // ExecutionManager::IsManagedCode takes a spinlock. Since we're in the middle of throwing,
7331 // we'll allow the lock, even if a caller didn't expect it.
7332 CONTRACT_VIOLATION(TakesLockViolation);
7334 if (!ExecutionManager::IsManagedCode(GetIP(pContext)))
7338 // caller should call HandleManagedFault and resume execution.
7344 LONG WINAPI CLRVectoredExceptionHandlerPhase2(PEXCEPTION_POINTERS pExceptionInfo);
7349 VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION,
7350 VEH_CONTINUE_EXECUTION,
7351 VEH_CONTINUE_SEARCH,
7356 VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExceptionInfo);
7358 LONG WINAPI CLRVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
7360 // It is not safe to execute code inside VM after we shutdown EE. One example is DisablePreemptiveGC
7361 // will block forever.
7362 if (g_fForbidEnterEE)
7364 return EXCEPTION_CONTINUE_SEARCH;
7368 // For images ngen'd with FEATURE_LAZY_COW_PAGES, the .data section will be read-only. Any writes to that data need to be
7369 // preceded by a call to EnsureWritablePages. This code is here to catch the ones we forget.
7371 #ifdef FEATURE_LAZY_COW_PAGES
7372 if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION &&
7373 IsWellFormedAV(pExceptionInfo->ExceptionRecord) &&
7374 pExceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1 /* means this was a failed write */)
7376 void* location = (void*)pExceptionInfo->ExceptionRecord->ExceptionInformation[1];
7378 if (IsInReadOnlyLazyCOWPage(location))
7381 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DebugAssertOnMissedCOWPage))
7382 _ASSERTE_MSG(false, "Writes to NGen'd data must be protected by EnsureWritablePages.");
7385 #pragma push_macro("VirtualQuery")
7387 MEMORY_BASIC_INFORMATION mbi;
7388 if (!::VirtualQuery(location, &mbi, sizeof(mbi)))
7390 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
7392 #pragma pop_macro("VirtualQuery")
7394 bool executable = (mbi.Protect == PAGE_EXECUTE_READ) ||
7395 (mbi.Protect == PAGE_EXECUTE_READWRITE) ||
7396 (mbi.Protect == PAGE_EXECUTE_READ) ||
7397 (mbi.Protect == PAGE_EXECUTE_WRITECOPY);
7399 if (!(executable ? EnsureWritableExecutablePagesNoThrow(location, 1) : EnsureWritablePagesNoThrow(location, 1)))
7401 // Note that this failfast is very rare. It will only be hit in the theoretical cases there is
7402 // missing EnsureWritablePages probe (there should be none when we ship), and the OS run into OOM
7403 // exactly at the point when we executed the code with the missing probe.
7404 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_OUTOFMEMORY);
7407 return EXCEPTION_CONTINUE_EXECUTION;
7410 #endif //FEATURE_LAZY_COW_PAGES
7414 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7415 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7423 // You cannot put any code in here that allocates during an out-of-memory handling.
7424 // This routine runs before *any* other handlers, including __try. Thus, if you
7425 // allocate anything in this routine then it will throw out-of-memory and end up
7428 // There are various things that allocate that you may not expect to allocate. One
7429 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7430 // not already have one allocated. Thus, if we OOM during the setting up of the
7431 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7432 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7433 // already occurred.
7439 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7441 pThread = GetThread();
7444 // Since we are in an OOM situation, we test the thread object before logging since if the
7445 // thread exists we know the log buffer has been allocated already.
7447 if (pThread != NULL)
7449 CantAllocHolder caHolder;
7450 STRESS_LOG4(LF_EH, LL_INFO100, "In CLRVectoredExceptionHandler, Exception = %x, Context = %p, IP = %p SP = %p\n",
7451 pExceptionInfo->ExceptionRecord->ExceptionCode, pExceptionInfo->ContextRecord,
7452 GetIP(pExceptionInfo->ContextRecord), GetSP(pExceptionInfo->ContextRecord));
7457 // We need to unhijack the thread here if it is not unhijacked already. On x86 systems,
7458 // we do this in Thread::StackWalkFramesEx, but on amd64 systems we have the OS walk the
7459 // stack for us. If we leave CLRVectoredExceptionHandler with a thread still hijacked,
7460 // the operating system will not be able to walk the stack and not find the handlers for
7461 // the exception. It is safe to unhijack the thread in this case for two reasons:
7462 // 1. pThread refers to *this* thread.
7463 // 2. If another thread tries to hijack this thread, it will see we are not in managed
7464 // code (and thus won't try to hijack us).
7465 #if defined(WIN64EXCEPTIONS) && defined(FEATURE_HIJACK)
7466 if (pThread != NULL)
7468 pThread->UnhijackThreadNoAlloc();
7470 #endif // defined(WIN64EXCEPTIONS) && defined(FEATURE_HIJACK)
7473 if (IsSOExceptionCode(pExceptionInfo->ExceptionRecord->ExceptionCode))
7476 // Not an Out-of-memory situation, so no need for a forbid fault region here
7478 return EXCEPTION_CONTINUE_SEARCH;
7483 #ifdef FEATURE_STACK_PROBE
7484 // See if we've got enough stack to handle this exception
7486 // There isn't much stack left to attempt to report an exception. Let's trigger a hard
7487 // SO, so we clear the guard page and give us at least another page of stack to work with.
7489 if (pThread && !pThread->IsStackSpaceAvailable(ADJUST_PROBE(1)))
7491 DontCallDirectlyForceStackOverflow();
7493 #endif // FEATURE_STACK_PROBE
7495 // We can't probe here, because we won't return from the CLRVectoredExceptionHandlerPhase2
7501 FAULT_FORBID_NO_ALLOC();
7502 CantAllocHolder caHolder;
7505 retVal = CLRVectoredExceptionHandlerPhase2(pExceptionInfo);
7508 //END_ENTRYPOINT_VOIDRET;
7511 #else // !FEATURE_PAL
7512 return CLRVectoredExceptionHandlerPhase2(pExceptionInfo);
7513 #endif // !FEATURE_PAL
7516 LONG WINAPI CLRVectoredExceptionHandlerPhase2(PEXCEPTION_POINTERS pExceptionInfo)
7519 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7520 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7527 // You cannot put any code in here that allocates during an out-of-memory handling.
7528 // This routine runs before *any* other handlers, including __try. Thus, if you
7529 // allocate anything in this routine then it will throw out-of-memory and end up
7532 // There are various things that allocate that you may not expect to allocate. One
7533 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7534 // not already have one allocated. Thus, if we OOM during the setting up of the
7535 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7536 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7537 // already occurred.
7540 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
7544 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7545 CantAllocHolder caHolder;
7547 action = CLRVectoredExceptionHandlerPhase3(pExceptionInfo);
7550 if (action == VEH_CONTINUE_EXECUTION)
7552 return EXCEPTION_CONTINUE_EXECUTION;
7555 if (action == VEH_CONTINUE_SEARCH)
7557 return EXCEPTION_CONTINUE_SEARCH;
7560 if (action == VEH_EXECUTE_HANDLER)
7562 return EXCEPTION_EXECUTE_HANDLER;
7565 #if defined(WIN64EXCEPTIONS)
7567 if (action == VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION)
7570 // If the exception context was unwound by Phase3 then
7571 // we'll jump here to save the managed context and resume execution at
7572 // NakedThrowHelper. This needs to be done outside of any holder's
7573 // scope, because HandleManagedFault may not return.
7575 HandleManagedFault(pExceptionInfo->ExceptionRecord,
7576 pExceptionInfo->ContextRecord,
7577 NULL, // establisher frame (x86 only)
7578 NULL // pThread (x86 only)
7580 return EXCEPTION_CONTINUE_EXECUTION;
7583 #endif // defined(WIN64EXCEPTIONS)
7587 // In OOM situations, this call better not fault.
7590 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7591 CantAllocHolder caHolder;
7593 // Give the debugger a chance. Note that its okay for this call to trigger a GC, since the debugger will take
7594 // special steps to make that okay.
7596 // @TODO: I'd love a way to call into the debugger with GCX_NOTRIGGER still in scope, and force them to make
7597 // the choice to break the no-trigger region after taking all necessary precautions.
7598 if (IsDebuggerFault(pExceptionRecord, pExceptionInfo->ContextRecord, pExceptionRecord->ExceptionCode, GetThread()))
7600 return EXCEPTION_CONTINUE_EXECUTION;
7605 // No reason to put a forbid fault region here as the exception code is not STATUS_NO_MEMORY.
7608 // Handle a user breakpoint. Note that its okay for the UserBreakpointFilter to trigger a GC, since we're going
7609 // to either a) terminate the process, or b) let a user attach an unmanaged debugger, and debug knowing that
7610 // managed state may be messed up.
7611 if ((pExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) ||
7612 (pExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP))
7614 // A breakpoint outside managed code and outside the runtime will have to be handled by some
7615 // other piece of code.
7617 BOOL fExternalException = FALSE;
7619 BEGIN_SO_INTOLERANT_CODE_NOPROBE;
7622 // ExecutionManager::IsManagedCode takes a spinlock. Since we're in the middle of throwing,
7623 // we'll allow the lock, even if a caller didn't expect it.
7624 CONTRACT_VIOLATION(TakesLockViolation);
7626 fExternalException = (!ExecutionManager::IsManagedCode(GetIP(pExceptionInfo->ContextRecord)) &&
7627 !IsIPInModule(g_pMSCorEE, GetIP(pExceptionInfo->ContextRecord)));
7630 END_SO_INTOLERANT_CODE_NOPROBE;
7632 if (fExternalException)
7634 // The breakpoint was not ours. Someone else can handle it. (Or if not, we'll get it again as
7635 // an unhandled exception.)
7636 return EXCEPTION_CONTINUE_SEARCH;
7639 // The breakpoint was from managed or the runtime. Handle it. Or,
7640 // this may be a Rotor build.
7641 return UserBreakpointFilter(pExceptionInfo);
7644 #if defined(WIN64EXCEPTIONS)
7645 BOOL fShouldHandleManagedFault;
7648 MAYBE_FAULT_FORBID_NO_ALLOC((pExceptionRecord->ExceptionCode == STATUS_NO_MEMORY));
7649 CantAllocHolder caHolder;
7650 fShouldHandleManagedFault = ShouldHandleManagedFault(pExceptionInfo->ExceptionRecord,
7651 pExceptionInfo->ContextRecord,
7652 NULL, // establisher frame (x86 only)
7653 NULL // pThread (x86 only)
7657 if (fShouldHandleManagedFault)
7660 // HandleManagedFault may never return, so we cannot use a forbid fault region around it.
7662 HandleManagedFault(pExceptionInfo->ExceptionRecord,
7663 pExceptionInfo->ContextRecord,
7664 NULL, // establisher frame (x86 only)
7665 NULL // pThread (x86 only)
7667 return EXCEPTION_CONTINUE_EXECUTION;
7669 #endif // defined(WIN64EXCEPTIONS)
7671 return EXCEPTION_EXECUTE_HANDLER;
7675 * CLRVectoredExceptionHandlerPhase3
7677 * This routine does some basic processing on the exception, making decisions about common
7678 * exception types and whether to continue them or not. It has side-effects, in that it may
7679 * adjust the context in the exception.
7682 * pExceptionInfo - pointer to the exception
7685 * VEH_NO_ACTION - This indicates that Phase3 has no specific action to take and that further
7686 * processing of this exception should continue.
7687 * VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION - This indicates that the caller should call HandleMandagedException
7689 * VEH_CONTINUE_EXECUTION - Caller should return EXCEPTION_CONTINUE_EXECUTION.
7690 * VEH_CONTINUE_SEARCH - Caller should return EXCEPTION_CONTINUE_SEARCH;
7691 * VEH_EXECUTE_HANDLER - Caller should return EXCEPTION_EXECUTE_HANDLER.
7693 * Note that in all cases the context in the exception may have been adjusted.
7697 VEH_ACTION WINAPI CLRVectoredExceptionHandlerPhase3(PEXCEPTION_POINTERS pExceptionInfo)
7700 // DO NOT USE CONTRACTS HERE AS THIS ROUTINE MAY NEVER RETURN. You can use
7701 // static contracts, but currently this is all WRAPPER_NO_CONTRACT.
7708 // You cannot put any code in here that allocates during an out-of-memory handling.
7709 // This routine runs before *any* other handlers, including __try. Thus, if you
7710 // allocate anything in this routine then it will throw out-of-memory and end up
7713 // There are various things that allocate that you may not expect to allocate. One
7714 // instance of this is STRESS_LOG. It allocates the log buffer if the thread does
7715 // not already have one allocated. Thus, if we OOM during the setting up of the
7716 // thread, the log buffer will not be allocated and this will try to do so. Thus,
7717 // all STRESS_LOGs in here need to be after you have guaranteed the allocation has
7718 // already occurred.
7721 // Handle special cases which are common amongst all filters.
7722 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
7723 PCONTEXT pContext = pExceptionInfo->ContextRecord;
7724 DWORD exceptionCode = pExceptionRecord->ExceptionCode;
7726 // Its extremely important that no one trigger a GC in here. This is called from CPFH_FirstPassHandler, in
7727 // cases where we've taken an unmanaged exception on a managed thread (AV, divide by zero, etc.) but
7728 // _before_ we've done our work to erect a FaultingExceptionFrame. Thus, the managed frames are
7729 // unprotected. We setup a GCX_NOTRIGGER holder in this scope to prevent us from messing this up. Note
7730 // that the scope of this is limited, since there are times when its okay to trigger even in this special
7731 // case. The debugger is a good example: if it gets a breakpoint in managed code, it has the smarts to
7732 // prevent the GC before enabling GC, thus its okay for it to trigger.
7736 #ifdef USE_REDIRECT_FOR_GCSTRESS
7737 // NOTE: this is effectively ifdef (_TARGET_AMD64_ || _TARGET_ARM_), and does not actually trigger
7738 // a GC. This will redirect the exception context to a stub which will
7739 // push a frame and cause GC.
7740 if (IsGcMarker(exceptionCode, pContext))
7742 return VEH_CONTINUE_EXECUTION;;
7744 #endif // USE_REDIRECT_FOR_GCSTRESS
7746 #if defined(FEATURE_HIJACK) && !defined(PLATFORM_UNIX)
7748 CPFH_AdjustContextForThreadSuspensionRace(pContext, GetThread());
7749 #endif // _TARGET_X86_
7750 #endif // FEATURE_HIJACK && !PLATFORM_UNIX
7752 // Some other parts of the EE use exceptions in their own nefarious ways. We do some up-front processing
7753 // here to fix up the exception if needed.
7754 if (exceptionCode == STATUS_ACCESS_VIOLATION)
7756 if (IsWellFormedAV(pExceptionRecord))
7758 if (AdjustContextForWriteBarrier(pExceptionRecord, pContext))
7760 // On x86, AdjustContextForWriteBarrier simply backs up AV's
7761 // in write barrier helpers into the calling frame, so that
7762 // the subsequent logic here sees a managed fault.
7764 // On 64-bit, some additional work is required..
7765 #ifdef WIN64EXCEPTIONS
7766 return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION;
7767 #endif // defined(WIN64EXCEPTIONS)
7769 else if (AdjustContextForVirtualStub(pExceptionRecord, pContext))
7771 #ifdef WIN64EXCEPTIONS
7772 return VEH_EXECUTE_HANDLE_MANAGED_EXCEPTION;
7776 // Remember the EIP for stress debugging purposes.
7777 g_LastAccessViolationEIP = (void*) ::GetIP(pContext);
7779 // Note: we have a holder, called AVInRuntimeImplOkayHolder, that tells us that its okay to have an
7780 // AV in the Runtime's implementation in certain places. So, if its okay to have an AV at this
7781 // time, then skip the check for whether or not the AV is in our impl.
7782 // AVs are ok on the Helper thread (for which there is no pThread object,
7783 // and so the AVInRuntime holder doesn't work.
7784 Thread *pThread = GetThread();
7787 (IsDbgHelperSpecialThread() || IsETWRundownSpecialThread() ||
7788 ((pThread != NULL) && (pThread->AVInRuntimeImplOkay())) );
7791 // It is unnecessary to check this on second pass as we would have torn down
7792 // the process on the first pass. Also, the context record is not reliable
7793 // on second pass and this subjects us to false positives.
7794 if ((!fAVisOk) && !(pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING))
7796 PCODE ip = (PCODE)GetIP(pContext);
7797 if (IsIPInModule(g_pMSCorEE, ip) || IsIPInModule(GCHeapUtilities::GetGCModule(), ip))
7799 CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|SOToleranceViolation);
7802 // If you're debugging, set the debugger to catch first-chance AV's, then simply hit F5 or
7803 // 'go' and continue after the assert. We'll recgonize that a debugger is attached, and
7804 // return EXCEPTION_CONTINUE_EXECUTION. You'll re-execute the faulting instruction, and the
7805 // debugger will stop at the AV. The value of EXCEPTION_CONTINUE_EXECUTION is -1, just in
7806 // case you need to verify the return value for some reason. If you need to actually debug
7807 // the failure path, then set your IP around the check below.
7809 // You can also use Windbg's .cxr command to set the context to pContext.
7812 const char * pStack = "<stack not available>";
7813 StackScratchBuffer buffer;
7815 if (GetStackTraceAtContext(sStack, pContext))
7817 pStack = sStack.GetANSI(buffer);
7820 DWORD tid = GetCurrentThreadId();
7822 BOOL debuggerPresentBeforeAssert = IsDebuggerPresent();
7825 CONSISTENCY_CHECK_MSGF(false, ("AV in clr at this callstack:\n------\n%s\n-----\n.AV on tid=0x%x (%d), cxr=%p, exr=%p\n",
7826 pStack, tid, tid, pContext, pExceptionRecord));
7828 // @todo - this may not be what we want for interop-debugging...
7830 // If there was no debugger before the assert, but there is one now, then go ahead and
7831 // return EXCEPTION_CONTINUE_EXECUTION to re-execute the faulting instruction. This is
7832 // supposed to be a nice little feature for CLR devs who attach debuggers on the "Av in
7833 // mscorwks" assert above. Since this is only for that case, its only in debug builds.
7834 if (!debuggerPresentBeforeAssert && IsDebuggerPresent())
7836 return VEH_CONTINUE_EXECUTION;;
7838 #endif // defined(_DEBUG)
7840 EEPOLICY_HANDLE_FATAL_ERROR_USING_EXCEPTION_INFO(COR_E_EXECUTIONENGINE, pExceptionInfo);
7845 else if (exceptionCode == BOOTUP_EXCEPTION_COMPLUS)
7847 // Don't handle a boot exception
7848 return VEH_CONTINUE_SEARCH;
7851 return VEH_NO_ACTION;
7854 #endif // !FEATURE_PAL
7856 BOOL IsIPInEE(void *ip)
7858 WRAPPER_NO_CONTRACT;
7860 #if defined(FEATURE_PREJIT) && !defined(FEATURE_PAL)
7861 if ((TADDR)ip > g_runtimeLoadedBaseAddress &&
7862 (TADDR)ip < g_runtimeLoadedBaseAddress + g_runtimeVirtualSize)
7867 #endif // FEATURE_PREJIT && !FEATURE_PAL
7873 #if defined(FEATURE_HIJACK) && (!defined(_TARGET_X86_) || defined(FEATURE_PAL))
7875 // This function is used to check if the specified IP is in the prolog or not.
7876 bool IsIPInProlog(EECodeInfo *pCodeInfo)
7886 bool fInsideProlog = true;
7888 _ASSERTE(pCodeInfo->IsValid());
7890 #ifdef _TARGET_AMD64_
7892 // Optimized version for AMD64 that doesn't need to go through the GC info decoding
7893 PTR_RUNTIME_FUNCTION funcEntry = pCodeInfo->GetFunctionEntry();
7895 // We should always get a function entry for a managed method
7896 _ASSERTE(funcEntry != NULL);
7898 // Get the unwindInfo from the function entry
7899 PUNWIND_INFO pUnwindInfo = (PUNWIND_INFO)(pCodeInfo->GetModuleBase() + funcEntry->UnwindData);
7901 // Check if the specified IP is beyond the prolog or not.
7902 DWORD prologLen = pUnwindInfo->SizeOfProlog;
7904 #else // _TARGET_AMD64_
7906 GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken();
7908 #ifdef USE_GC_INFO_DECODER
7910 GcInfoDecoder gcInfoDecoder(
7912 DECODE_PROLOG_LENGTH
7915 DWORD prologLen = gcInfoDecoder.GetPrologSize();
7917 #else // USE_GC_INFO_DECODER
7920 pCodeInfo->GetCodeManager()->IsInPrologOrEpilog(0, gcInfoToken, &prologLen);
7922 #endif // USE_GC_INFO_DECODER
7924 #endif // _TARGET_AMD64_
7926 if (pCodeInfo->GetRelOffset() >= prologLen)
7928 fInsideProlog = false;
7931 return fInsideProlog;
7934 // This function is used to check if the specified IP is in the epilog or not.
7935 bool IsIPInEpilog(PTR_CONTEXT pContextToCheck, EECodeInfo *pCodeInfo, BOOL *pSafeToInjectThreadAbort)
7942 PRECONDITION(pContextToCheck != NULL);
7943 PRECONDITION(ExecutionManager::IsManagedCode(GetIP(pContextToCheck)));
7944 PRECONDITION(pSafeToInjectThreadAbort != NULL);
7948 TADDR ipToCheck = GetIP(pContextToCheck);
7950 _ASSERTE(pCodeInfo->IsValid());
7952 // The Codeinfo should correspond to the IP we are interested in.
7953 _ASSERTE(PCODEToPINSTR(ipToCheck) == pCodeInfo->GetCodeAddress());
7955 // By default, assume its safe to inject the abort.
7956 *pSafeToInjectThreadAbort = TRUE;
7958 // If we are inside a prolog, then we are obviously not in the epilog.
7959 // Its safe to inject the abort here.
7960 if (IsIPInProlog(pCodeInfo))
7965 // We are not inside the prolog. We could either be in the middle of the method body or
7966 // inside the epilog. While unwindInfo contains the prolog length, it does not contain the
7969 // Thus, to determine if we are inside the epilog, we use a property of RtlVirtualUnwind.
7970 // When invoked for an IP, it will return a NULL for personality routine in only two scenarios:
7972 // 1) The unwindInfo does not contain any personality routine information, OR
7973 // 2) The IP is in prolog or epilog.
7975 // For jitted code, (1) is not applicable since we *always* emit details of the managed personality routine
7976 // in the unwindInfo. Thus, since we have already determined that we are not inside the prolog, if performing
7977 // RtlVirtualUnwind against "ipToCheck" results in a NULL personality routine, it implies that we are inside
7980 DWORD_PTR imageBase = 0;
7981 CONTEXT tempContext;
7983 DWORD_PTR establisherFrame = 0;
7984 PEXCEPTION_ROUTINE personalityRoutine = NULL;
7986 // Lookup the function entry for the IP
7987 PTR_RUNTIME_FUNCTION funcEntry = pCodeInfo->GetFunctionEntry();
7989 // We should always get a function entry for a managed method
7990 _ASSERTE(funcEntry != NULL);
7992 imageBase = pCodeInfo->GetModuleBase();
7994 ZeroMemory(&tempContext, sizeof(CONTEXT));
7995 CopyOSContext(&tempContext, pContextToCheck);
7996 KNONVOLATILE_CONTEXT_POINTERS ctxPtrs;
7997 ZeroMemory(&ctxPtrs, sizeof(ctxPtrs));
7999 personalityRoutine = RtlVirtualUnwind(UNW_FLAG_EHANDLER, // HandlerType
8008 bool fIsInEpilog = false;
8010 if (personalityRoutine == NULL)
8012 // We are in epilog.
8015 #ifdef _TARGET_AMD64_
8016 // Check if context pointers has returned the address of the stack location in the hijacked function
8017 // from where RBP was restored. If the address is NULL, then it implies that RBP has been popped off.
8018 // Since JIT64 ensures that pop of RBP is the last instruction before ret/jmp, it implies its not safe
8019 // to inject an abort @ this point as EstablisherFrame (which will be based
8020 // of RBP for managed code since that is the FramePointer register, as indicated in the UnwindInfo)
8021 // will be off and can result in bad managed exception dispatch.
8022 if (ctxPtrs.Rbp == NULL)
8025 *pSafeToInjectThreadAbort = FALSE;
8032 #endif // FEATURE_HIJACK && (!_TARGET_X86_ || FEATURE_PAL)
8034 #define EXCEPTION_VISUALCPP_DEBUGGER ((DWORD) (1<<30 | 0x6D<<16 | 5000))
8036 #if defined(_TARGET_X86_)
8038 // This holder is used to capture the FPU state, reset it to what the CLR expects
8039 // and then restore the original state that was captured.
8041 // FPU has a set of exception masks which the CLR expects to be always set,
8042 // implying that any corresponding condition will *not* result in FPU raising
8045 // However, native code (e.g. high precision math libs) can change this mask.
8046 // Thus, when control enters the CLR (e.g. via exception dispatch into the VEH),
8047 // we will end up using floating point instructions that could satify the exception mask
8048 // condition and raise an exception. This could result in an infinite loop, resulting in
8051 // We use this holder to protect applicable parts of the runtime from running into such cases.
8052 extern "C" void CaptureFPUContext(BYTE *pFPBUBuf);
8053 extern "C" void RestoreFPUContext(BYTE *pFPBUBuf);
8055 // This is FPU specific and only applicable to x86 on Windows.
8056 class FPUStateHolder
8058 // Capturing FPU state requires a 28byte buffer
8059 BYTE m_bufFPUState[28];
8064 LIMITED_METHOD_CONTRACT;
8066 BYTE *pFPUBuf = m_bufFPUState;
8068 // Save the FPU state using the non-waiting instruction
8069 // so that FPU may not raise an exception incase the
8070 // exception masks are unset in the FPU Control Word
8071 CaptureFPUContext(pFPUBuf);
8073 // Reset the FPU state
8074 ResetCurrentContext();
8079 LIMITED_METHOD_CONTRACT;
8081 BYTE *pFPUBuf = m_bufFPUState;
8083 // Restore the capture FPU state
8084 RestoreFPUContext(pFPUBuf);
8088 #endif // defined(_TARGET_X86_)
8092 LONG WINAPI CLRVectoredExceptionHandlerShim(PEXCEPTION_POINTERS pExceptionInfo)
8095 // HandleManagedFault will take a Crst that causes an unbalanced
8096 // notrigger scope, and this contract will whack the thread's
8097 // ClrDebugState to what it was on entry in the dtor, which causes
8098 // us to assert when we finally release the Crst later on.
8109 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8111 // o This function should not call functions that acquire
8112 // synchronization objects or allocate memory, because this
8113 // can cause problems. <-- quoteth MSDN -- probably for
8114 // the same reason as we cannot use LOG(); we'll recurse
8115 // into a stack overflow.
8117 // o You cannot use LOG() in here because that will trigger an
8118 // exception which will cause infinite recursion with this
8119 // function. We work around this by ignoring all non-error
8120 // exception codes, which serves as the base of the recursion.
8121 // That way, we can LOG() from the rest of the function
8123 // The same goes for any function called by this
8126 // WARNING WARNING WARNING WARNING WARNING WARNING WARNING
8129 // If exceptions (or runtime) have been disabled, then simply return.
8130 if (g_fForbidEnterEE || g_fNoExceptions)
8132 return EXCEPTION_CONTINUE_SEARCH;
8137 // We must preserve this so that GCStress=4 eh processing doesnt kill last error.
8138 // Note that even GetThread below can affect the LastError.
8139 // Keep this in mind when adding code above this line!
8142 DWORD dwLastError = GetLastError();
8144 #if defined(_TARGET_X86_)
8145 // Capture the FPU state before we do anything involving floating point instructions
8146 FPUStateHolder captureFPUState;
8147 #endif // defined(_TARGET_X86_)
8149 #ifdef FEATURE_INTEROP_DEBUGGING
8150 // For interop debugging we have a fancy exception queueing stunt. When the debugger
8151 // initially gets the first chance exception notification it may not know whether to
8152 // continue it handled or unhandled, but it must continue the process to allow the
8153 // in-proc helper thread to work. What it does is continue the exception unhandled which
8154 // will let the thread immediately execute to this point. Inside this worker the thread
8155 // will block until the debugger knows how to continue the exception. If it decides the
8156 // exception was handled then we immediately resume execution as if the exeption had never
8157 // even been allowed to run into this handler. If it is unhandled then we keep processing
8160 // WARNING: This function could potentially throw an exception, however it should only
8161 // be able to do so when an interop debugger is attached
8162 if(g_pDebugInterface != NULL)
8164 if(g_pDebugInterface->FirstChanceSuspendHijackWorker(pExceptionInfo->ContextRecord,
8165 pExceptionInfo->ExceptionRecord) == EXCEPTION_CONTINUE_EXECUTION)
8166 return EXCEPTION_CONTINUE_EXECUTION;
8171 DWORD dwCode = pExceptionInfo->ExceptionRecord->ExceptionCode;
8172 if (dwCode == DBG_PRINTEXCEPTION_C || dwCode == EXCEPTION_VISUALCPP_DEBUGGER)
8174 return EXCEPTION_CONTINUE_SEARCH;
8177 #if defined(_TARGET_X86_)
8178 if (dwCode == EXCEPTION_BREAKPOINT || dwCode == EXCEPTION_SINGLE_STEP)
8180 // For interop debugging, debugger bashes our managed exception handler.
8181 // Interop debugging does not work with real vectored exception handler :(
8182 return EXCEPTION_CONTINUE_SEARCH;
8186 bool bIsGCMarker = false;
8188 #ifdef USE_REDIRECT_FOR_GCSTRESS
8189 // This is AMD64 & ARM specific as the macro above is defined for AMD64 & ARM only
8190 bIsGCMarker = IsGcMarker(dwCode, pExceptionInfo->ContextRecord);
8191 #elif defined(_TARGET_X86_) && defined(HAVE_GCCOVER)
8192 // This is the equivalent of the check done in COMPlusFrameHandler, incase the exception is
8193 // seen by VEH first on x86.
8194 bIsGCMarker = IsGcMarker(dwCode, pExceptionInfo->ContextRecord);
8195 #endif // USE_REDIRECT_FOR_GCSTRESS
8197 // Do not update the TLS with exception details for exceptions pertaining to GCStress
8198 // as they are continueable in nature.
8201 SaveCurrentExceptionInfo(pExceptionInfo->ExceptionRecord, pExceptionInfo->ContextRecord);
8205 LONG result = EXCEPTION_CONTINUE_SEARCH;
8207 // If we cannot obtain a Thread object, then we have no business processing any
8208 // exceptions on this thread. Indeed, even checking to see if the faulting
8209 // address is in JITted code is problematic if we have no Thread object, since
8210 // this thread will bypass all our locks.
8211 Thread *pThread = GetThread();
8213 // Also check if the exception was in the EE or not
8214 BOOL fExceptionInEE = FALSE;
8217 // Check if the exception was in EE only if Thread object isnt available.
8218 // This will save us from unnecessary checks
8219 fExceptionInEE = IsIPInEE(pExceptionInfo->ExceptionRecord->ExceptionAddress);
8222 // We are going to process the exception only if one of the following conditions is true:
8224 // 1) We have a valid Thread object (implies exception on managed thread)
8225 // 2) Not a valid Thread object but the IP is in the execution engine (implies native thread within EE faulted)
8226 if (pThread || fExceptionInEE)
8229 result = CLRVectoredExceptionHandler(pExceptionInfo);
8231 result = EXCEPTION_CONTINUE_EXECUTION;
8233 if (EXCEPTION_EXECUTE_HANDLER == result)
8235 result = EXCEPTION_CONTINUE_SEARCH;
8239 #ifndef WIN64EXCEPTIONS
8241 CantAllocHolder caHolder;
8243 PEXCEPTION_REGISTRATION_RECORD pRecord = GetCurrentSEHRecord();
8244 while (pRecord != EXCEPTION_CHAIN_END)
8246 STRESS_LOG2(LF_EH, LL_INFO10000, "CLRVectoredExceptionHandlerShim: FS:0 %p:%p\n",
8247 pRecord, pRecord->Handler);
8248 pRecord = pRecord->Next;
8251 #endif // WIN64EXCEPTIONS
8254 // The call to "CLRVectoredExceptionHandler" above can return EXCEPTION_CONTINUE_SEARCH
8255 // for different scenarios like StackOverFlow/SOFT_SO, or if it is forbidden to enter the EE.
8256 // Thus, if we dont have a Thread object for the thread that has faulted and we came this far
8257 // because the fault was in MSCORWKS, then we work with the frame chain below only if we have
8258 // valid Thread object.
8262 CantAllocHolder caHolder;
8267 void* stopPoint = pThread->GetCachedStackBase();
8268 // If Frame chain is corrupted, we may get AV while accessing frames, and this function will be
8269 // called recursively. We use Frame chain to limit our search range. It is not disaster if we
8271 if (!(dwCode == STATUS_ACCESS_VIOLATION &&
8272 IsIPInEE(pExceptionInfo->ExceptionRecord->ExceptionAddress)))
8274 // Find the stop point (most jitted function)
8275 Frame* pFrame = pThread->GetFrame();
8279 if (pFrame == 0 || pFrame == (Frame*) -1)
8282 Frame::ETransitionType type = pFrame->GetTransitionType();
8283 if (type == Frame::TT_M2U || type == Frame::TT_InternalCall)
8288 pFrame = pFrame->Next();
8291 STRESS_LOG0(LF_EH, LL_INFO100, "CLRVectoredExceptionHandlerShim: stack");
8292 while (count < 20 && sp < stopPoint)
8294 if (IsIPInEE((BYTE*)*sp))
8296 STRESS_LOG1(LF_EH, LL_INFO100, "%pK\n", *sp);
8305 #ifndef WIN64EXCEPTIONS
8307 CantAllocHolder caHolder;
8308 STRESS_LOG1(LF_EH, LL_INFO1000, "CLRVectoredExceptionHandlerShim: returning %d\n", result);
8310 #endif // WIN64EXCEPTIONS
8314 SetLastError(dwLastError);
8319 #endif // !FEATURE_PAL
8321 // Contains the handle to the registered VEH
8322 static PVOID g_hVectoredExceptionHandler = NULL;
8324 void CLRAddVectoredHandlers(void)
8328 // We now install a vectored exception handler on all supporting Windows architectures.
8329 g_hVectoredExceptionHandler = AddVectoredExceptionHandler(TRUE, (PVECTORED_EXCEPTION_HANDLER)CLRVectoredExceptionHandlerShim);
8330 if (g_hVectoredExceptionHandler == NULL)
8332 LOG((LF_EH, LL_INFO100, "CLRAddVectoredHandlers: AddVectoredExceptionHandler() failed\n"));
8333 COMPlusThrowHR(E_FAIL);
8336 LOG((LF_EH, LL_INFO100, "CLRAddVectoredHandlers: AddVectoredExceptionHandler() succeeded\n"));
8337 #endif // !FEATURE_PAL
8340 // This function removes the vectored exception and continue handler registration
8342 void CLRRemoveVectoredHandlers(void)
8353 // Unregister the vectored exception handler if one is registered (and we can).
8354 if (g_hVectoredExceptionHandler != NULL)
8356 // Unregister the vectored exception handler
8357 if (RemoveVectoredExceptionHandler(g_hVectoredExceptionHandler) == FALSE)
8359 LOG((LF_EH, LL_INFO100, "CLRRemoveVectoredHandlers: RemoveVectoredExceptionHandler() failed.\n"));
8363 LOG((LF_EH, LL_INFO100, "CLRRemoveVectoredHandlers: RemoveVectoredExceptionHandler() succeeded.\n"));
8366 #endif // !FEATURE_PAL
8370 // This does the work of the Unwind and Continue Hanlder inside the catch clause of that handler. The stack has not
8371 // been unwound when this is called. Keep that in mind when deciding where to put new code :)
8373 void UnwindAndContinueRethrowHelperInsideCatch(Frame* pEntryFrame, Exception* pException)
8375 STATIC_CONTRACT_NOTHROW;
8376 STATIC_CONTRACT_GC_TRIGGERS;
8377 STATIC_CONTRACT_MODE_ANY;
8378 STATIC_CONTRACT_SO_TOLERANT;
8380 Thread* pThread = GetThread();
8384 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE inside catch, unwinding frame chain\n"));
8386 // This SetFrame is OK because we will not have frames that require ExceptionUnwind in strictly unmanaged EE
8387 // code chunks which is all that an UnC handler can guard.
8389 // @todo: we'd rather use UnwindFrameChain, but there is a concern: some of the ExceptionUnwind methods on some
8390 // of the Frame types do a great deal of work; load classes, throw exceptions, etc. We need to decide on some
8391 // policy here. Do we want to let such funcitons throw, etc.? Right now, we believe that there are no such
8392 // frames on the stack to be unwound, so the SetFrame is alright (see the first comment above.) At the very
8393 // least, we should add some way to assert that.
8394 pThread->SetFrame(pEntryFrame);
8397 if (!NingenEnabled())
8399 CONTRACT_VIOLATION(ThrowsViolation);
8400 BEGIN_SO_INTOLERANT_CODE(pThread);
8401 // Call CLRException::GetThrowableFromException to force us to retrieve the THROWABLE
8402 // while we are still within the context of the catch block. This will help diagnose
8403 // cases where the last thrown object is NULL.
8404 OBJECTREF orThrowable = CLRException::GetThrowableFromException(pException);
8405 CONSISTENCY_CHECK(orThrowable != NULL);
8406 END_SO_INTOLERANT_CODE;
8412 // This does the work of the Unwind and Continue Hanlder after the catch clause of that handler. The stack has been
8413 // unwound by the time this is called. Keep that in mind when deciding where to put new code :)
8415 VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFrame, Exception* pException)
8417 STATIC_CONTRACT_THROWS;
8418 STATIC_CONTRACT_GC_TRIGGERS;
8419 STATIC_CONTRACT_MODE_ANY;
8420 STATIC_CONTRACT_SO_TOLERANT;
8422 // We really should probe before switching to cooperative mode, although there's no chance
8423 // we'll SO in doing that as we've just caught an exception. We can't probe just
8424 // yet though, because we want to avoid reprobing on an SO exception and we need to switch
8425 // to cooperative to check the throwable for an SO as well as the pException object (as the
8426 // pException could be a LastThrownObjectException.) Blech.
8427 CONTRACT_VIOLATION(SOToleranceViolation);
8431 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE caught and will rethrow\n"));
8433 OBJECTREF orThrowable = NingenEnabled() ? NULL : CLRException::GetThrowableFromException(pException);
8434 LOG((LF_EH, LL_INFO1000, "UNWIND_AND_CONTINUE got throwable %p\n",
8435 OBJECTREFToObject(orThrowable)));
8437 Exception::Delete(pException);
8439 if (orThrowable != NULL && g_CLRPolicyRequested)
8441 if (orThrowable->GetMethodTable() == g_pOutOfMemoryExceptionClass)
8443 EEPolicy::HandleOutOfMemory();
8445 else if (orThrowable->GetMethodTable() == g_pStackOverflowExceptionClass)
8447 #ifdef FEATURE_STACK_PROBE
8448 EEPolicy::HandleSoftStackOverflow();
8450 /* The parameters of the function do not matter here */
8451 EEPolicy::HandleStackOverflow(SOD_UnmanagedFrameHandler, NULL);
8456 RaiseTheExceptionInternalOnly(orThrowable, FALSE);
8459 void SaveCurrentExceptionInfo(PEXCEPTION_RECORD pRecord, PCONTEXT pContext)
8470 if ((pRecord->ExceptionFlags & (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)))
8472 // If exception is unwinding the stack, the ExceptionCode may have been changed to
8473 // STATUS_UNWIND if RtlUnwind is called with a NULL ExceptionRecord.
8474 // Since we have captured exception info in the first pass, we don't need to capture it again.
8478 if (CExecutionEngine::CheckThreadStateNoCreate(TlsIdx_PEXCEPTION_RECORD))
8481 if (!IsSOExceptionCode(pRecord->ExceptionCode))
8483 DWORD dwLastExceptionCode = (DWORD)(SIZE_T) (ClrFlsGetValue(TlsIdx_EXCEPTION_CODE));
8484 if (IsSOExceptionCode(dwLastExceptionCode))
8486 PEXCEPTION_RECORD lastRecord =
8487 static_cast<PEXCEPTION_RECORD> (ClrFlsGetValue(TlsIdx_PEXCEPTION_RECORD));
8489 // We are trying to see if C++ is attempting a rethrow of a SO exception. If so,
8490 // we want to prevent updating the exception details in the TLS. This is a workaround,
8491 // as explained below.
8492 if (pRecord->ExceptionCode == EXCEPTION_MSVC)
8494 // This is a workaround.
8495 // When C++ rethrows, C++ internally gets rid of the new exception record after
8496 // unwinding stack, and present the original exception record to the thread.
8497 // When we get VC's support to obtain exception record in try/catch, we will replace
8499 if (pRecord < lastRecord)
8501 // For the C++ rethrow workaround, ensure that the last exception record is still valid and as we expect it to be.
8503 // Its possible that we are still below the address of last exception record
8504 // but since the execution stack could have changed, simply comparing its address
8505 // with the address of the current exception record may not be enough.
8507 // Thus, ensure that its still valid and holds the exception code we expect it to
8508 // have (i.e. value in dwLastExceptionCode).
8509 if ((lastRecord != NULL) && (lastRecord->ExceptionCode == dwLastExceptionCode))
8519 ClrFlsSetValue(TlsIdx_EXCEPTION_CODE, (void*)(size_t)(pRecord->ExceptionCode));
8520 ClrFlsSetValue(TlsIdx_PEXCEPTION_RECORD, pRecord);
8521 ClrFlsSetValue(TlsIdx_PCONTEXT, pContext);
8526 #ifndef DACCESS_COMPILE
8527 //******************************************************************************
8529 // NotifyOfCHFFilterWrapper
8531 // Helper function to deliver notifications of CatchHandlerFound inside a
8535 // pExceptionInfo - the pExceptionInfo passed to a filter function.
8536 // pCatcherStackAddr - a Frame* from the PAL_TRY/PAL_EXCEPT_FILTER site.
8539 // always returns EXCEPTION_CONTINUE_SEARCH.
8541 //******************************************************************************
8542 LONG NotifyOfCHFFilterWrapper(
8543 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8544 PVOID pParam) // contains a Frame* from the PAL_TRY/PAL_EXCEPT_FILTER site.
8546 LIMITED_METHOD_CONTRACT;
8548 PVOID pCatcherStackAddr = ((NotifyOfCHFFilterWrapperParam *)pParam)->pFrame;
8549 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8551 // We are here to send an event notification to the debugger and to the appdomain. To
8552 // determine if it is safe to send these notifications, check the following:
8553 // 1) The thread object has been set up.
8554 // 2) The thread has an exception on it.
8555 // 3) The exception is the same as the one this filter is called on.
8556 Thread *pThread = GetThread();
8557 if ( (pThread == NULL) ||
8558 (pThread->GetExceptionState()->GetContextRecord() == NULL) ||
8559 (GetSP(pThread->GetExceptionState()->GetContextRecord()) != GetSP(pExceptionInfo->ContextRecord) ) )
8561 LOG((LF_EH, LL_INFO1000, "NotifyOfCHFFilterWrapper: not sending notices. pThread: %0x8", pThread));
8564 LOG((LF_EH, LL_INFO1000, ", Thread SP: %0x8, Exception SP: %08x",
8565 pThread->GetExceptionState()->GetContextRecord() ? GetSP(pThread->GetExceptionState()->GetContextRecord()) : NULL,
8566 pExceptionInfo->ContextRecord ? GetSP(pExceptionInfo->ContextRecord) : NULL ));
8568 LOG((LF_EH, LL_INFO1000, "\n"));
8572 if (g_pDebugInterface)
8574 // It looks safe, so make the debugger notification.
8575 ret = g_pDebugInterface->NotifyOfCHFFilter(pExceptionInfo, pCatcherStackAddr);
8579 } // LONG NotifyOfCHFFilterWrapper()
8581 // This filter will be used process exceptions escaping out of AD transition boundaries
8582 // that are not at the base of the managed thread. Those are handled in ThreadBaseRedirectingFilter.
8583 // This will be invoked when an exception is going unhandled from the called AppDomain.
8585 // This can be used to do last moment work before the exception gets caught by the EX_CATCH setup
8586 // at the AD transition point.
8587 LONG AppDomainTransitionExceptionFilter(
8588 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8591 // Ideally, we would be NOTHROW here. However, NotifyOfCHFFilterWrapper calls into
8592 // NotifyOfCHFFilter that is THROWS. Thus, to prevent contract violation,
8593 // we abide by the rules and be THROWS.
8595 // Same rationale for GC_TRIGGERS as well.
8604 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8606 // First, call into NotifyOfCHFFilterWrapper
8607 ret = NotifyOfCHFFilterWrapper(pExceptionInfo, pParam);
8610 // Setup the watson bucketing details if the escaping
8611 // exception is preallocated.
8612 if (SetupWatsonBucketsForEscapingPreallocatedExceptions())
8614 // Set the flag that these were captured at AD Transition
8615 DEBUG_STMT(GetThread()->GetExceptionState()->GetUEWatsonBucketTracker()->SetCapturedAtADTransition());
8618 // Attempt to capture buckets for non-preallocated exceptions just before the AppDomain transition boundary
8621 OBJECTREF oThrowable = GetThread()->GetThrowable();
8622 if ((oThrowable != NULL) && (CLRException::IsPreallocatedExceptionObject(oThrowable) == FALSE))
8624 SetupWatsonBucketsForNonPreallocatedExceptions();
8627 #endif // !FEATURE_PAL
8630 } // LONG AppDomainTransitionExceptionFilter()
8632 // This filter will be used process exceptions escaping out of dynamic reflection invocation as
8633 // unhandled and will eventually be caught in the VM to be made as inner exception of
8634 // TargetInvocationException that will be thrown from the VM.
8635 LONG ReflectionInvocationExceptionFilter(
8636 EXCEPTION_POINTERS *pExceptionInfo, // the pExceptionInfo passed to a filter function.
8639 // Ideally, we would be NOTHROW here. However, NotifyOfCHFFilterWrapper calls into
8640 // NotifyOfCHFFilter that is THROWS. Thus, to prevent contract violation,
8641 // we abide by the rules and be THROWS.
8643 // Same rationale for GC_TRIGGERS as well.
8652 ULONG ret = EXCEPTION_CONTINUE_SEARCH;
8654 // First, call into NotifyOfCHFFilterWrapper
8655 ret = NotifyOfCHFFilterWrapper(pExceptionInfo, pParam);
8658 // Setup the watson bucketing details if the escaping
8659 // exception is preallocated.
8660 if (SetupWatsonBucketsForEscapingPreallocatedExceptions())
8662 // Set the flag that these were captured during Reflection Invocation
8663 DEBUG_STMT(GetThread()->GetExceptionState()->GetUEWatsonBucketTracker()->SetCapturedAtReflectionInvocation());
8666 // Attempt to capture buckets for non-preallocated exceptions just before the ReflectionInvocation boundary
8669 OBJECTREF oThrowable = GetThread()->GetThrowable();
8670 if ((oThrowable != NULL) && (CLRException::IsPreallocatedExceptionObject(oThrowable) == FALSE))
8672 SetupWatsonBucketsForNonPreallocatedExceptions();
8675 #endif // !FEATURE_PAL
8677 // If the application has opted into triggering a failfast when a CorruptedStateException enters the Reflection system,
8678 // then do the needful.
8679 if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_FailFastOnCorruptedStateException) == 1)
8681 // Get the thread and the managed exception object - they must exist at this point
8682 Thread *pCurThread = GetThread();
8683 _ASSERTE(pCurThread != NULL);
8685 // Get the thread exception state
8686 ThreadExceptionState * pCurTES = pCurThread->GetExceptionState();
8687 _ASSERTE(pCurTES != NULL);
8689 // Get the exception tracker for the current exception
8690 #ifdef WIN64EXCEPTIONS
8691 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
8693 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
8694 #else // !(_WIN64 || _TARGET_X86_)
8695 #error Unsupported platform
8698 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
8699 if (pEHTracker->GetCorruptionSeverity() == ProcessCorrupting)
8701 EEPolicy::HandleFatalError(COR_E_FAILFAST, reinterpret_cast<UINT_PTR>(pExceptionInfo->ExceptionRecord->ExceptionAddress), NULL, pExceptionInfo);
8703 #endif // FEATURE_CORRUPTING_EXCEPTIONS
8707 } // LONG ReflectionInvocationExceptionFilter()
8709 #endif // !DACCESS_COMPILE
8712 bool DebugIsEECxxExceptionPointer(void* pv)
8728 // check whether the memory is readable in no-throw way
8729 if (!isMemoryReadable((TADDR)pv, sizeof(UINT_PTR)))
8734 bool retVal = false;
8738 UINT_PTR vtbl = *(UINT_PTR*)pv;
8742 HRException boilerplate1;
8743 COMException boilerplate2;
8744 SEHException boilerplate3;
8748 CLRException boilerplate4;
8749 CLRLastThrownObjectException boilerplate5;
8750 EEException boilerplate6;
8751 EEMessageException boilerplate7;
8752 EEResourceException boilerplate8;
8754 // EECOMException::~EECOMException calls FreeExceptionData, which is GC_TRIGGERS,
8755 // but it won't trigger in this case because EECOMException's members remain NULL.
8756 CONTRACT_VIOLATION(GCViolation);
8757 EECOMException boilerplate9;
8759 EEFieldException boilerplate10;
8760 EEMethodException boilerplate11;
8761 EEArgumentException boilerplate12;
8762 EETypeLoadException boilerplate13;
8763 EEFileLoadException boilerplate14;
8764 ObjrefException boilerplate15;
8766 UINT_PTR ValidVtbls[] =
8768 *((TADDR*)&boilerplate1),
8769 *((TADDR*)&boilerplate2),
8770 *((TADDR*)&boilerplate3),
8771 *((TADDR*)&boilerplate4),
8772 *((TADDR*)&boilerplate5),
8773 *((TADDR*)&boilerplate6),
8774 *((TADDR*)&boilerplate7),
8775 *((TADDR*)&boilerplate8),
8776 *((TADDR*)&boilerplate9),
8777 *((TADDR*)&boilerplate10),
8778 *((TADDR*)&boilerplate11),
8779 *((TADDR*)&boilerplate12),
8780 *((TADDR*)&boilerplate13),
8781 *((TADDR*)&boilerplate14),
8782 *((TADDR*)&boilerplate15)
8785 const int nVtbls = sizeof(ValidVtbls) / sizeof(ValidVtbls[0]);
8787 for (int i = 0; i < nVtbls; i++)
8789 if (vtbl == ValidVtbls[i])
8798 // Swallow any exception out of the exception constructors above and simply return false.
8800 EX_END_CATCH(SwallowAllExceptions);
8805 void *DebugGetCxxException(EXCEPTION_RECORD* pExceptionRecord);
8807 bool DebugIsEECxxException(EXCEPTION_RECORD* pExceptionRecord)
8809 return DebugIsEECxxExceptionPointer(DebugGetCxxException(pExceptionRecord));
8813 // C++ EH cracking material gleaned from the debugger:
8814 // (DO NOT USE THIS KNOWLEDGE IN NON-DEBUG CODE!!!)
8816 // EHExceptionRecord::EHParameters
8817 // [0] magicNumber : uint
8818 // [1] pExceptionObject : void*
8819 // [2] pThrowInfo : ThrowInfo*
8822 #define NUM_CXX_EXCEPTION_PARAMS 4
8824 #define NUM_CXX_EXCEPTION_PARAMS 3
8827 void *DebugGetCxxException(EXCEPTION_RECORD* pExceptionRecord)
8829 WRAPPER_NO_CONTRACT;
8831 bool fExCodeIsCxx = (EXCEPTION_MSVC == pExceptionRecord->ExceptionCode);
8832 bool fExHasCorrectNumParams = (NUM_CXX_EXCEPTION_PARAMS == pExceptionRecord->NumberParameters);
8834 if (fExCodeIsCxx && fExHasCorrectNumParams)
8836 void** ppException = (void**)pExceptionRecord->ExceptionInformation[1];
8838 if (NULL == ppException)
8843 return *ppException;
8847 CONSISTENCY_CHECK_MSG(!fExCodeIsCxx || fExHasCorrectNumParams, "We expected an EXCEPTION_MSVC exception to have 3 parameters. Did the CRT change its exception format?");
8854 #endif // #ifndef DACCESS_COMPILE
8856 BOOL IsException(MethodTable *pMT) {
8867 ASSERT(g_pExceptionClass != NULL);
8869 while (pMT != NULL && pMT != g_pExceptionClass) {
8870 pMT = pMT->GetParentMethodTable();
8874 } // BOOL IsException()
8876 // Returns TRUE iff calling get_StackTrace on an exception of the given type ends up
8877 // executing some other code than just Exception.get_StackTrace.
8878 BOOL ExceptionTypeOverridesStackTraceGetter(PTR_MethodTable pMT)
8890 _ASSERTE(IsException(pMT));
8892 if (pMT == g_pExceptionClass)
8894 // if the type is System.Exception, it certainly doesn't override anything
8898 // find the slot corresponding to get_StackTrace
8899 for (DWORD slot = g_pObjectClass->GetNumVirtuals(); slot < g_pExceptionClass->GetNumVirtuals(); slot++)
8901 MethodDesc *pMD = g_pExceptionClass->GetMethodDescForSlot(slot);
8902 LPCUTF8 name = pMD->GetName();
8904 if (name != NULL && strcmp(name, "get_StackTrace") == 0)
8906 // see if the slot is overriden by pMT
8907 MethodDesc *pDerivedMD = pMT->GetMethodDescForSlot(slot);
8908 return (pDerivedMD != pMD);
8912 // there must be get_StackTrace on System.Exception
8916 // Removes source file names/paths and line information from a stack trace generated
8917 // by Environment.GetStackTrace.
8918 void StripFileInfoFromStackTrace(SString &ssStackTrace)
8929 SString::Iterator i = ssStackTrace.Begin();
8930 SString::Iterator end;
8931 int countBracket = 0;
8934 while (i < ssStackTrace.End())
8940 else if (i[0] == W(')'))
8942 if (countBracket == 1)
8945 SString::Iterator j = i + 1;
8946 while (j < ssStackTrace.End())
8948 if (j[0] == W('\r') || j[0] == W('\n'))
8956 ssStackTrace.Delete(end,j-end);
8957 i = ssStackTrace.Begin() + position;
8965 ssStackTrace.Truncate(end);
8969 //==============================================================================
8970 // This function will set a thread state indicating if an exception is escaping
8971 // the last CLR personality routine on the stack in a reverse pinvoke scenario.
8973 // If the exception continues to go unhandled, it will eventually reach the OS
8974 // that will start invoking the UEFs. Since CLR registers its UEF only to handle
8975 // unhandled exceptions on such reverse pinvoke threads, we will assert this
8976 // state in our UEF to ensure it does not get called for any other reason.
8978 // This function should be called only if the personality routine returned
8979 // EXCEPTION_CONTINUE_SEARCH.
8980 //==============================================================================
8981 void SetReversePInvokeEscapingUnhandledExceptionStatus(BOOL fIsUnwinding,
8982 #if defined(_TARGET_X86_)
8983 EXCEPTION_REGISTRATION_RECORD * pEstablisherFrame
8984 #elif defined(WIN64EXCEPTIONS)
8985 ULONG64 pEstablisherFrame
8987 #error Unsupported platform
8991 #ifndef DACCESS_COMPILE
8993 LIMITED_METHOD_CONTRACT;
8995 Thread *pCurThread = GetThread();
8996 _ASSERTE(pCurThread);
8998 if (pCurThread->GetExceptionState()->IsExceptionInProgress())
9002 // Get the top-most Frame of this thread.
9003 Frame* pCurFrame = pCurThread->GetFrame();
9004 Frame* pTopMostFrame = pCurFrame;
9005 while (pCurFrame && (pCurFrame != FRAME_TOP))
9007 pTopMostFrame = pCurFrame;
9008 pCurFrame = pCurFrame->PtrNextFrame();
9011 // Is the exception escaping the last CLR personality routine on the stack of a
9012 // reverse pinvoke thread?
9013 if (((pTopMostFrame == NULL) || (pTopMostFrame == FRAME_TOP)) ||
9014 ((void *)(pEstablisherFrame) > (void *)(pTopMostFrame)))
9016 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: setting Ex_RPInvokeEscapingException\n"));
9017 // Set the flag on the thread indicating the exception is escaping the
9018 // top most reverse pinvoke exception handler.
9019 pCurThread->GetExceptionState()->GetFlags()->SetReversePInvokeEscapingException();
9024 // Since we are unwinding, simply unset the flag indicating escaping unhandled exception
9026 if (pCurThread->GetExceptionState()->GetFlags()->ReversePInvokeEscapingException())
9028 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: unsetting Ex_RPInvokeEscapingException\n"));
9029 pCurThread->GetExceptionState()->GetFlags()->ResetReversePInvokeEscapingException();
9035 LOG((LF_EH, LL_INFO100, "SetReversePInvokeEscapingUnhandledExceptionStatus: not setting Ex_RPInvokeEscapingException since no exception is in progress.\n"));
9037 #endif // !DACCESS_COMPILE
9044 // This function will capture the watson buckets for the current exception object that is:
9046 // 1) Non-preallocated
9047 // 2) Already contains the IP for watson bucketing
9048 BOOL SetupWatsonBucketsForNonPreallocatedExceptions(OBJECTREF oThrowable /* = NULL */)
9050 #ifndef DACCESS_COMPILE
9052 // CoreCLR may have watson bucketing conditionally enabled.
9053 if (!IsWatsonEnabled())
9063 PRECONDITION(GetThread() != NULL);
9067 // By default, assume we didnt get the buckets
9068 BOOL fSetupWatsonBuckets = FALSE;
9070 Thread * pThread = GetThread();
9074 OBJECTREF oThrowable;
9076 ZeroMemory(&gc, sizeof(gc));
9077 GCPROTECT_BEGIN(gc);
9079 // Get the throwable to be used
9080 gc.oThrowable = (oThrowable != NULL) ? oThrowable : pThread->GetThrowable();
9081 if (gc.oThrowable == NULL)
9083 // If we have no throwable, then simply return back.
9085 // We could be here because the VM may have raised an exception,
9086 // and not managed code, for its internal usage (e.g. TA to end the
9087 // threads when unloading an AppDomain). Thus, there would be no throwable
9088 // present since the exception has not been seen by the runtime's
9089 // personality routine.
9091 // Hence, we have no work to do here.
9092 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - No throwable available.\n"));
9096 // The exception object should be non-preallocated
9097 _ASSERTE(!CLRException::IsPreallocatedExceptionObject(gc.oThrowable));
9099 if (((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() == FALSE)
9101 // Attempt to capture the watson buckets since they are not present.
9102 UINT_PTR ip = ((EXCEPTIONREF)gc.oThrowable)->GetIPForWatsonBuckets();
9105 // Attempt to capture the buckets
9106 PTR_VOID pBuckets = GetBucketParametersForManagedException(ip, TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9107 if (pBuckets != NULL)
9109 // Got the buckets - save them to the exception object
9110 fSetupWatsonBuckets = FALSE;
9113 fSetupWatsonBuckets = CopyWatsonBucketsToThrowable(pBuckets, gc.oThrowable);
9117 // OOM can bring us here
9118 fSetupWatsonBuckets = FALSE;
9120 EX_END_CATCH(SwallowAllExceptions);
9122 if (!fSetupWatsonBuckets)
9124 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Unable to copy buckets to throwable likely due to OOM.\n"));
9128 // Clear the saved IP since we have captured the buckets
9129 ((EXCEPTIONREF)gc.oThrowable)->SetIPForWatsonBuckets(NULL);
9130 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Buckets copied to throwable.\n"));
9132 FreeBucketParametersForManagedException(pBuckets);
9136 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - Unable to capture buckets from IP likely due to OOM.\n"));
9141 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForNonPreallocatedExceptions - No IP available to capture buckets from.\n"));
9148 return fSetupWatsonBuckets;
9149 #else // DACCESS_COMPILE
9151 #endif // !DACCESS_COMPILE
9154 // When exceptions are escaping out of various transition boundaries,
9155 // we will need to capture bucket details for the original exception
9156 // before the exception goes across the boundary to the caller.
9158 // Examples of such boundaries include:
9160 // 1) AppDomain transition boundaries (these are physical transition boundaries)
9161 // 2) Dynamic method invocation in Reflection (these are logical transition boundaries).
9163 // This function will capture the bucketing details in the UE tracker so that
9164 // they can be used once we cross over.
9165 BOOL SetupWatsonBucketsForEscapingPreallocatedExceptions()
9167 #ifndef DACCESS_COMPILE
9169 // CoreCLR may have watson bucketing conditionally enabled.
9170 if (!IsWatsonEnabled())
9180 PRECONDITION(GetThread() != NULL);
9184 // By default, assume we didnt get the buckets
9185 BOOL fSetupWatsonBuckets = FALSE;
9186 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker;
9188 Thread * pThread = GetThread();
9190 // If the exception going unhandled is preallocated, then capture the Watson buckets in the UE Watson
9191 // bucket tracker provided its not already populated.
9193 // Switch to COOP mode
9198 OBJECTREF oThrowable;
9200 ZeroMemory(&gc, sizeof(gc));
9201 GCPROTECT_BEGIN(gc);
9203 // Get the throwable corresponding to the escaping exception
9204 gc.oThrowable = pThread->GetThrowable();
9205 if (gc.oThrowable == NULL)
9207 // If we have no throwable, then simply return back.
9209 // We could be here because the VM may have raised an exception,
9210 // and not managed code, for its internal usage (e.g. TA to end the
9211 // threads when unloading an AppDomain). Thus, there would be no throwable
9212 // present since the exception has not been seen by the runtime's
9213 // personality routine.
9215 // Hence, we have no work to do here.
9216 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - No throwable available.\n"));
9220 // Is the exception preallocated? We are not going to process non-preallocated exception objects since
9221 // they already have the watson buckets in them.
9223 // We skip thread abort as well since we track them in the UE watson bucket tracker at
9224 // throw time itself.
9225 if (!((CLRException::IsPreallocatedExceptionObject(gc.oThrowable)) &&
9226 !IsThrowableThreadAbortException(gc.oThrowable)))
9228 // Its either not preallocated or a thread abort exception,
9229 // neither of which we need to process.
9233 // The UE watson bucket tracker could be non-empty if there were earlier transitions
9234 // on the threads stack before the exception got raised.
9235 pUEWatsonBucketTracker = pThread->GetExceptionState()->GetUEWatsonBucketTracker();
9236 _ASSERTE(pUEWatsonBucketTracker != NULL);
9238 // Proceed to capture bucketing details only if the UE watson bucket tracker is empty.
9239 if((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) && (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL))
9241 // Get the Watson Bucket tracker for this preallocated exception
9242 PTR_EHWatsonBucketTracker pCurWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9244 if (pCurWatsonBucketTracker != NULL)
9246 // If the tracker exists, we must have the throw site IP
9247 _ASSERTE(pCurWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL);
9249 // Init the UE Watson bucket tracker
9250 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9252 // Copy the Bucket details to the UE watson bucket tracker
9253 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pCurWatsonBucketTracker));
9255 // If the buckets dont exist, capture them now
9256 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9258 pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9261 // If the IP was in managed code, we will have the buckets.
9262 if(pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9264 fSetupWatsonBuckets = TRUE;
9265 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Captured watson buckets for preallocated exception at transition.\n"));
9269 // IP was likely in native code - hence, watson helper functions couldnt get us the buckets
9270 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Watson buckets not found for IP. IP likely in native code.\n"));
9272 // Clear the UE tracker
9273 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9278 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForEscapingPreallocatedExceptions - Watson bucket tracker for preallocated exception not found. Exception likely thrown in native code.\n"));
9285 return fSetupWatsonBuckets;
9286 #else // DACCESS_COMPILE
9288 #endif // !DACCESS_COMPILE
9291 // This function is invoked from the UEF worker to setup the watson buckets
9292 // for the exception going unhandled, if details are available. See
9293 // implementation below for specifics.
9294 void SetupWatsonBucketsForUEF(BOOL fUseLastThrownObject)
9296 #ifndef DACCESS_COMPILE
9298 // CoreCLR may have watson bucketing conditionally enabled.
9299 if (!IsWatsonEnabled())
9309 PRECONDITION(GetThread() != NULL);
9313 Thread *pThread = GetThread();
9315 PTR_EHWatsonBucketTracker pCurWatsonBucketTracker = NULL;
9316 ThreadExceptionState *pExState = pThread->GetExceptionState();
9317 _ASSERTE(pExState != NULL);
9319 // If the exception tracker exists, then copy the bucketing details
9320 // from it to the UE Watson Bucket tracker.
9322 // On 64bit, the EH system allocates the EHTracker only in the case of an exception.
9323 // Thus, assume a reverse pinvoke thread transitions to managed code from native,
9324 // does some work in managed and returns back to native code.
9326 // In the native code, it has an exception that goes unhandled and the OS
9327 // ends up invoking our UEF, and thus, we land up here.
9329 // In such a case, on 64bit, we wont have an exception tracker since there
9330 // was no managed exception active. On 32bit, we will have a tracker
9331 // but there wont be an IP corresponding to the throw site since exception
9332 // was raised in native code.
9334 // But if the tracker exists, simply copy the bucket details to the UE Watson Bucket
9335 // tracker for use by the "WatsonLastChance" path.
9336 BOOL fDoWeHaveWatsonBuckets = FALSE;
9337 if (pExState->GetCurrentExceptionTracker() != NULL)
9339 // Check the exception state if we have Watson bucket details
9340 fDoWeHaveWatsonBuckets = pExState->GetFlags()->GotWatsonBucketDetails();
9343 // Switch to COOP mode before working with the throwable
9346 // Get the throwable we are going to work with
9349 OBJECTREF oThrowable;
9351 ZeroMemory(&gc, sizeof(gc));
9352 GCPROTECT_BEGIN(gc);
9354 gc.oThrowable = fUseLastThrownObject ? pThread->LastThrownObject() : pThread->GetThrowable();
9355 BOOL fThrowableExists = (gc.oThrowable != NULL);
9356 BOOL fIsThrowablePreallocated = !fThrowableExists ? FALSE : CLRException::IsPreallocatedExceptionObject(gc.oThrowable);
9358 if ((!fDoWeHaveWatsonBuckets) && fThrowableExists)
9360 // Check the throwable if it has buckets - this could be the scenario
9361 // of native code calling into a non-default domain and thus, have an AD
9362 // transition in between that could reraise the exception but that would
9363 // never be seen by our exception handler. Thus, there wont be any tracker
9364 // or tracker state.
9366 // Invocation of entry point on WLC via reverse pinvoke is an example.
9367 if (!fIsThrowablePreallocated)
9369 fDoWeHaveWatsonBuckets = ((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent();
9370 if (!fDoWeHaveWatsonBuckets)
9372 // If buckets are not present, then we may have IP to capture the buckets from.
9373 fDoWeHaveWatsonBuckets = ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent();
9378 // Get the watson bucket tracker for the preallocated exception
9379 PTR_EHWatsonBucketTracker pCurWBTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9381 // We would have buckets if we have the IP
9382 if (pCurWBTracker && (pCurWBTracker->RetrieveWatsonBucketIp() != NULL))
9384 fDoWeHaveWatsonBuckets = TRUE;
9389 if (fDoWeHaveWatsonBuckets)
9391 // Get the UE Watson bucket tracker
9392 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
9394 // Clear any existing information
9395 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9397 if (fIsThrowablePreallocated)
9399 // Get the watson bucket tracker for the preallocated exception
9400 PTR_EHWatsonBucketTracker pCurWBTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oThrowable, FALSE);
9402 if (pCurWBTracker != NULL)
9404 // We should be having an IP for this exception at this point
9405 _ASSERTE(pCurWBTracker->RetrieveWatsonBucketIp() != NULL);
9407 // Copy the existing bucketing details to the UE tracker
9408 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pCurWBTracker));
9410 // Get the buckets if we dont already have them since we
9411 // dont want to overwrite existing bucket information (e.g.
9412 // from an AD transition)
9413 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9415 pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oThrowable);
9416 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9418 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Collected watson bucket information for preallocated exception\n"));
9422 // If we are here, then one of the following could have happened:
9424 // 1) pCurWBTracker had buckets but we couldnt copy them over to pUEWatsonBucketTracker due to OOM, or
9425 // 2) pCurWBTracker's IP was in native code; thus pUEWatsonBucketTracker->CaptureUnhandledInfoForWatson()
9426 // couldnt get us the watson buckets.
9427 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Unable to collect watson bucket information for preallocated exception due to OOM or IP being in native code.\n"));
9433 // We likely had an OOM earlier (while copying the bucket information) if we are here
9434 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Watson bucket tracker for preallocated exception not found.\n"));
9439 // Throwable is not preallocated - get the bucket details from it for use by Watson
9440 _ASSERTE_MSG(((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() ||
9441 ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent(),
9442 "How come we dont have watson buckets (or IP) for a non-preallocated exception in the UEF?");
9444 if ((((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent() == FALSE) &&
9445 ((EXCEPTIONREF)gc.oThrowable)->IsIPForWatsonBucketsPresent())
9447 // Capture the buckets using the IP we have.
9448 SetupWatsonBucketsForNonPreallocatedExceptions(gc.oThrowable);
9451 if (((EXCEPTIONREF)gc.oThrowable)->AreWatsonBucketsPresent())
9453 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oThrowable);
9456 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9458 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: Unable to copy watson buckets from regular exception throwable (%p), likely due to OOM.\n",
9459 OBJECTREFToObject(gc.oThrowable)));
9465 // We dont have the watson buckets; exception was in native code that we dont care about
9466 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForUEF: We dont have watson buckets - likely an exception in native code.\n"));
9470 #endif // !DACCESS_COMPILE
9473 // Given a throwable, this function will return a BOOL indicating
9474 // if it corresponds to any of the following thread abort exception
9477 // 1) Regular allocated ThreadAbortException
9478 // 2) Preallocated ThreadAbortException
9479 // 3) Preallocated RudeThreadAbortException
9480 BOOL IsThrowableThreadAbortException(OBJECTREF oThrowable)
9482 #ifndef DACCESS_COMPILE
9488 PRECONDITION(GetThread() != NULL);
9489 PRECONDITION(oThrowable != NULL);
9493 BOOL fIsTAE = FALSE;
9497 OBJECTREF oThrowable;
9499 ZeroMemory(&gc, sizeof(gc));
9500 GCPROTECT_BEGIN(gc);
9502 gc.oThrowable = oThrowable;
9504 fIsTAE = (IsExceptionOfType(kThreadAbortException,&(gc.oThrowable)) || // regular TAE
9505 ((g_pPreallocatedThreadAbortException != NULL) &&
9506 (gc.oThrowable == CLRException::GetPreallocatedThreadAbortException())) ||
9507 ((g_pPreallocatedRudeThreadAbortException != NULL) &&
9508 (gc.oThrowable == CLRException::GetPreallocatedRudeThreadAbortException())));
9514 #else // DACCESS_COMPILE
9516 #endif // !DACCESS_COMPILE
9519 // Given a throwable, this function will walk the exception tracker
9520 // list to return the tracker, if available, corresponding to the preallocated
9521 // exception object.
9523 // The caller can also specify the starting EHTracker to walk the list from.
9524 // If not specified, this will default to the current exception tracker active
9526 #if defined(WIN64EXCEPTIONS)
9527 PTR_ExceptionTracker GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9528 PTR_ExceptionTracker pStartingEHTracker)
9530 PTR_ExInfo GetEHTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9531 PTR_ExInfo pStartingEHTracker)
9533 #error Unsupported platform
9541 PRECONDITION(GetThread() != NULL);
9542 PRECONDITION(oPreAllocThrowable != NULL);
9543 PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable));
9544 PRECONDITION(IsWatsonEnabled());
9548 // Get the reference to the current exception tracker
9549 #if defined(WIN64EXCEPTIONS)
9550 PTR_ExceptionTracker pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
9552 PTR_ExInfo pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
9553 #else // !(_WIN64 || _TARGET_X86_)
9554 #error Unsupported platform
9557 BOOL fFoundTracker = FALSE;
9561 OBJECTREF oPreAllocThrowable;
9563 ZeroMemory(&gc, sizeof(gc));
9564 GCPROTECT_BEGIN(gc);
9566 gc.oPreAllocThrowable = oPreAllocThrowable;
9568 // Start walking the list to find the tracker correponding
9569 // to the preallocated exception object.
9570 while (pEHTracker != NULL)
9572 if (pEHTracker->GetThrowable() == gc.oPreAllocThrowable)
9574 // found the tracker - break out.
9575 fFoundTracker = TRUE;
9579 // move to the previous tracker...
9580 pEHTracker = pEHTracker->GetPreviousExceptionTracker();
9585 return fFoundTracker ? pEHTracker : NULL;
9588 // This function will return the pointer to EHWatsonBucketTracker corresponding to the
9589 // preallocated exception object. If none is found, it will return NULL.
9590 PTR_EHWatsonBucketTracker GetWatsonBucketTrackerForPreallocatedException(OBJECTREF oPreAllocThrowable,
9591 BOOL fCaptureBucketsIfNotPresent,
9592 BOOL fStartSearchFromPreviousTracker /*= FALSE*/)
9594 #ifndef DACCESS_COMPILE
9600 PRECONDITION(GetThread() != NULL);
9601 PRECONDITION(oPreAllocThrowable != NULL);
9602 PRECONDITION(CLRException::IsPreallocatedExceptionObject(oPreAllocThrowable));
9603 PRECONDITION(IsWatsonEnabled());
9607 PTR_EHWatsonBucketTracker pWBTracker = NULL;
9611 OBJECTREF oPreAllocThrowable;
9613 ZeroMemory(&gc, sizeof(gc));
9614 GCPROTECT_BEGIN(gc);
9616 gc.oPreAllocThrowable = oPreAllocThrowable;
9618 // Before doing anything, check if this is a thread abort exception. If it is,
9619 // then simply return the reference to the UE watson bucket tracker since it
9620 // tracks the bucketing details for all types of TAE.
9621 if (IsThrowableThreadAbortException(gc.oPreAllocThrowable))
9623 pWBTracker = GetThread()->GetExceptionState()->GetUEWatsonBucketTracker();
9624 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Setting UE Watson Bucket Tracker to be returned for preallocated ThreadAbortException.\n"));
9629 // Find the reference to the exception tracker corresponding to the preallocated exception,
9630 // starting the search from the current exception tracker (2nd arg of NULL specifies that).
9631 #if defined(WIN64EXCEPTIONS)
9632 PTR_ExceptionTracker pEHTracker = NULL;
9633 PTR_ExceptionTracker pPreviousEHTracker = NULL;
9636 PTR_ExInfo pEHTracker = NULL;
9637 PTR_ExInfo pPreviousEHTracker = NULL;
9638 #else // !(_WIN64 || _TARGET_X86_)
9639 #error Unsupported platform
9642 if (fStartSearchFromPreviousTracker)
9644 // Get the exception tracker previous to the current one
9645 pPreviousEHTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker()->GetPreviousExceptionTracker();
9647 // If there is no previous tracker to start from, then simply abort the search attempt.
9648 // If we couldnt find the exception tracker, then buckets are not available
9649 if (pPreviousEHTracker == NULL)
9651 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Couldnt find the previous EHTracker to start the search from.\n"));
9657 pEHTracker = GetEHTrackerForPreallocatedException(gc.oPreAllocThrowable, pPreviousEHTracker);
9659 // If we couldnt find the exception tracker, then buckets are not available
9660 if (pEHTracker == NULL)
9662 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Couldnt find EHTracker for preallocated exception object.\n"));
9667 // Get the Watson Bucket Tracker from the exception tracker
9668 pWBTracker = pEHTracker->GetWatsonBucketTracker();
9671 _ASSERTE(pWBTracker != NULL);
9673 // Incase of an OOM, we may not have an IP in the Watson bucket tracker. A scenario
9674 // would be default domain calling to AD 2 that calls into AD 3.
9676 // AD 3 has an exception that is represented by a preallocated exception object. The
9677 // exception goes unhandled and reaches AD2/AD3 transition boundary. The bucketing details
9678 // from AD3 are copied to UETracker and once the exception is reraised in AD2, we will
9679 // enter SetupInitialThrowBucketingDetails to copy the bucketing details to the active
9680 // exception tracker.
9682 // This copy operation could fail due to OOM and the active exception tracker in AD 2,
9683 // for the preallocated exception object, will not have any bucketing details. If the
9684 // exception remains unhandled in AD 2, then just before it reaches DefDomain/AD2 boundary,
9685 // we will attempt to capture the bucketing details in AppDomainTransitionExceptionFilter,
9686 // that will bring us here.
9688 // In such a case, the active exception tracker will not have any bucket details for the
9689 // preallocated exception. In such a case, if the IP does not exist, we will return NULL
9690 // indicating that we couldnt find the Watson bucket tracker, since returning a tracker
9691 // that does not have any bucketing details will be of no use to the caller.
9692 if (pWBTracker->RetrieveWatsonBucketIp() != NULL)
9694 // Check if the buckets exist or not..
9695 PTR_VOID pBuckets = pWBTracker->RetrieveWatsonBuckets();
9697 // If they dont exist and we have been asked to collect them,
9699 if (pBuckets == NULL)
9701 if (fCaptureBucketsIfNotPresent)
9703 pWBTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, GetThread(), &gc.oPreAllocThrowable);
9705 // Check if we have the buckets now
9706 if (pWBTracker->RetrieveWatsonBuckets() != NULL)
9708 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Captured watson buckets for preallocated exception object.\n"));
9712 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Unable to capture watson buckets for preallocated exception object due to OOM.\n"));
9717 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Found IP but no buckets for preallocated exception object.\n"));
9722 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Buckets already exist for preallocated exception object.\n"));
9727 LOG((LF_EH, LL_INFO100, "GetWatsonBucketTrackerForPreallocatedException - Returning NULL EHWatsonBucketTracker since bucketing IP does not exist. This is likely due to an earlier OOM.\n"));
9735 // Return the Watson bucket tracker
9737 #else // DACCESS_COMPILE
9739 #endif // !DACCESS_COMPILE
9742 // Given an exception object, this function will attempt to look up
9743 // the watson buckets for it and set them up against the thread
9744 // for use by FailFast mechanism.
9745 // Return TRUE when it succeeds or Waston is disabled on CoreCLR
9746 // Return FALSE when refException neither has buckets nor has inner exception
9747 BOOL SetupWatsonBucketsForFailFast(EXCEPTIONREF refException)
9749 BOOL fResult = TRUE;
9751 #ifndef DACCESS_COMPILE
9752 // On CoreCLR, Watson may not be enabled. Thus, we should
9754 if (!IsWatsonEnabled())
9764 PRECONDITION(GetThread() != NULL);
9765 PRECONDITION(refException != NULL);
9766 PRECONDITION(IsWatsonEnabled());
9770 // Switch to COOP mode
9775 OBJECTREF refException;
9776 OBJECTREF oInnerMostExceptionThrowable;
9778 ZeroMemory(&gc, sizeof(gc));
9779 GCPROTECT_BEGIN(gc);
9780 gc.refException = refException;
9782 Thread *pThread = GetThread();
9784 // If we dont already have the bucketing details for the exception
9785 // being thrown, then get them.
9786 ThreadExceptionState *pExState = pThread->GetExceptionState();
9788 // Check if the exception object is preallocated or not
9789 BOOL fIsPreallocatedException = CLRException::IsPreallocatedExceptionObject(gc.refException);
9791 // Get the WatsonBucketTracker where bucketing details will be copied to
9792 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
9794 // Check if this is a thread abort exception of any kind.
9795 // See IsThrowableThreadAbortException implementation for details.
9796 BOOL fIsThreadAbortException = IsThrowableThreadAbortException(gc.refException);
9798 if (fIsPreallocatedException)
9800 // If the exception being used to FailFast is preallocated,
9801 // then it cannot have any inner exception. Thus, try to
9802 // find the watson bucket tracker corresponding to this exception.
9804 // Also, capture the buckets if we dont have them already.
9805 PTR_EHWatsonBucketTracker pTargetWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.refException, TRUE);
9806 if ((pTargetWatsonBucketTracker != NULL) && (!fIsThreadAbortException))
9808 // Buckets are not captured proactively for preallocated exception objects. We only
9809 // save the IP in the watson bucket tracker (see SetupInitialThrowBucketingDetails for
9812 // Thus, if, say in DefDomain, a preallocated exception is thrown and we enter
9813 // the catch block and invoke the FailFast API with the reference to the preallocated
9814 // exception object, we will have the IP but not the buckets. In such a case,
9815 // capture the buckets before proceeding ahead.
9816 if (pTargetWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9818 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Collecting watson bucket details for preallocated exception.\n"));
9819 pTargetWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.refException);
9822 // Copy the buckets to the UE tracker
9823 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9824 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*pTargetWatsonBucketTracker);
9825 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9827 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Collected watson bucket details for preallocated exception in UE tracker.\n"));
9831 // If we are here, then the copy operation above had an OOM, resulting
9832 // in no buckets for us.
9833 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Unable to collect watson bucket details for preallocated exception due to out of memory.\n"));
9835 // Make sure the tracker is clean.
9836 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9841 // For TAE, UE watson bucket tracker is the one that tracks the buckets. It *may*
9842 // not have the bucket details if FailFast is being invoked from outside the
9843 // managed EH clauses. But if invoked from within the active EH clause for the exception,
9844 // UETracker will have the bucketing details (see SetupInitialThrowBucketingDetails for details).
9845 if (fIsThreadAbortException && (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL))
9847 _ASSERTE(pTargetWatsonBucketTracker == pUEWatsonBucketTracker);
9848 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - UE tracker already watson bucket details for preallocated thread abort exception.\n"));
9852 LOG((LF_EH, LL_INFO100, "SetupWatsonBucketsForFailFast - Unable to find bucket details for preallocated %s exception.\n",
9853 fIsThreadAbortException?"rude/thread abort":""));
9855 // Make sure the tracker is clean.
9856 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9862 // Since the exception object is not preallocated, start by assuming
9863 // that we dont need to check it for watson buckets
9864 BOOL fCheckThrowableForWatsonBuckets = FALSE;
9866 // Get the innermost exception object (if any)
9867 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.refException)->GetBaseException();
9868 if (gc.oInnerMostExceptionThrowable != NULL)
9870 if (CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable))
9872 // If the inner most exception being used to FailFast is preallocated,
9873 // try to find the watson bucket tracker corresponding to it.
9875 // Also, capture the buckets if we dont have them already.
9876 PTR_EHWatsonBucketTracker pTargetWatsonBucketTracker =
9877 GetWatsonBucketTrackerForPreallocatedException(gc.oInnerMostExceptionThrowable, TRUE);
9879 if (pTargetWatsonBucketTracker != NULL)
9881 if (pTargetWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
9883 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Capturing Watson bucket details for preallocated inner exception.\n"));
9884 pTargetWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oInnerMostExceptionThrowable);
9887 // Copy the details to the UE tracker
9888 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9889 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*pTargetWatsonBucketTracker);
9890 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9892 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Watson bucket details collected for preallocated inner exception.\n"));
9896 // If we are here, copy operation failed likely due to OOM
9897 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy watson bucket details for preallocated inner exception.\n"));
9899 // Keep the UETracker clean
9900 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9905 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to find bucket details for preallocated inner exception.\n"));
9907 // Keep the UETracker clean
9908 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9910 // Since we couldnt find the watson bucket tracker for the the inner most exception,
9911 // try to look for the buckets in the throwable.
9912 fCheckThrowableForWatsonBuckets = TRUE;
9917 // Inner most exception is not preallocated.
9919 // If it has the IP but not the buckets, then capture them now.
9920 if ((((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() == FALSE) &&
9921 (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent()))
9923 SetupWatsonBucketsForNonPreallocatedExceptions(gc.oInnerMostExceptionThrowable);
9926 // If it has the buckets, copy them over to the current Watson bucket tracker
9927 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
9929 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9930 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.oInnerMostExceptionThrowable);
9931 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9933 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Got watson buckets from regular innermost exception.\n"));
9937 // Copy operation can fail due to OOM
9938 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy watson buckets from regular innermost exception, likely due to OOM.\n"));
9943 // Since the inner most exception didnt have the buckets,
9944 // try to look for them in the throwable
9945 fCheckThrowableForWatsonBuckets = TRUE;
9946 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Neither exception object nor its inner exception has watson buckets.\n"));
9952 // There is no innermost exception - try to look for buckets
9954 fCheckThrowableForWatsonBuckets = TRUE;
9955 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Innermost exception does not exist\n"));
9958 if (fCheckThrowableForWatsonBuckets)
9960 // Since we have not found buckets anywhere, try to look for them
9961 // in the throwable.
9962 if ((((EXCEPTIONREF)gc.refException)->AreWatsonBucketsPresent() == FALSE) &&
9963 (((EXCEPTIONREF)gc.refException)->IsIPForWatsonBucketsPresent()))
9965 // Capture the buckets from the IP.
9966 SetupWatsonBucketsForNonPreallocatedExceptions(gc.refException);
9969 if (((EXCEPTIONREF)gc.refException)->AreWatsonBucketsPresent())
9971 // Copy the buckets to the current watson bucket tracker
9972 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
9973 pUEWatsonBucketTracker->CopyBucketsFromThrowable(gc.refException);
9974 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
9976 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Watson buckets copied from the exception object.\n"));
9980 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Unable to copy Watson buckets copied from the exception object, likely due to OOM.\n"));
9986 LOG((LF_EH, LL_INFO1000, "SetupWatsonBucketsForFailFast - Exception object neither has buckets nor has inner exception.\n"));
9993 #endif // !DACCESS_COMPILE
9998 // This function will setup the bucketing details in the exception
9999 // tracker or the throwable, if they are not already setup.
10001 // This is called when an exception is thrown (or raised):
10003 // 1) from outside the confines of managed EH clauses, OR
10004 // 2) from within the confines of managed EH clauses but the
10005 // exception does not have bucketing details with it, OR
10006 // 3) When an exception is reraised at AD transition boundary
10007 // after it has been marshalled over to the returning AD.
10008 void SetupInitialThrowBucketDetails(UINT_PTR adjustedIp)
10010 #ifndef DACCESS_COMPILE
10012 // On CoreCLR, Watson may not be enabled. Thus, we should
10014 if (!IsWatsonEnabled())
10024 PRECONDITION(GetThread() != NULL);
10025 PRECONDITION(!(GetThread()->GetExceptionState()->GetFlags()->GotWatsonBucketDetails()));
10026 PRECONDITION(adjustedIp != NULL);
10027 PRECONDITION(IsWatsonEnabled());
10031 Thread *pThread = GetThread();
10033 // If we dont already have the bucketing details for the exception
10034 // being thrown, then get them.
10035 ThreadExceptionState *pExState = pThread->GetExceptionState();
10037 // Ensure that the exception tracker exists
10038 _ASSERTE(pExState->GetCurrentExceptionTracker() != NULL);
10040 // Switch to COOP mode
10043 // Get the throwable for the exception being thrown
10046 OBJECTREF oCurrentThrowable;
10047 OBJECTREF oInnerMostExceptionThrowable;
10049 ZeroMemory(&gc, sizeof(gc));
10051 GCPROTECT_BEGIN(gc);
10053 gc.oCurrentThrowable = pExState->GetThrowable();
10055 // Check if the exception object is preallocated or not
10056 BOOL fIsPreallocatedException = CLRException::IsPreallocatedExceptionObject(gc.oCurrentThrowable);
10058 // Get the WatsonBucketTracker for the current exception
10059 PTR_EHWatsonBucketTracker pWatsonBucketTracker = pExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker();
10061 // Get the innermost exception object (if any)
10062 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.oCurrentThrowable)->GetBaseException();
10064 // By default, assume that no watson bucketing details are available and inner exception
10065 // is not preallocated
10066 BOOL fAreBucketingDetailsPresent = FALSE;
10067 BOOL fIsInnerExceptionPreallocated = FALSE;
10069 // Check if this is a thread abort exception of any kind. See IsThrowableThreadAbortException implementation for details.
10070 // We shouldnt use the thread state as well to determine if it is a TAE since, in cases like throwing a cached exception
10071 // as part of type initialization failure, we could throw a TAE but the thread will not be in abort state (which is expected).
10072 BOOL fIsThreadAbortException = IsThrowableThreadAbortException(gc.oCurrentThrowable);
10074 // If we are here, then this was a new exception raised
10075 // from outside the managed EH clauses (fault/finally/catch).
10077 // The throwable *may* have the bucketing details already
10078 // if this exception was raised when it was crossing over
10079 // an AD transition boundary. Those are stored in UE watson bucket
10080 // tracker by AppDomainTransitionExceptionFilter.
10081 if (fIsPreallocatedException)
10083 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10084 fAreBucketingDetailsPresent = ((pUEWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL) &&
10085 (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL));
10087 // If they are present, copy them over to the watson tracker for the exception
10088 // being processed.
10089 if (fAreBucketingDetailsPresent)
10092 // Under OOM scenarios, its possible that when we are raising a threadabort,
10093 // the throwable may get converted to preallocated OOM object when RaiseTheExceptionInternalOnly
10094 // invokes Thread::SafeSetLastThrownObject. We check if this is the current case and use it in
10095 // our validation below.
10096 BOOL fIsPreallocatedOOMExceptionForTA = FALSE;
10097 if ((!fIsThreadAbortException) && pUEWatsonBucketTracker->CapturedForThreadAbort())
10099 fIsPreallocatedOOMExceptionForTA = (gc.oCurrentThrowable == CLRException::GetPreallocatedOutOfMemoryException());
10100 if (fIsPreallocatedOOMExceptionForTA)
10102 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Got preallocated OOM throwable for buckets captured for thread abort.\n"));
10106 // These should have been captured at AD transition OR
10107 // could be bucketing details of preallocated [rude] thread abort exception.
10108 _ASSERTE(pUEWatsonBucketTracker->CapturedAtADTransition() ||
10109 ((fIsThreadAbortException || fIsPreallocatedOOMExceptionForTA) && pUEWatsonBucketTracker->CapturedForThreadAbort()));
10111 if (!fIsThreadAbortException)
10113 // The watson bucket tracker for the exceptiong being raised should be empty at this point
10114 // since we are here because of a cross AD reraise of the original exception.
10115 _ASSERTE((pWatsonBucketTracker->RetrieveWatsonBucketIp() == NULL) && (pWatsonBucketTracker->RetrieveWatsonBuckets() == NULL));
10117 // Copy the buckets over to it
10118 pWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pUEWatsonBucketTracker));
10119 if (pWatsonBucketTracker->RetrieveWatsonBuckets() == NULL)
10121 // If we dont have buckets after the copy operation, its due to us running out of
10123 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Unable to copy watson buckets from cross AD rethrow, likely due to out of memory.\n"));
10127 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson buckets from cross AD rethrow.\n"));
10132 // Thread abort watson bucket details are already present in the
10133 // UE watson bucket tracker.
10134 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Already have watson buckets for preallocated thread abort reraise.\n"));
10137 else if (fIsThreadAbortException)
10139 // This is a preallocated thread abort exception.
10140 UINT_PTR ip = pUEWatsonBucketTracker->RetrieveWatsonBucketIp();
10143 // Since we have the IP, assert that this was the one setup
10144 // for ThreadAbort. This is for the reraise scenario where
10145 // the original exception was non-preallocated TA but the
10146 // reraise resulted in preallocated TA.
10148 // In this case, we will update the ip to be used as the
10149 // one we have. The control flow below will automatically
10151 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort());
10153 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setting an existing IP (%p) to be used for capturing buckets for preallocated thread abort.\n", ip));
10158 if (!fAreBucketingDetailsPresent || !fIsThreadAbortException)
10160 // Clear the UE Watson bucket tracker so that its usable
10161 // in future. We dont clear this for ThreadAbort since
10162 // the UE watson bucket tracker carries bucketing details
10163 // for the same, unless the UE tracker is not containing them
10165 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10170 // The exception object is not preallocated
10171 fAreBucketingDetailsPresent = ((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent();
10172 if (!fAreBucketingDetailsPresent)
10174 // If buckets are not present, check if the bucketing IP is present.
10175 fAreBucketingDetailsPresent = ((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent();
10178 // If throwable does not have buckets and this is a thread abort exception,
10179 // then this maybe a reraise of the original thread abort.
10181 // We can also be here if an exception was caught at AppDomain transition and
10182 // in the returning domain, a non-preallocated TAE was raised. In such a case,
10183 // the UE tracker flags could indicate the exception is from AD transition.
10184 // This is similar to preallocated case above.
10186 // Check the UE Watson bucket tracker if it has the buckets and if it does,
10187 // copy them over to the current throwable.
10188 if (!fAreBucketingDetailsPresent && fIsThreadAbortException)
10190 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10191 UINT_PTR ip = pUEWatsonBucketTracker->RetrieveWatsonBucketIp();
10194 // Confirm that we had the buckets captured for thread abort
10195 _ASSERTE(pUEWatsonBucketTracker->CapturedForThreadAbort() || pUEWatsonBucketTracker->CapturedAtADTransition());
10197 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
10199 // Copy the buckets to the current throwable - CopyWatsonBucketsToThrowable
10200 // can throw in OOM. However, since the current function is called as part of
10201 // setting up the stack trace, where we bail out incase of OOM, we will do
10202 // no different here as well.
10203 BOOL fCopiedBuckets = TRUE;
10206 CopyWatsonBucketsToThrowable(pUEWatsonBucketTracker->RetrieveWatsonBuckets());
10207 _ASSERTE(((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent());
10211 fCopiedBuckets = FALSE;
10213 EX_END_CATCH(SwallowAllExceptions);
10215 if (fCopiedBuckets)
10217 // Since the throwable has the buckets, set the flag that indicates so
10218 fAreBucketingDetailsPresent = TRUE;
10219 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setup watson buckets for thread abort reraise.\n"));
10224 // Copy the faulting IP from the UE tracker to the exception object. This was setup in COMPlusCheckForAbort
10225 // for non-preallocated exceptions.
10226 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(ip);
10227 fAreBucketingDetailsPresent = TRUE;
10228 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Setup watson bucket IP for thread abort reraise.\n"));
10233 // Clear the UE Watson bucket tracker so that its usable
10235 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10236 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Didnt find watson buckets for thread abort - likely being raised.\n"));
10242 if (fAreBucketingDetailsPresent)
10244 // Since we already have the buckets, simply bail out
10245 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Already had watson ip/buckets.\n"));
10249 // Check if an inner most exception exists and if it does, examine
10250 // it for watson bucketing details.
10251 if (gc.oInnerMostExceptionThrowable != NULL)
10253 // Preallocated exception objects do not have inner exception objects.
10254 // Thus, if we are here, then the current throwable cannot be
10255 // a preallocated exception object.
10256 _ASSERTE(!fIsPreallocatedException);
10258 fIsInnerExceptionPreallocated = CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable);
10260 // If we are here, then this was a "throw" with inner exception
10261 // outside of any managed EH clauses.
10263 // If the inner exception object is preallocated, then we will need to create the
10264 // watson buckets since we are outside the managed EH clauses with no exception tracking
10265 // information relating to the inner exception.
10267 // But if the inner exception object was not preallocated, create new watson buckets
10268 // only if inner exception does not have them.
10269 if (fIsInnerExceptionPreallocated)
10271 fAreBucketingDetailsPresent = FALSE;
10275 // Do we have either the IP for Watson buckets or the buckets themselves?
10276 fAreBucketingDetailsPresent = (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10277 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10281 if (!fAreBucketingDetailsPresent)
10283 // Collect the bucketing details since they are not already present
10284 pWatsonBucketTracker->SaveIpForWatsonBucket(adjustedIp);
10286 if (!fIsPreallocatedException || fIsThreadAbortException)
10288 if (!fIsPreallocatedException)
10290 // Save the IP for Watson bucketing in the exception object for non-preallocated exception
10292 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(adjustedIp);
10294 // Save the IP in the UE tracker as well for TAE if an abort is in progress
10295 // since when we attempt reraise, the exception object is not available. Otherwise,
10296 // treat the exception like a regular non-preallocated exception and not do anything else.
10297 if (fIsThreadAbortException && pThread->IsAbortInitiated())
10299 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10301 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10302 pUEWatsonBucketTracker->SaveIpForWatsonBucket(adjustedIp);
10304 // Set the flag that we captured the IP for Thread abort
10305 DEBUG_STMT(pUEWatsonBucketTracker->SetCapturedForThreadAbort());
10306 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved bucket IP for initial thread abort raise.\n"));
10311 // Create the buckets proactively for preallocated threadabort exception
10312 pWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oCurrentThrowable);
10313 PTR_VOID pUnmanagedBuckets = pWatsonBucketTracker->RetrieveWatsonBuckets();
10314 if(pUnmanagedBuckets != NULL)
10316 // Copy the details over to the UE Watson bucket tracker so that we can use them if the exception
10317 // is "reraised" after invoking the catch block.
10319 // Since we can be here for preallocated threadabort exception when UE Tracker is simply
10320 // carrying the IP (that has been copied to pWatsonBucketTracker and buckets captured for it),
10321 // we will need to clear UE tracker so that we can copy over the captured buckets.
10322 PTR_EHWatsonBucketTracker pUEWatsonBucketTracker = pExState->GetUEWatsonBucketTracker();
10323 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10325 // Copy over the buckets from the current tracker that captured them.
10326 pUEWatsonBucketTracker->CopyEHWatsonBucketTracker(*(pWatsonBucketTracker));
10328 // Buckets should be present now (unless the copy operation had an OOM)
10329 if (pUEWatsonBucketTracker->RetrieveWatsonBuckets() != NULL)
10331 // Set the flag that we captured buckets for Thread abort
10332 DEBUG_STMT(pUEWatsonBucketTracker->SetCapturedForThreadAbort());
10333 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved buckets for Watson Bucketing for initial thread abort raise.\n"));
10337 // If we are here, then the bucket copy operation (above) failed due to OOM.
10338 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Unable to save buckets for Watson Bucketing for initial thread abort raise, likely due to OOM.\n"));
10339 pUEWatsonBucketTracker->ClearWatsonBucketDetails();
10344 // Watson helper function can bail out on us under OOM scenarios and return a NULL.
10345 // We cannot do much in such a case.
10346 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - No buckets were captured and returned to us for initial thread abort raise. Likely encountered an OOM.\n"));
10349 // Clear the buckets since we no longer need them
10350 pWatsonBucketTracker->ClearWatsonBucketDetails();
10355 // We have already saved the throw site IP for bucketing the non-ThreadAbort preallocated exceptions
10356 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Saved IP (%p) for Watson Bucketing for a preallocated exception\n", adjustedIp));
10361 // The inner exception object should be having either the IP for watson bucketing or the buckets themselves.
10362 // We shall copy over, whatever is available, to the current exception object.
10363 _ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
10364 _ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10365 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10367 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
10371 // Copy the bucket details from innermost exception to the current exception object.
10372 CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
10377 EX_END_CATCH(SwallowAllExceptions);
10379 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket details from the innermost exception\n"));
10383 // Copy the IP to the current exception object
10384 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
10385 LOG((LF_EH, LL_INFO100, "SetupInitialThrowBucketDetails - Copied watson bucket IP from the innermost exception\n"));
10390 // Set the flag that we have got the bucketing details
10391 pExState->GetFlags()->SetGotWatsonBucketDetails();
10395 #endif // !DACCESS_COMPILE
10398 // This function is a wrapper to copy the watson bucket byte[] from the specified
10399 // throwable to the current throwable.
10400 void CopyWatsonBucketsFromThrowableToCurrentThrowable(OBJECTREF oThrowableFrom)
10402 #ifndef DACCESS_COMPILE
10409 PRECONDITION(oThrowableFrom != NULL);
10410 PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
10411 PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
10412 PRECONDITION(IsWatsonEnabled());
10418 OBJECTREF oThrowableFrom;
10421 ZeroMemory(&_gc, sizeof(_gc));
10422 GCPROTECT_BEGIN(_gc);
10423 _gc.oThrowableFrom = oThrowableFrom;
10425 // Copy the watson buckets to the current throwable by NOT passing
10426 // the second argument that will default to NULL.
10428 // CopyWatsonBucketsBetweenThrowables will pass that NULL to
10429 // CopyWatsonBucketsToThrowables that will make it copy the buckets
10430 // to the current throwable.
10431 CopyWatsonBucketsBetweenThrowables(_gc.oThrowableFrom);
10435 #endif // !DACCESS_COMPILE
10438 // This function will copy the watson bucket byte[] from the source
10439 // throwable to the destination throwable.
10441 // If the destination throwable is NULL, it will result in the buckets
10442 // being copied to the current throwable.
10443 void CopyWatsonBucketsBetweenThrowables(OBJECTREF oThrowableFrom, OBJECTREF oThrowableTo /*=NULL*/)
10445 #ifndef DACCESS_COMPILE
10452 PRECONDITION(oThrowableFrom != NULL);
10453 PRECONDITION(!CLRException::IsPreallocatedExceptionObject(oThrowableFrom));
10454 PRECONDITION(((EXCEPTIONREF)oThrowableFrom)->AreWatsonBucketsPresent());
10455 PRECONDITION(IsWatsonEnabled());
10459 BOOL fRetVal = FALSE;
10465 OBJECTREF oWatsonBuckets;
10468 ZeroMemory(&_gc, sizeof(_gc));
10469 GCPROTECT_BEGIN(_gc);
10471 _gc.oFrom = oThrowableFrom;
10472 _gc.oTo = (oThrowableTo == NULL)?GetThread()->GetThrowable():oThrowableTo;
10473 _ASSERTE(_gc.oTo != NULL);
10475 // The target throwable to which Watson buckets are going to be copied
10476 // shouldnt be preallocated exception object.
10477 _ASSERTE(!CLRException::IsPreallocatedExceptionObject(_gc.oTo));
10479 // Size of a watson bucket
10480 DWORD size = sizeof(GenericModeBlock);
10482 // Create the managed byte[] to hold the bucket details
10483 _gc.oWatsonBuckets = AllocatePrimitiveArray(ELEMENT_TYPE_U1, size);
10484 if (_gc.oWatsonBuckets == NULL)
10486 // return failure if failed to create bucket array
10491 // Get the raw array data pointer of the source array
10492 U1ARRAYREF refSourceWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
10493 PTR_VOID pRawSourceWatsonBucketArray = dac_cast<PTR_VOID>(refSourceWatsonBucketArray->GetDataPtr());
10495 // Get the raw array data pointer to the destination array
10496 U1ARRAYREF refDestWatsonBucketArray = (U1ARRAYREF)_gc.oWatsonBuckets;
10497 PTR_VOID pRawDestWatsonBucketArray = dac_cast<PTR_VOID>(refDestWatsonBucketArray->GetDataPtr());
10499 // Deep copy the bucket information to the managed array
10500 memcpyNoGCRefs(pRawDestWatsonBucketArray, pRawSourceWatsonBucketArray, size);
10502 // Setup the managed field reference to point to the byte array.
10504 // The throwable, to which the buckets are being copied to, may be
10505 // having existing buckets (e.g. when TypeInitialization exception
10506 // maybe thrown again when attempt is made to load the originally
10509 // This is also possible if exception object is used as singleton
10510 // and thrown by multiple threads.
10511 if (((EXCEPTIONREF)_gc.oTo)->AreWatsonBucketsPresent())
10513 LOG((LF_EH, LL_INFO1000, "CopyWatsonBucketsBetweenThrowables: Throwable (%p) being copied to had previous buckets.\n", OBJECTREFToObject(_gc.oTo)));
10516 ((EXCEPTIONREF)_gc.oTo)->SetWatsonBucketReference(_gc.oWatsonBuckets);
10521 // We shouldn't be here when fRetVal is FALSE since failure to allocate the primitive
10522 // array should throw an OOM.
10526 #endif // !DACCESS_COMPILE
10529 // This function will copy the watson bucket information to the managed byte[] in
10530 // the specified managed exception object.
10532 // If throwable is not specified, it will be copied to the current throwable.
10534 // pUnmanagedBuckets is a pointer to native memory that cannot be affected by GC.
10535 BOOL CopyWatsonBucketsToThrowable(PTR_VOID pUnmanagedBuckets, OBJECTREF oTargetThrowable /*= NULL*/)
10537 #ifndef DACCESS_COMPILE
10544 PRECONDITION(GetThread() != NULL);
10545 PRECONDITION(pUnmanagedBuckets != NULL);
10546 PRECONDITION(!CLRException::IsPreallocatedExceptionObject((oTargetThrowable == NULL)?GetThread()->GetThrowable():oTargetThrowable));
10547 PRECONDITION(IsWatsonEnabled());
10551 BOOL fRetVal = TRUE;
10554 OBJECTREF oThrowable;
10555 OBJECTREF oWatsonBuckets;
10558 ZeroMemory(&_gc, sizeof(_gc));
10559 GCPROTECT_BEGIN(_gc);
10560 _gc.oThrowable = (oTargetThrowable == NULL)?GetThread()->GetThrowable():oTargetThrowable;
10562 // Throwable to which buckets should be copied to, must exist.
10563 _ASSERTE(_gc.oThrowable != NULL);
10565 // Size of a watson bucket
10566 DWORD size = sizeof(GenericModeBlock);
10568 _gc.oWatsonBuckets = AllocatePrimitiveArray(ELEMENT_TYPE_U1, size);
10569 if (_gc.oWatsonBuckets == NULL)
10571 // return failure if failed to create bucket array
10576 // Get the raw array data pointer
10577 U1ARRAYREF refWatsonBucketArray = (U1ARRAYREF)_gc.oWatsonBuckets;
10578 PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
10580 // Deep copy the bucket information to the managed array
10581 memcpyNoGCRefs(pRawWatsonBucketArray, pUnmanagedBuckets, size);
10583 // Setup the managed field reference to point to the byte array.
10585 // The throwable, to which the buckets are being copied to, may be
10586 // having existing buckets (e.g. when TypeInitialization exception
10587 // maybe thrown again when attempt is made to load the originally
10590 // This is also possible if exception object is used as singleton
10591 // and thrown by multiple threads.
10592 if (((EXCEPTIONREF)_gc.oThrowable)->AreWatsonBucketsPresent())
10594 LOG((LF_EH, LL_INFO1000, "CopyWatsonBucketsToThrowable: Throwable (%p) being copied to had previous buckets.\n", OBJECTREFToObject(_gc.oThrowable)));
10597 ((EXCEPTIONREF)_gc.oThrowable)->SetWatsonBucketReference(_gc.oWatsonBuckets);
10603 #else // DACCESS_COMPILE
10605 #endif // !DACCESS_COMPILE
10608 // This function will setup the bucketing information for nested exceptions
10609 // raised. These would be any exceptions thrown from within the confines of
10610 // managed EH clauses and include "rethrow" and "throw new ...".
10612 // This is called from within CLR's personality routine for managed
10613 // exceptions to preemptively setup the watson buckets from the ones that may
10614 // already exist. If none exist already, we will automatically endup in the
10615 // path (SetupInitialThrowBucketDetails) that will set up buckets for the
10616 // exception being thrown.
10617 void SetStateForWatsonBucketing(BOOL fIsRethrownException, OBJECTHANDLE ohOriginalException)
10619 #ifndef DACCESS_COMPILE
10621 // On CoreCLR, Watson may not be enabled. Thus, we should
10623 if (!IsWatsonEnabled())
10633 PRECONDITION(GetThread() != NULL);
10634 PRECONDITION(IsWatsonEnabled());
10638 // Switch to COOP mode
10643 OBJECTREF oCurrentThrowable;
10644 OBJECTREF oInnerMostExceptionThrowable;
10646 ZeroMemory(&gc, sizeof(gc));
10647 GCPROTECT_BEGIN(gc);
10649 Thread* pThread = GetThread();
10651 // Get the current exception state of the thread
10652 ThreadExceptionState* pCurExState = pThread->GetExceptionState();
10653 _ASSERTE(NULL != pCurExState);
10655 // Ensure that the exception tracker exists
10656 _ASSERTE(pCurExState->GetCurrentExceptionTracker() != NULL);
10658 // Get the current throwable
10659 gc.oCurrentThrowable = pThread->GetThrowable();
10660 _ASSERTE(gc.oCurrentThrowable != NULL);
10662 // Is the throwable a preallocated exception object?
10663 BOOL fIsPreallocatedExceptionObject = CLRException::IsPreallocatedExceptionObject(gc.oCurrentThrowable);
10665 // Copy the bucketing details from the original exception tracker if the current exception is a rethrow
10666 // AND the throwable is a preallocated exception object.
10668 // For rethrown non-preallocated exception objects, the throwable would already have the bucketing
10669 // details inside it.
10670 if (fIsRethrownException)
10672 if (fIsPreallocatedExceptionObject)
10674 // Get the WatsonBucket tracker for the original exception, starting search from the previous EH tracker.
10675 // This is required so that when a preallocated exception is rethrown, then the current tracker would have
10676 // the same throwable as the original exception but no bucketing details.
10678 // To ensure GetWatsonBucketTrackerForPreallocatedException uses the EH tracker corresponding to the original
10679 // exception to get the bucketing details, we pass TRUE as the third parameter.
10680 PTR_EHWatsonBucketTracker pPreallocWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oCurrentThrowable, FALSE, TRUE);
10681 if (pPreallocWatsonBucketTracker != NULL)
10683 if (!IsThrowableThreadAbortException(gc.oCurrentThrowable))
10685 // For non-thread abort preallocated exceptions, we copy the bucketing details
10686 // from their corresponding watson bucket tracker to the one corresponding to the
10687 // rethrow that is taking place.
10689 // Bucketing details for preallocated exception may not be present if the exception came
10690 // from across AD transition and we attempted to copy them over from the UETracker, when
10691 // the exception was reraised in the calling AD, and the copy operation failed due to OOM.
10693 // In such a case, when the reraised exception is caught and rethrown, we will not have
10694 // any bucketing details.
10695 if (NULL != pPreallocWatsonBucketTracker->RetrieveWatsonBucketIp())
10697 // Copy the bucketing details now
10698 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->CopyEHWatsonBucketTracker(*pPreallocWatsonBucketTracker);
10702 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Watson bucketing details for rethrown preallocated exception not found in the EH tracker corresponding to the original exception. This is likely due to a previous OOM.\n"));
10703 LOG((LF_EH, LL_INFO1000, ">>>>>>>>>>>>>>>>>>>>>>>>>> Original WatsonBucketTracker = %p\n", pPreallocWatsonBucketTracker));
10705 // Make the active tracker clear
10706 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->ClearWatsonBucketDetails();
10712 // For thread abort exceptions, the returned watson bucket tracker
10713 // would correspond to UE Watson bucket tracker and it will have
10714 // all the details.
10715 _ASSERTE(pPreallocWatsonBucketTracker == pCurExState->GetUEWatsonBucketTracker());
10721 // OOM can result in not having a Watson bucket tracker with valid bucketing details for a preallocated exception.
10722 // Thus, we may end up here. For details, see implementation of GetWatsonBucketTrackerForPreallocatedException.
10723 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Watson bucketing tracker for rethrown preallocated exception not found. This is likely due to a previous OOM.\n"));
10725 // Make the active tracker clear
10726 pCurExState->GetCurrentExceptionTracker()->GetWatsonBucketTracker()->ClearWatsonBucketDetails();
10731 // We dont need to do anything here since the throwable would already have the bucketing
10732 // details inside it. Simply assert that the original exception object is the same as the current throwable.
10734 // We cannot assert for Watson buckets since the original throwable may not have got them in
10735 // SetupInitialThrowBucketDetails due to OOM
10736 _ASSERTE((NULL != ohOriginalException) && (ObjectFromHandle(ohOriginalException) == gc.oCurrentThrowable));
10737 if ((((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent() == FALSE) &&
10738 (((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent() == FALSE))
10740 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Regular rethrown exception (%p) does not have Watson buckets, likely due to OOM.\n",
10741 OBJECTREFToObject(gc.oCurrentThrowable)));
10745 // Set the flag that we have bucketing details for the exception
10746 pCurExState->GetFlags()->SetGotWatsonBucketDetails();
10747 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using original exception details for Watson bucketing for rethrown exception.\n"));
10751 // If we are here, then an exception is being thrown from within the
10752 // managed EH clauses of fault, finally or catch, with an inner exception.
10754 // By default, we will create buckets based upon the exception being thrown unless
10755 // thrown exception has an inner exception that has got bucketing details
10756 BOOL fCreateBucketsForExceptionBeingThrown = TRUE;
10758 // Start off by assuming that inner exception object is not preallocated
10759 BOOL fIsInnerExceptionPreallocated = FALSE;
10761 // Reference to the WatsonBucket tracker for the inner exception, if it is preallocated
10762 PTR_EHWatsonBucketTracker pInnerExceptionWatsonBucketTracker = NULL;
10764 // Since this is a new exception being thrown, we will check if it has buckets already or not.
10765 // This is possible when Reflection throws TargetInvocationException with an inner exception
10766 // that is preallocated exception object. In such a case, we copy the inner exception details
10767 // to the TargetInvocationException object already. This is done in InvokeImpl in ReflectionInvocation.cpp.
10768 if (((EXCEPTIONREF)gc.oCurrentThrowable)->AreWatsonBucketsPresent() ||
10769 ((EXCEPTIONREF)gc.oCurrentThrowable)->IsIPForWatsonBucketsPresent())
10774 // If no buckets are present, then we will check if it has an innermost exception or not.
10775 // If it does, then we will make the exception being thrown use the bucketing details of the
10776 // innermost exception.
10778 // If there is no innermost exception or if one is present without bucketing details, then
10779 // we will have bucket details based upon the exception being thrown.
10781 // Get the innermost exception from the exception being thrown.
10782 gc.oInnerMostExceptionThrowable = ((EXCEPTIONREF)gc.oCurrentThrowable)->GetBaseException();
10783 if (gc.oInnerMostExceptionThrowable != NULL)
10785 fIsInnerExceptionPreallocated = CLRException::IsPreallocatedExceptionObject(gc.oInnerMostExceptionThrowable);
10787 // Preallocated exception objects do not have inner exception objects.
10788 // Thus, if we are here, then the current throwable cannot be
10789 // a preallocated exception object.
10790 _ASSERTE(!fIsPreallocatedExceptionObject);
10792 // Create the new buckets only if the innermost exception object
10793 // does not have them already.
10794 if (fIsInnerExceptionPreallocated)
10796 // If we are able to find the watson bucket tracker for the preallocated
10797 // inner exception, then we dont need to create buckets for throw site.
10798 pInnerExceptionWatsonBucketTracker = GetWatsonBucketTrackerForPreallocatedException(gc.oInnerMostExceptionThrowable, FALSE, TRUE);
10799 fCreateBucketsForExceptionBeingThrown = ((pInnerExceptionWatsonBucketTracker != NULL) &&
10800 (pInnerExceptionWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL)) ? FALSE : TRUE;
10804 // Since the inner exception object is not preallocated, create
10805 // watson buckets only if it does not have them.
10806 fCreateBucketsForExceptionBeingThrown = !(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10807 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10811 // If we are NOT going to create buckets for the thrown exception,
10812 // then copy them over from the inner exception object.
10814 // If we have to create the buckets for the thrown exception,
10815 // we wont do that now - it will be done in StackTraceInfo::AppendElement
10816 // when we get the IP for bucketing.
10817 if (!fCreateBucketsForExceptionBeingThrown)
10819 // Preallocated exception objects do not have inner exception objects.
10820 // Thus, if we are here, then the current throwable cannot be
10821 // a preallocated exception object.
10822 _ASSERTE(!fIsPreallocatedExceptionObject);
10824 if (fIsInnerExceptionPreallocated)
10827 // We should have the inner exception watson bucket tracker
10828 _ASSERTE((pInnerExceptionWatsonBucketTracker != NULL) && (pInnerExceptionWatsonBucketTracker->RetrieveWatsonBucketIp() != NULL));
10830 // Capture the buckets for the innermost exception if they dont already exist.
10831 // Since the current throwable cannot be preallocated (see the assert above),
10832 // copy the buckets to the throwable.
10833 PTR_VOID pInnerExceptionWatsonBuckets = pInnerExceptionWatsonBucketTracker->RetrieveWatsonBuckets();
10834 if (pInnerExceptionWatsonBuckets == NULL)
10836 // Capture the buckets since they dont exist
10837 pInnerExceptionWatsonBucketTracker->CaptureUnhandledInfoForWatson(TypeOfReportedError::UnhandledException, pThread, &gc.oInnerMostExceptionThrowable);
10838 pInnerExceptionWatsonBuckets = pInnerExceptionWatsonBucketTracker->RetrieveWatsonBuckets();
10841 if (pInnerExceptionWatsonBuckets == NULL)
10843 // Couldnt capture details like due to OOM
10844 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Preallocated inner-exception's WBTracker (%p) has no bucketing details for the thrown exception, likely due to OOM.\n", pInnerExceptionWatsonBucketTracker));
10848 // Copy the buckets to the current throwable
10849 BOOL fCopied = TRUE;
10852 fCopied = CopyWatsonBucketsToThrowable(pInnerExceptionWatsonBuckets);
10857 // Dont do anything if we fail to copy the buckets - this is no different than
10858 // the native watson helper functions failing under OOM
10861 EX_END_CATCH(SwallowAllExceptions);
10866 // Assert that the inner exception has the Watson buckets
10867 _ASSERTE(gc.oInnerMostExceptionThrowable != NULL);
10868 _ASSERTE(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent() ||
10869 ((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->IsIPForWatsonBucketsPresent());
10871 if (((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->AreWatsonBucketsPresent())
10873 // Copy the bucket information from the inner exception object to the current throwable
10876 CopyWatsonBucketsFromThrowableToCurrentThrowable(gc.oInnerMostExceptionThrowable);
10880 // Dont do anything if we fail to copy the buckets - this is no different than
10881 // the native watson helper functions failing under OOM
10883 EX_END_CATCH(SwallowAllExceptions);
10887 // Copy the IP for Watson bucketing to the exception object
10888 ((EXCEPTIONREF)gc.oCurrentThrowable)->SetIPForWatsonBuckets(((EXCEPTIONREF)gc.oInnerMostExceptionThrowable)->GetIPForWatsonBuckets());
10892 // Set the flag that we got bucketing details for the exception
10893 pCurExState->GetFlags()->SetGotWatsonBucketDetails();
10894 LOG((LF_EH, LL_INFO1000, "SetStateForWatsonBucketing - Using innermost exception details for Watson bucketing for thrown exception.\n"));
10901 #endif // !DACCESS_COMPILE
10904 // Constructor that will do the initialization of the object
10905 EHWatsonBucketTracker::EHWatsonBucketTracker()
10907 LIMITED_METHOD_CONTRACT;
10912 // Reset the fields to default values
10913 void EHWatsonBucketTracker::Init()
10915 LIMITED_METHOD_CONTRACT;
10917 m_WatsonUnhandledInfo.m_UnhandledIp = 0;
10918 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
10920 DEBUG_STMT(ResetFlags());
10922 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::Init - initializing watson bucket tracker (%p)\n", this));
10925 // This method copies the bucketing details from the specified throwable
10926 // to the current Watson Bucket tracker.
10927 void EHWatsonBucketTracker::CopyBucketsFromThrowable(OBJECTREF oThrowable)
10929 #ifndef DACCESS_COMPILE
10935 PRECONDITION(oThrowable != NULL);
10936 PRECONDITION(((EXCEPTIONREF)oThrowable)->AreWatsonBucketsPresent());
10937 PRECONDITION(IsWatsonEnabled());
10948 ZeroMemory(&_gc, sizeof(_gc));
10949 GCPROTECT_BEGIN(_gc);
10951 _gc.oFrom = oThrowable;
10953 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from throwable (%p) to tracker (%p)\n",
10954 OBJECTREFToObject(_gc.oFrom), this));
10956 // Watson bucket is a "GenericModeBlock" type. Set up an empty GenericModeBlock
10957 // to hold the bucket parameters.
10958 GenericModeBlock *pgmb = new (nothrow) GenericModeBlock;
10961 // If we are unable to allocate memory to hold the WatsonBucket, then
10962 // reset the IP and bucket pointer to NULL and bail out
10963 SaveIpForWatsonBucket(NULL);
10964 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
10968 // Get the raw array data pointer
10969 U1ARRAYREF refWatsonBucketArray = ((EXCEPTIONREF)_gc.oFrom)->GetWatsonBucketReference();
10970 PTR_VOID pRawWatsonBucketArray = dac_cast<PTR_VOID>(refWatsonBucketArray->GetDataPtr());
10972 // Copy over the details to our new allocation
10973 memcpyNoGCRefs(pgmb, pRawWatsonBucketArray, sizeof(GenericModeBlock));
10975 // and save the address where the buckets were copied
10976 _ASSERTE(m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL);
10977 m_WatsonUnhandledInfo.m_pUnhandledBuckets = pgmb;
10982 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copied Watson Buckets from throwable to (%p)\n",
10983 m_WatsonUnhandledInfo.m_pUnhandledBuckets));
10984 #endif // !DACCESS_COMPILE
10987 // This method copies the bucketing details from the specified Watson Bucket tracker
10988 // to the current one.
10989 void EHWatsonBucketTracker::CopyEHWatsonBucketTracker(const EHWatsonBucketTracker& srcTracker)
10991 #ifndef DACCESS_COMPILE
10997 PRECONDITION(m_WatsonUnhandledInfo.m_UnhandledIp == 0);
10998 PRECONDITION(m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL);
10999 PRECONDITION(IsWatsonEnabled());
11003 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copying bucketing details from %p to %p\n", &srcTracker, this));
11005 // Copy the tracking details over from the specified tracker
11006 SaveIpForWatsonBucket(srcTracker.m_WatsonUnhandledInfo.m_UnhandledIp);
11008 if (srcTracker.m_WatsonUnhandledInfo.m_pUnhandledBuckets != NULL)
11010 // To save the bucket information, we will need to memcpy.
11011 // This is to ensure that if the original watson bucket tracker
11012 // (for original exception) is released and its memory deallocated,
11013 // the new watson bucket tracker (for rethrown exception, for e.g.)
11014 // would still have all the bucket details.
11016 // Watson bucket is a "GenericModeBlock" type. Set up an empty GenericModeBlock
11017 // to hold the bucket parameters.
11018 GenericModeBlock *pgmb = new (nothrow) GenericModeBlock;
11021 // If we are unable to allocate memory to hold the WatsonBucket, then
11022 // reset the IP and bucket pointer to NULL and bail out
11023 SaveIpForWatsonBucket(NULL);
11024 m_WatsonUnhandledInfo.m_pUnhandledBuckets = NULL;
11026 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Not copying buckets due to out of memory.\n"));
11030 // Copy over the details to our new allocation
11031 memcpyNoGCRefs(pgmb, srcTracker.m_WatsonUnhandledInfo.m_pUnhandledBuckets, sizeof(GenericModeBlock));
11033 // and save the address where the buckets were copied
11034 m_WatsonUnhandledInfo.m_pUnhandledBuckets = pgmb;
11038 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CopyEHWatsonBucketTracker - Copied Watson Bucket to (%p)\n", m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11039 #endif // !DACCESS_COMPILE
11042 void EHWatsonBucketTracker::SaveIpForWatsonBucket(
11043 UINT_PTR ip) // The new IP.
11045 #ifndef DACCESS_COMPILE
11052 PRECONDITION(IsWatsonEnabled());
11056 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::SaveIpForUnhandledInfo - this = %p, IP = %p\n", this, ip));
11058 // Since we are setting a new IP for tracking buckets,
11059 // clear any existing details we may hold
11060 ClearWatsonBucketDetails();
11062 // Save the new IP for bucketing
11063 m_WatsonUnhandledInfo.m_UnhandledIp = ip;
11064 #endif // !DACCESS_COMPILE
11067 UINT_PTR EHWatsonBucketTracker::RetrieveWatsonBucketIp()
11069 LIMITED_METHOD_CONTRACT;
11071 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::RetrieveWatsonBucketIp - this = %p, IP = %p\n", this, m_WatsonUnhandledInfo.m_UnhandledIp));
11073 return m_WatsonUnhandledInfo.m_UnhandledIp;
11076 // This function returns the reference to the Watson buckets tracked by the
11077 // instance of WatsonBucket tracker.
11079 // This is *also* invoked from the DAC when buckets are requested.
11080 PTR_VOID EHWatsonBucketTracker::RetrieveWatsonBuckets()
11082 #if !defined(DACCESS_COMPILE)
11083 if (!IsWatsonEnabled())
11087 #endif //!defined(DACCESS_COMPILE)
11095 PRECONDITION(IsWatsonEnabled());
11099 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::RetrieveWatsonBuckets - this = %p, bucket address = %p\n", this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11101 return m_WatsonUnhandledInfo.m_pUnhandledBuckets;
11104 void EHWatsonBucketTracker::ClearWatsonBucketDetails()
11106 #ifndef DACCESS_COMPILE
11108 if (!IsWatsonEnabled())
11119 PRECONDITION(IsWatsonEnabled());
11124 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::ClearWatsonBucketDetails for tracker (%p)\n", this));
11126 if (m_WatsonUnhandledInfo.m_pUnhandledBuckets != NULL)
11128 FreeBucketParametersForManagedException(m_WatsonUnhandledInfo.m_pUnhandledBuckets);
11132 #endif // !DACCESS_COMPILE
11135 void EHWatsonBucketTracker::CaptureUnhandledInfoForWatson(TypeOfReportedError tore, Thread * pThread, OBJECTREF * pThrowable)
11137 #ifndef DACCESS_COMPILE
11143 PRECONDITION(IsWatsonEnabled());
11147 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson capturing watson bucket details for (%p)\n", this));
11149 // Only capture the bucket information if there is an IP AND we dont already have collected them.
11150 // We could have collected them from a previous AD transition and wouldnt want to overwrite them.
11151 if (m_WatsonUnhandledInfo.m_UnhandledIp != 0)
11153 if (m_WatsonUnhandledInfo.m_pUnhandledBuckets == NULL)
11155 // Get the bucket details since we dont have them
11156 m_WatsonUnhandledInfo.m_pUnhandledBuckets = GetBucketParametersForManagedException(m_WatsonUnhandledInfo.m_UnhandledIp, tore, pThread, pThrowable);
11157 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson captured the following watson bucket details: (this = %p, bucket addr = %p)\n",
11158 this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11162 // We already have the bucket details - so no need to capture them again
11163 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson already have the watson bucket details: (this = %p, bucket addr = %p)\n",
11164 this, m_WatsonUnhandledInfo.m_pUnhandledBuckets));
11169 LOG((LF_EH, LL_INFO1000, "EHWatsonBucketTracker::CaptureUnhandledInfoForWatson didnt have an IP to use for capturing watson buckets\n"));
11171 #endif // !DACCESS_COMPILE
11173 #endif // !FEATURE_PAL
11175 // Given a throwable, this function will attempt to find an active EH tracker corresponding to it.
11176 // If none found, it will return NULL
11177 #ifdef WIN64EXCEPTIONS
11178 PTR_ExceptionTracker GetEHTrackerForException(OBJECTREF oThrowable, PTR_ExceptionTracker pStartingEHTracker)
11180 PTR_ExInfo GetEHTrackerForException(OBJECTREF oThrowable, PTR_ExInfo pStartingEHTracker)
11182 #error Unsupported platform
11191 PRECONDITION(GetThread() != NULL);
11192 PRECONDITION(oThrowable != NULL);
11196 // Get the reference to the exception tracker to start with. If one has been provided to us,
11197 // then use it. Otherwise, start from the current one.
11198 #ifdef WIN64EXCEPTIONS
11199 PTR_ExceptionTracker pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
11201 PTR_ExInfo pEHTracker = (pStartingEHTracker != NULL) ? pStartingEHTracker : GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
11203 #error Unsupported platform
11206 BOOL fFoundTracker = FALSE;
11208 // Start walking the list to find the tracker correponding
11209 // to the exception object.
11210 while (pEHTracker != NULL)
11212 if (pEHTracker->GetThrowable() == oThrowable)
11214 // found the tracker - break out.
11215 fFoundTracker = TRUE;
11219 // move to the previous tracker...
11220 pEHTracker = pEHTracker->GetPreviousExceptionTracker();
11223 return fFoundTracker ? pEHTracker : NULL;
11226 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
11227 // -----------------------------------------------------------------------
11228 // Support for CorruptedState Exceptions
11229 // -----------------------------------------------------------------------
11231 // Given an exception code, this method returns a BOOL to indicate if the
11232 // code belongs to a corrupting exception or not.
11234 BOOL CEHelper::IsProcessCorruptedStateException(DWORD dwExceptionCode, BOOL fCheckForSO /*= TRUE*/)
11245 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11250 // Call into the utilcode helper function to check if this
11252 return (::IsProcessCorruptedStateException(dwExceptionCode, fCheckForSO));
11255 // This is used in the VM folder version of "SET_CE_RETHROW_FLAG_FOR_EX_CATCH" (in clrex.h)
11256 // to check if the managed exception caught by EX_END_CATCH is CSE or not.
11258 // If you are using it from rethrow boundaries (e.g. SET_CE_RETHROW_FLAG_FOR_EX_CATCH
11259 // macro that is used to automatically rethrow corrupting exceptions), then you may
11260 // want to set the "fMarkForReuseIfCorrupting" to TRUE to enable propagation of the
11261 // corruption severity when the reraised exception is seen by managed code again.
11263 BOOL CEHelper::IsLastActiveExceptionCorrupting(BOOL fMarkForReuseIfCorrupting /* = FALSE */)
11270 PRECONDITION(GetThread() != NULL);
11274 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11279 BOOL fIsCorrupting = FALSE;
11280 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
11282 // Check the corruption severity
11283 CorruptionSeverity severity = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11284 fIsCorrupting = (severity == ProcessCorrupting);
11285 if (fIsCorrupting && fMarkForReuseIfCorrupting)
11287 // Mark the corruption severity for reuse
11288 CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse();
11291 LOG((LF_EH, LL_INFO100, "CEHelper::IsLastActiveExceptionCorrupting - Using corruption severity from TES.\n"));
11293 return fIsCorrupting;
11296 // Given a MethodDesc, this method will return a BOOL to indicate if
11297 // the containing assembly was built for PreV4 runtime or not.
11299 BOOL CEHelper::IsMethodInPreV4Assembly(PTR_MethodDesc pMethodDesc)
11306 PRECONDITION(pMethodDesc != NULL);
11310 // By default, assume that the containing assembly was not
11311 // built for PreV4 runtimes.
11312 BOOL fBuiltForPreV4Runtime = FALSE;
11314 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11319 LPCSTR pszVersion = NULL;
11321 // Retrieve the manifest metadata reference since that contains
11322 // the "built-for" runtime details
11323 IMDInternalImport *pImport = pMethodDesc->GetAssembly()->GetManifestImport();
11324 if (pImport && SUCCEEDED(pImport->GetVersionString(&pszVersion)))
11326 if (pszVersion != NULL)
11328 // If version begins with "v1.*" or "v2.*", it was built for preV4 runtime
11329 if ((pszVersion[0] == 'v' || pszVersion[0] == 'V') &&
11330 IS_DIGIT(pszVersion[1]) &&
11331 (pszVersion[2] == '.') )
11333 // Looks like a version. Is it lesser than v4.0 major version where we start using new behavior?
11334 fBuiltForPreV4Runtime = ((DIGIT_TO_INT(pszVersion[1]) != 0) &&
11335 (DIGIT_TO_INT(pszVersion[1]) <= HIGHEST_MAJOR_VERSION_OF_PREV4_RUNTIME));
11340 return fBuiltForPreV4Runtime;
11343 // Given a MethodDesc and CorruptionSeverity, this method will return a
11344 // BOOL indicating if the method can handle those kinds of CEs or not.
11346 BOOL CEHelper::CanMethodHandleCE(PTR_MethodDesc pMethodDesc, CorruptionSeverity severity, BOOL fCalculateSecurityInfo /*= TRUE*/)
11348 BOOL fCanMethodHandleSeverity = FALSE;
11350 #ifndef DACCESS_COMPILE
11353 if (fCalculateSecurityInfo)
11355 GC_TRIGGERS; // CEHelper::CanMethodHandleCE will invoke Security::IsMethodCritical that could endup invoking MethodTable::LoadEnclosingMethodTable that is GC_TRIGGERS
11359 // See comment in COMPlusUnwindCallback for details.
11362 // First pass requires THROWS and in 2nd we need to be due to the AppX check below where GetFusionAssemblyName can throw.
11365 PRECONDITION(pMethodDesc != NULL);
11370 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11375 // Since the method is Security Critical, now check if it is
11376 // attributed to handle the CE or not.
11377 IMDInternalImport *pImport = pMethodDesc->GetMDImport();
11378 if (pImport != NULL)
11380 mdMethodDef methodDef = pMethodDesc->GetMemberDef();
11383 case ProcessCorrupting:
11384 fCanMethodHandleSeverity = (S_OK == pImport->GetCustomAttributeByName(
11386 HANDLE_PROCESS_CORRUPTED_STATE_EXCEPTION_ATTRIBUTE,
11391 _ASSERTE(!"Unknown Exception Corruption Severity!");
11395 #endif // !DACCESS_COMPILE
11397 return fCanMethodHandleSeverity;
11400 // Given a MethodDesc, this method will return a BOOL to indicate if the method should be examined for exception
11401 // handlers for the specified exception.
11403 // This method accounts for both corrupting and non-corrupting exceptions.
11405 BOOL CEHelper::CanMethodHandleException(CorruptionSeverity severity, PTR_MethodDesc pMethodDesc, BOOL fCalculateSecurityInfo /*= TRUE*/)
11409 // CEHelper::CanMethodHandleCE will invoke Security::IsMethodCritical that could endup invoking MethodTable::LoadEnclosingMethodTable that is GC_TRIGGERS/THROWS
11410 if (fCalculateSecurityInfo)
11416 // See comment in COMPlusUnwindCallback for details.
11421 PRECONDITION(pMethodDesc != NULL);
11425 // By default, assume that the runtime shouldn't look for exception handlers
11426 // in the method pointed by the MethodDesc
11427 BOOL fLookForExceptionHandlersInMethod = FALSE;
11429 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11434 // If we have been asked to use the last active corruption severity (e.g. in cases of Reflection
11435 // or COM interop), then retrieve it.
11436 if (severity == UseLast)
11438 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Using LastActiveExceptionCorruptionSeverity.\n"));
11439 severity = GetThread()->GetExceptionState()->GetLastActiveExceptionCorruptionSeverity();
11442 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Processing CorruptionSeverity: %d.\n", severity));
11444 if (severity > NotCorrupting)
11446 // If the method lies in an assembly built for pre-V4 runtime, allow the runtime
11447 // to look for exception handler for the CE.
11448 BOOL fIsMethodInPreV4Assembly = FALSE;
11449 fIsMethodInPreV4Assembly = CEHelper::IsMethodInPreV4Assembly(pMethodDesc);
11451 if (!fIsMethodInPreV4Assembly)
11453 // Method lies in an assembly built for V4 or later runtime.
11454 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Method is in an assembly built for V4 or later runtime.\n"));
11456 // Depending upon the corruption severity of the exception, see if the
11457 // method supports handling that.
11458 LOG((LF_EH, LL_INFO100, "CEHelper::CanMethodHandleException - Exception is corrupting.\n"));
11460 // Check if the method can handle the severity specified in the exception object.
11461 fLookForExceptionHandlersInMethod = CEHelper::CanMethodHandleCE(pMethodDesc, severity, fCalculateSecurityInfo);
11465 // Method is in a Pre-V4 assembly - allow it to be examined for processing the CE
11466 fLookForExceptionHandlersInMethod = TRUE;
11471 // Non-corrupting exceptions can continue to be delivered
11472 fLookForExceptionHandlersInMethod = TRUE;
11475 return fLookForExceptionHandlersInMethod;
11478 // Given a managed exception object, this method will return a BOOL
11479 // indicating if it corresponds to a ProcessCorruptedState exception
11482 BOOL CEHelper::IsProcessCorruptedStateException(OBJECTREF oThrowable)
11490 PRECONDITION(oThrowable != NULL);
11494 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11499 #ifndef DACCESS_COMPILE
11500 // If the throwable represents preallocated SO, then indicate it as a CSE
11501 if (CLRException::GetPreallocatedStackOverflowException() == oThrowable)
11505 #endif // !DACCESS_COMPILE
11507 // Check if we have an exception tracker for this exception
11508 // and if so, if it represents corrupting exception or not.
11509 // Get the exception tracker for the current exception
11510 #ifdef WIN64EXCEPTIONS
11511 PTR_ExceptionTracker pEHTracker = GetEHTrackerForException(oThrowable, NULL);
11513 PTR_ExInfo pEHTracker = GetEHTrackerForException(oThrowable, NULL);
11515 #error Unsupported platform
11518 if (pEHTracker != NULL)
11520 // Found the tracker for exception object - check if its CSE or not.
11521 return (pEHTracker->GetCorruptionSeverity() == ProcessCorrupting);
11527 #ifdef WIN64EXCEPTIONS
11528 void CEHelper::SetupCorruptionSeverityForActiveExceptionInUnwindPass(Thread *pCurThread, PTR_ExceptionTracker pEHTracker, BOOL fIsFirstPass,
11529 DWORD dwExceptionCode)
11531 #ifndef DACCESS_COMPILE
11538 PRECONDITION(!fIsFirstPass); // This method should only be called during an unwind
11539 PRECONDITION(pCurThread != NULL);
11545 // Typically, exception tracker is created for an exception when the OS is in the first pass.
11546 // However, it may be created during the 2nd pass under specific cases. Managed C++ provides
11547 // such a scenario. In the following, stack grows left to right:
11549 // CallDescrWorker -> ILStub1 -> <Native Main> -> UMThunkStub -> IL_Stub2 -> <Managed Main>
11551 // If a CSE exception goes unhandled from managed main, it will reach the OS. The [CRT in?] OS triggers
11552 // unwind that results in invoking the personality routine of UMThunkStub, called UMThunkStubUnwindFrameChainHandler,
11553 // that releases all exception trackers below it. Thus, the tracker for the CSE, which went unhandled, is also
11554 // released. This detail is 64bit specific and the crux of this issue.
11556 // Now, it is expected that by the time we are in the unwind pass, the corruption severity would have already been setup in the
11557 // exception tracker and thread exception state (TES) as part of the first pass, and thus, are identical.
11559 // However, for the scenario above, when the unwind continues and reaches ILStub1, its personality routine (which is ProcessCLRException)
11560 // is invoked. It attempts to get the exception tracker corresponding to the exception. Since none exists, it creates a brand new one,
11561 // which has the exception corruption severity as NotSet.
11563 // During the stack walk, we know (from TES) that the active exception was a CSE, and thus, ILStub1 cannot handle the exception. Prior
11564 // to bailing out, we assert that our data structures are intact by comparing the exception severity in TES with the one in the current
11565 // exception tracker. Since the tracker was recreated, it had the severity as NotSet and this does not match the severity in TES.
11566 // Thus, the assert fires. [This check is performed in ProcessManagedCallFrame.]
11568 // To address such a case, if we have created a new exception tracker in the unwind (2nd) pass, then set its
11569 // exception corruption severity to what the TES holds currently. This will maintain the same semantic as the case
11570 // where new tracker is not created (for e.g. the exception was caught in Managed main).
11572 // The exception is the scenario of code that uses longjmp to jump to a different context. Longjmp results in a raise
11573 // of a new exception with the longjmp exception code (0x80000026) but with ExceptionFlags set indicating unwind. When this is
11574 // seen by ProcessCLRException (64bit personality routine), it will create a new tracker in the 2nd pass.
11576 // Longjmp outside an exceptional path does not interest us, but the one in the exceptional
11577 // path would only happen when a method attributed to handle CSE invokes it. Thus, if the longjmp happened during the 2nd pass of a CSE,
11578 // we want it to proceed (and thus, jump) as expected and not apply the CSE severity to the tracker - this is equivalent to
11579 // a catch block that handles a CSE and then does a "throw new Exception();". The new exception raised is
11580 // non-CSE in nature as well.
11582 // http://www.nynaeve.net/?p=105 has a brief description of how exception-safe setjmp/longjmp works.
11585 if (pEHTracker->GetCorruptionSeverity() == NotSet)
11587 // Get the thread exception state
11588 ThreadExceptionState *pCurTES = pCurThread->GetExceptionState();
11590 // Set the tracker to have the same corruption severity as the last active severity unless we are dealing
11592 if (dwExceptionCode == STATUS_LONGJUMP)
11594 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotCorrupting);
11597 pEHTracker->SetCorruptionSeverity(pCurTES->GetLastActiveExceptionCorruptionSeverity());
11598 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveExceptionInUnwindPass - Setup the corruption severity in the second pass.\n"));
11600 #endif // !DACCESS_COMPILE
11602 #endif // WIN64EXCEPTIONS
11604 // This method is invoked from the personality routine for managed code and is used to setup the
11605 // corruption severity for the active exception on the thread exception state and the
11606 // exception tracker corresponding to the exception.
11608 void CEHelper::SetupCorruptionSeverityForActiveException(BOOL fIsRethrownException, BOOL fIsNestedException, BOOL fShouldTreatExceptionAsNonCorrupting /* = FALSE */)
11610 #ifndef DACCESS_COMPILE
11619 // Get the thread and the managed exception object - they must exist at this point
11620 Thread *pCurThread = GetThread();
11621 _ASSERTE(pCurThread != NULL);
11623 OBJECTREF oThrowable = pCurThread->GetThrowable();
11624 _ASSERTE(oThrowable != NULL);
11626 // Get the thread exception state
11627 ThreadExceptionState * pCurTES = pCurThread->GetExceptionState();
11628 _ASSERTE(pCurTES != NULL);
11630 // Get the exception tracker for the current exception
11631 #ifdef WIN64EXCEPTIONS
11632 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
11634 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
11635 #else // !(_WIN64 || _TARGET_X86_)
11636 #error Unsupported platform
11639 _ASSERTE(pEHTracker != NULL);
11641 // Get the current exception code from the tracker.
11642 PEXCEPTION_RECORD pEHRecord = pCurTES->GetExceptionRecord();
11643 _ASSERTE(pEHRecord != NULL);
11644 DWORD dwActiveExceptionCode = pEHRecord->ExceptionCode;
11646 if (pEHTracker->GetCorruptionSeverity() != NotSet)
11648 // Since the exception tracker already has the corruption severity set,
11649 // we dont have much to do. Just confirm that our assumptions are correct.
11650 _ASSERTE(pEHTracker->GetCorruptionSeverity() == pCurTES->GetLastActiveExceptionCorruptionSeverity());
11652 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Current tracker already has the corruption severity set.\n"));
11656 // If the exception in question is to be treated as non-corrupting,
11657 // then flag it and exit.
11658 if (fShouldTreatExceptionAsNonCorrupting || g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11660 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11661 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Exception treated as non-corrupting.\n"));
11665 if (!fIsRethrownException && !fIsNestedException)
11667 // There should be no previously active exception for this case
11668 _ASSERTE(pEHTracker->GetPreviousExceptionTracker() == NULL);
11670 CorruptionSeverity severityTES = NotSet;
11672 if (pCurTES->ShouldLastActiveExceptionCorruptionSeverityBeReused())
11674 // Get the corruption severity from the ThreadExceptionState (TES) for the last active exception
11675 severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11677 // Incase of scenarios like AD transition or Reflection invocation,
11678 // TES would hold corruption severity of the last active exception. To propagate it
11679 // to the current exception, we will apply it to current tracker and only if the applied
11680 // severity is "NotSet", will we proceed to check the current exception for corruption
11682 pEHTracker->SetCorruptionSeverity(severityTES);
11685 // Reset TES Corruption Severity
11686 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11688 if (severityTES == NotSet)
11690 // Since the last active exception's severity was "NotSet", we will look up the
11691 // exception code and the exception object to see if the exception should be marked
11694 // Since this exception was neither rethrown nor is nested, it implies that we are
11695 // outside an active exception. Thus, even if it contains inner exceptions, we wont have
11696 // corruption severity for them since that information is tracked in EH tracker and
11697 // we wont have an EH tracker for the inner most exception.
11699 if (CEHelper::IsProcessCorruptedStateException(dwActiveExceptionCode) ||
11700 CEHelper::IsProcessCorruptedStateException(oThrowable))
11702 pEHTracker->SetCorruptionSeverity(ProcessCorrupting);
11703 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked non-rethrow/non-nested exception as ProcessCorrupting.\n"));
11707 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11708 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked non-rethrow/non-nested exception as NotCorrupting.\n"));
11713 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity to tracker from ThreadExceptionState for non-rethrow/non-nested exception.\n"));
11718 // Its either a rethrow or nested exception
11720 #ifdef WIN64EXCEPTIONS
11721 PTR_ExceptionTracker pOrigEHTracker = NULL;
11723 PTR_ExInfo pOrigEHTracker = NULL;
11725 #error Unsupported platform
11728 BOOL fDoWeHaveCorruptionSeverity = FALSE;
11730 if (fIsRethrownException)
11732 // Rethrown exceptions are nested by nature (of our implementation). The
11733 // original EHTracker will exist for the exception - infact, it will be
11734 // the tracker previous to the current one. We will simply copy
11735 // its severity to the current EH tracker representing the rethrow.
11736 pOrigEHTracker = pEHTracker->GetPreviousExceptionTracker();
11737 _ASSERTE(pOrigEHTracker != NULL);
11739 // Ideally, we would like have the assert below enabled. But, as may happen under OOM
11740 // stress, this can be false. Here's how it will happen:
11742 // An exception is thrown, which is later caught and rethrown in the catch block. Rethrow
11743 // results in calling IL_Rethrow that will call RaiseTheExceptionInternalOnly to actually
11744 // raise the exception. Prior to the raise, we update the last thrown object on the thread
11745 // by calling Thread::SafeSetLastThrownObject which, internally, could have an OOM, resulting
11746 // in "changing" the throwable used to raise the exception to be preallocated OOM object.
11748 // When the rethrow happens and CLR's exception handler for managed code sees the exception,
11749 // the exception tracker created for the rethrown exception will contain the reference to
11750 // the last thrown object, which will be the preallocated OOM object.
11752 // Thus, though, we came here because of a rethrow, and logically, the throwable should remain
11753 // the same, it neednt be. Simply put, rethrow can result in working with a completely different
11754 // exception object than what was originally thrown.
11756 // Hence, the assert cannot be enabled.
11758 // Thus, we will use the EH tracker corresponding to the original exception, to get the
11759 // rethrown exception's corruption severity, only when the rethrown throwable is the same
11760 // as the original throwable. Otherwise, we will pretend that we didnt get the original tracker
11761 // and will automatically enter the path below to set the corruption severity based upon the
11762 // rethrown throwable.
11764 // _ASSERTE(pOrigEHTracker->GetThrowable() == oThrowable);
11765 if (pOrigEHTracker->GetThrowable() != oThrowable)
11767 pOrigEHTracker = NULL;
11768 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Rethrown throwable does not match the original throwable. Corruption severity will be set based upon rethrown throwable.\n"));
11773 // Get the corruption severity from the ThreadExceptionState (TES) for the last active exception
11774 CorruptionSeverity severityTES = NotSet;
11776 if (pCurTES->ShouldLastActiveExceptionCorruptionSeverityBeReused())
11778 severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11780 // Incase of scenarios like AD transition or Reflection invocation,
11781 // TES would hold corruption severity of the last active exception. To propagate it
11782 // to the current exception, we will apply it to current tracker and only if the applied
11783 // severity is "NotSet", will we proceed to check the current exception for corruption
11785 pEHTracker->SetCorruptionSeverity(severityTES);
11788 // Reset TES Corruption Severity
11789 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11791 // If the last exception didnt have any corruption severity, proceed to look for it.
11792 if (severityTES == NotSet)
11794 // This is a nested exception - check if it has an inner exception(s). If it does,
11795 // find the EH tracker corresponding to the innermost exception and we will copy the
11796 // corruption severity from the original tracker to the current one.
11797 OBJECTREF oInnermostThrowable = ((EXCEPTIONREF)oThrowable)->GetBaseException();
11798 if (oInnermostThrowable != NULL)
11800 // Find the tracker corresponding to the inner most exception, starting from
11801 // the tracker previous to the current one. An EH tracker may not be found if
11802 // the code did the following inside a catch clause:
11804 // Exception ex = new Exception("inner exception");
11805 // throw new Exception("message", ex);
11807 // Or, an exception like AV happened in the catch clause.
11808 pOrigEHTracker = GetEHTrackerForException(oInnermostThrowable, pEHTracker->GetPreviousExceptionTracker());
11813 // We have the corruption severity from the TES. Set the flag indicating so.
11814 fDoWeHaveCorruptionSeverity = TRUE;
11815 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity to tracker from ThreadExceptionState for nested exception.\n"));
11819 if (!fDoWeHaveCorruptionSeverity)
11821 if (pOrigEHTracker != NULL)
11823 // Copy the severity from the original EH tracker to the current one
11824 CorruptionSeverity origCorruptionSeverity = pOrigEHTracker->GetCorruptionSeverity();
11825 _ASSERTE(origCorruptionSeverity != NotSet);
11826 pEHTracker->SetCorruptionSeverity(origCorruptionSeverity);
11828 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity (%d) from the original EH tracker for rethrown exception.\n", origCorruptionSeverity));
11832 if (CEHelper::IsProcessCorruptedStateException(dwActiveExceptionCode) ||
11833 CEHelper::IsProcessCorruptedStateException(oThrowable))
11835 pEHTracker->SetCorruptionSeverity(ProcessCorrupting);
11836 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked nested exception as ProcessCorrupting.\n"));
11840 pEHTracker->SetCorruptionSeverity(NotCorrupting);
11841 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Marked nested exception as NotCorrupting.\n"));
11848 // Save the current exception's corruption severity in the ThreadExceptionState (TES)
11849 // for cases when we catch the managed exception in the runtime using EX_CATCH.
11850 // At such a time, all exception trackers get released (due to unwind triggered
11851 // by EX_END_CATCH) and yet we need the corruption severity information for
11852 // scenarios like AD Transition, Reflection invocation, etc.
11853 CorruptionSeverity currentSeverity = pEHTracker->GetCorruptionSeverity();
11855 // We should be having a valid corruption severity at this point
11856 _ASSERTE(currentSeverity != NotSet);
11858 // Save it in the TES
11859 pCurTES->SetLastActiveExceptionCorruptionSeverity(currentSeverity);
11860 LOG((LF_EH, LL_INFO100, "CEHelper::SetupCorruptionSeverityForActiveException - Copied the corruption severity (%d) to ThreadExceptionState.\n", currentSeverity));
11862 #endif // !DACCESS_COMPILE
11865 // CE can be caught in the VM and later reraised again. Examples of such scenarios
11866 // include AD transition, COM interop, Reflection invocation, to name a few.
11867 // In such cases, we want to mark the corruption severity for reuse upon reraise,
11868 // implying that when the VM does a reraise of such an exception, we should use
11869 // the original corruption severity for the new raised exception, instead of creating
11870 // a new one for it.
11872 void CEHelper::MarkLastActiveExceptionCorruptionSeverityForReraiseReuse()
11880 PRECONDITION(GetThread() != NULL);
11884 // If the last active exception's corruption severity is anything but
11885 // "NotSet", mark it for ReraiseReuse
11886 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
11887 _ASSERTE(pCurTES != NULL);
11889 CorruptionSeverity severityTES = pCurTES->GetLastActiveExceptionCorruptionSeverity();
11890 if (severityTES != NotSet)
11892 pCurTES->SetLastActiveExceptionCorruptionSeverity((CorruptionSeverity)(severityTES | ReuseForReraise));
11896 // This method will return a BOOL to indicate if the current exception is to be treated as
11897 // non-corrupting. Currently, this returns true for NullReferenceException only.
11899 BOOL CEHelper::ShouldTreatActiveExceptionAsNonCorrupting()
11901 BOOL fShouldTreatAsNonCorrupting = FALSE;
11903 #ifndef DACCESS_COMPILE
11909 PRECONDITION(GetThread() != NULL);
11913 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
11918 DWORD dwActiveExceptionCode = GetThread()->GetExceptionState()->GetExceptionRecord()->ExceptionCode;
11919 if (dwActiveExceptionCode == STATUS_ACCESS_VIOLATION)
11921 // NullReference has the same exception code as AV
11922 OBJECTREF oThrowable = NULL;
11923 GCPROTECT_BEGIN(oThrowable);
11925 // Get the throwable and check if it represents null reference exception
11926 oThrowable = GetThread()->GetThrowable();
11927 _ASSERTE(oThrowable != NULL);
11928 if (MscorlibBinder::GetException(kNullReferenceException) == oThrowable->GetMethodTable())
11930 fShouldTreatAsNonCorrupting = TRUE;
11934 #endif // !DACCESS_COMPILE
11936 return fShouldTreatAsNonCorrupting;
11939 // If we were working in a nested exception scenario, reset the corruption severity to the last
11940 // exception we were processing, based upon its EH tracker.
11942 // If none was present, reset it to NotSet.
11944 // Note: This method must be called once the exception trackers have been adjusted post catch-block execution.
11946 void CEHelper::ResetLastActiveCorruptionSeverityPostCatchHandler(Thread *pThread)
11953 PRECONDITION(pThread != NULL);
11957 ThreadExceptionState *pCurTES = pThread->GetExceptionState();
11959 // By this time, we would have set the correct exception tracker for the active exception domain,
11960 // if applicable. An example is throwing and catching an exception within a catch block. We will update
11961 // the LastActiveCorruptionSeverity based upon the active exception domain. If we are not in one, we will
11962 // set it to "NotSet".
11963 #ifdef WIN64EXCEPTIONS
11964 PTR_ExceptionTracker pEHTracker = pCurTES->GetCurrentExceptionTracker();
11966 PTR_ExInfo pEHTracker = pCurTES->GetCurrentExceptionTracker();
11968 #error Unsupported platform
11973 pCurTES->SetLastActiveExceptionCorruptionSeverity(pEHTracker->GetCorruptionSeverity());
11977 pCurTES->SetLastActiveExceptionCorruptionSeverity(NotSet);
11980 LOG((LF_EH, LL_INFO100, "CEHelper::ResetLastActiveCorruptionSeverityPostCatchHandler - Reset LastActiveException corruption severity to %d.\n",
11981 pCurTES->GetLastActiveExceptionCorruptionSeverity()));
11984 // This method will return a BOOL indicating if the target of IDispatch can handle the specified exception or not.
11986 BOOL CEHelper::CanIDispatchTargetHandleException()
11993 PRECONDITION(GetThread() != NULL);
11997 // By default, assume that the target of IDispatch cannot handle the exception.
11998 BOOL fCanMethodHandleException = FALSE;
12000 if (g_pConfig->LegacyCorruptedStateExceptionsPolicy())
12005 // IDispatch implementation in COM interop works by invoking the actual target via reflection.
12006 // Thus, a COM client could use the V4 runtime to invoke a V2 method. In such a case, a CSE
12007 // could come unhandled at the actual target invoked via reflection.
12009 // Reflection invocation would have set a flag for us, indicating if the actual target was
12010 // enabled to handle the CE or not. If it is, then we should allow the COM client to get the
12011 // hresult from the call and not let the exception continue up the stack.
12012 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
12013 fCanMethodHandleException = pCurTES->CanReflectionTargetHandleException();
12015 // Reset the flag so that subsequent invocations work as expected.
12016 pCurTES->SetCanReflectionTargetHandleException(FALSE);
12018 return fCanMethodHandleException;
12021 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12023 #ifndef DACCESS_COMPILE
12024 // When a managed thread starts in non-default domain, its callstack looks like below:
12026 // <ManagedThreadBase_DispatchOuter>
12027 // <ManagedThreadBase_DispatchMiddle>
12028 // <ManagedThreadBase_DispatchInner>
12030 // -- AD transition is here -- ==> Pushes ContextTransitionFrame and has EX_CATCH
12032 // <ManagedThreadBase_DispatchOuter>
12033 // <ManagedThreadBase_DispatchMiddle>
12034 // <ManagedThreadBase_DispatchInner>
12036 // In CoreCLR, all managed threads spawned will have a stack like this since they all
12037 // run in non-DefaultDomain. The upper three frames are in default domain and the lower
12038 // three are in the non-default domain in which the thread was created. Any exception
12039 // that is unhandled in non-default domain will be caught at AD transition boundary.
12040 // The transition boundary does the following tasks:
12042 // 1) Catch any incoming unhandled exception from the non-default domain using EX_CATCH.
12043 // 2) Marshal the exception object to the return context (i.e. DefaultDomain)
12044 // 3) Return to the context of DefaultDomain and throw the marshalled exception object there.
12046 // All this depends upon the EX_CATCH (which is based upon C++ exception handling) being
12047 // able to catch the exception.
12049 // However, if a breakpoint exception ia raised and a debugger is not available to handle it,
12050 // C++'s catch(...) will not be able to catch it, even when compiled with /EHa. For the curious,
12051 // refer to "FindHandlerForForeignException" function's implementation in the CRT. One of the first
12052 // things it does is check for breakpoint exception and if it is, it will simply bail out of the
12053 // process of finding a handler. Thus, EX_CATCH will not be able to catch this exception and we
12054 // will not be able to transition to the previous AD context.
12056 // Imagine a thread in non-default domain suffers breakpoint exception. Assuming it will go unhandled,
12057 // it will reach the OS, which will trigger an unwind. The execution of termination handlers in lower
12058 // three frames (above) is fine since they are in the same AD as the thread. But when termination
12059 // handlers in the upper three frames execute, its a case of bad mixup since the thread is in a different
12060 // AD context than what the frames are expected to be in.
12062 // Hence, we need a mechanism to transition to the expected AppDomain in case of breakpoint exception.
12063 // This function supports this mechanism in a generic fashion, i.e., one can use it to transition to
12064 // any AppDomain, though only up the stack.
12066 BOOL ReturnToPreviousAppDomain()
12068 STATIC_CONTRACT_GC_NOTRIGGER;
12069 STATIC_CONTRACT_NOTHROW;
12070 STATIC_CONTRACT_MODE_COOPERATIVE;
12071 STATIC_CONTRACT_SO_TOLERANT;
12073 Thread *pCurThread = GetThread();
12074 _ASSERTE(pCurThread != NULL);
12076 BOOL fTransitioned = FALSE;
12078 BEGIN_SO_INTOLERANT_CODE_NOTHROW(pCurThread, return FALSE);
12080 // Get the thread's current domain
12081 AppDomain *pCurDomain = pCurThread->GetDomain();
12082 _ASSERTE(pCurDomain != NULL);
12084 // Lookup the ContextTransitionFrame for the transition into the current AppDomain.
12085 Frame *pCtxTransitionFrame = pCurThread->GetFirstTransitionInto(pCurDomain, NULL);
12086 if (pCtxTransitionFrame == NULL)
12088 // Since we couldnt find the context transition frame, check if its the default domain.
12089 // If so, we will set fTransitioned to TRUE since there is no context transition frame
12090 // setup for the initial entry into the default domain. For all other transitions to it
12091 // from non-default domains, we will have a context transition frame. We will do a
12092 // debug-only check to assert this invariant.
12093 BOOL fIsDefDomain = pCurDomain->IsDefaultDomain();
12097 // Start with the topmost frame and look for a CTX frame until we reach the top of the frame chain.
12098 // We better not find one since we couldnt find a transition frame to the DefaultDomain.
12099 Frame *pStartFrame = pCurThread->GetFrame();
12100 BOOL fFoundCTXFrame = FALSE;
12101 while ((pStartFrame != NULL) && (pStartFrame != (Frame *)FRAME_TOP))
12103 if (pStartFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr())
12105 fFoundCTXFrame = TRUE;
12109 // Get the next frame in the chain
12110 pStartFrame = pStartFrame->PtrNextFrame();
12113 _ASSERTE_MSG(!fFoundCTXFrame, "How come we didnt find the transition frame to DefDomain but found another CTX frame on the frame chain?");
12116 fTransitioned = fIsDefDomain;
12117 LOG((LF_EH, LL_INFO100, "ReturnToPreviousAppDomain: Unable to find the transition into the current domain (IsDefaultDomain: %d).\n", fIsDefDomain));
12122 // Confirm its the correct type of frame
12123 _ASSERTE_MSG(pCtxTransitionFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr(),
12124 "How come we didn't find context transition frame for this AD transition?");
12126 // Get the topmost Frame
12128 pCurFrame = pCurThread->GetFrame();
12132 // The loop below assumes we are called during an exception unwind since it
12133 // unwinds the Frames and pops them off the thread.
12137 // Clear all the frames until we are at the frame of our interest. If there was a
12138 // CTX frame between the topmost frame and the AD transition, then we should be able to
12139 // catch it here as well.
12140 while((pCurFrame != NULL) && (pCurFrame < pCtxTransitionFrame) &&
12141 (pCurFrame->GetVTablePtr() != ContextTransitionFrame::GetMethodFrameVPtr()))
12143 // Invoke exception unwind and pop the frame off
12144 pCurFrame->ExceptionUnwind();
12146 pCurFrame = pCurThread->GetFrame();
12149 // Confirm that we are at the expected Frame.
12150 _ASSERTE_MSG(((pCurFrame != NULL) &&
12151 (pCurFrame->GetVTablePtr() == ContextTransitionFrame::GetMethodFrameVPtr()) &&
12152 (pCurFrame == pCtxTransitionFrame)),
12153 "How come we are not at the exact context transition frame?");
12155 // Log our context return
12156 LOG((LF_EH, LL_INFO100, "ReturnToPreviousAppDomain: Returning from AD %d to AD %d\n",
12157 GetAppDomain()->GetId().m_dwId, pCtxTransitionFrame->GetReturnDomain()->GetId().m_dwId));
12159 // Return to the previous AD context
12160 pCurThread->ReturnToContext((ContextTransitionFrame *)pCtxTransitionFrame);
12163 // At this point, the context transition frame would have been popped off by
12164 // ReturnToContext above.
12165 pCurFrame = pCurThread->GetFrame();
12166 _ASSERTE_MSG(pCurFrame != pCtxTransitionFrame, "How come the CTX frame of AD transition is still on the frame chain?");
12169 // Set the flag that we transitioned correctly.
12170 fTransitioned = TRUE;
12173 END_SO_INTOLERANT_CODE;
12175 return fTransitioned;
12178 // This class defines a holder that can be used to return to previous AppDomain incase an exception
12179 // goes across an AD transition boundary without reverting the active context.
12181 // Use this holder *after* you have transitioned to the target AD.
12182 void ReturnToPreviousAppDomainHolder::Init()
12193 m_fShouldReturnToPreviousAppDomain = TRUE;
12194 m_pThread = GetThread();
12195 _ASSERTE(m_pThread != NULL);
12198 m_pTransitionedToAD = m_pThread->GetDomain();
12202 ReturnToPreviousAppDomainHolder::ReturnToPreviousAppDomainHolder()
12216 void ReturnToPreviousAppDomainHolder::ReturnToPreviousAppDomain()
12224 // Test your sanity - we should still be in the transitioned-to AD.
12225 PRECONDITION(m_pThread->GetDomain() == m_pTransitionedToAD);
12231 ::ReturnToPreviousAppDomain();
12234 // Set the LastThrownObject as NULL since we have returned to a different
12235 // AD. Maintaining the reference to an object in the "returned-from" AD
12236 // will prevent the AD from getting unloaded.
12238 // Setting to NULL does not require us to be in COOP mode.
12239 m_pThread->SafeSetLastThrownObject(NULL);
12242 ReturnToPreviousAppDomainHolder::~ReturnToPreviousAppDomainHolder()
12253 if (m_fShouldReturnToPreviousAppDomain)
12255 ReturnToPreviousAppDomain();
12259 // Reset the flag to indicate that reverting to previous AD is not required anymore.
12260 // This should be invoked when the call has successfully returned from the target execution context.
12262 // By default, this flag is TRUE (see the contructor above) to enable automatic context
12263 // revert incase an exception goes past the transition.
12265 // END_DOMAIN_TRANSITION_NO_EH_AT_TRANSITION macro uses it. See its implementation in threads.h
12267 void ReturnToPreviousAppDomainHolder::SuppressRelease()
12269 LIMITED_METHOD_CONTRACT;
12271 m_fShouldReturnToPreviousAppDomain = FALSE;
12274 #endif // !DACCESS_COMPILE
12276 #ifndef DACCESS_COMPILE
12277 // This method will deliver the actual exception notification. Its assumed that the caller has done the necessary checks, including
12278 // checking whether the delegate can be invoked for the exception's corruption severity.
12279 void ExceptionNotifications::DeliverExceptionNotification(ExceptionNotificationHandlerType notificationType, OBJECTREF *pDelegate,
12280 OBJECTREF *pAppDomain, OBJECTREF *pEventArgs)
12287 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12288 PRECONDITION(pEventArgs != NULL && IsProtectedByGCFrame(pEventArgs));
12289 PRECONDITION(pAppDomain != NULL && IsProtectedByGCFrame(pAppDomain));
12293 PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(DELEGATEREF(*pDelegate)->GetMethodPtr());
12295 DECLARE_ARGHOLDER_ARRAY(args, 3);
12297 args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(DELEGATEREF(*pDelegate)->GetTarget());
12298 args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(*pAppDomain);
12299 args[ARGNUM_2] = OBJECTREF_TO_ARGHOLDER(*pEventArgs);
12301 CALL_MANAGED_METHOD_NORET(args);
12304 // To include definition of COMDelegate::GetMethodDesc
12305 #include "comdelegate.h"
12307 // This method constructs the arguments to be passed to the exception notification event callback
12308 void ExceptionNotifications::GetEventArgsForNotification(ExceptionNotificationHandlerType notificationType,
12309 OBJECTREF *pOutEventArgs, OBJECTREF *pThrowable)
12316 PRECONDITION(notificationType != UnhandledExceptionHandler);
12317 PRECONDITION((pOutEventArgs != NULL) && IsProtectedByGCFrame(pOutEventArgs));
12318 PRECONDITION(*pOutEventArgs == NULL);
12319 PRECONDITION((pThrowable != NULL) && (*pThrowable != NULL) && IsProtectedByGCFrame(pThrowable));
12320 PRECONDITION(IsException((*pThrowable)->GetMethodTable())); // We expect a valid exception object
12324 MethodTable *pMTEventArgs = NULL;
12325 BinderMethodID idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;
12329 switch(notificationType)
12331 case FirstChanceExceptionHandler:
12332 pMTEventArgs = MscorlibBinder::GetClass(CLASS__FIRSTCHANCE_EVENTARGS);
12333 idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;
12336 _ASSERTE(!"Invalid Exception Notification Handler!");
12340 // Allocate the instance of the eventargs corresponding to the notification
12341 *pOutEventArgs = AllocateObject(pMTEventArgs);
12343 // Prepare to invoke the .ctor
12344 MethodDescCallSite ctor(idEventArgsCtor, pOutEventArgs);
12346 // Setup the arguments to be passed to the notification specific EventArgs .ctor
12347 if (notificationType == FirstChanceExceptionHandler)
12349 // FirstChance notification takes only a single argument: the exception object.
12352 ObjToArgSlot(*pOutEventArgs),
12353 ObjToArgSlot(*pThrowable),
12360 // Since we have already asserted above, just set the args to NULL.
12361 *pOutEventArgs = NULL;
12366 // Set event args to be NULL incase of any error (e.g. OOM)
12367 *pOutEventArgs = NULL;
12368 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::GetEventArgsForNotification: Setting event args to NULL due to an exception.\n"));
12370 EX_END_CATCH(RethrowCorruptingExceptions); // Dont swallow any CSE that may come in from the .ctor.
12373 // This SEH filter will be invoked when an exception escapes out of the exception notification
12374 // callback and enters the runtime. In such a case, we ill simply failfast.
12375 static LONG ExceptionNotificationFilter(PEXCEPTION_POINTERS pExceptionInfo, LPVOID pParam)
12377 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
12380 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12381 // This method will return a BOOL indicating if the delegate should be invoked for the exception
12382 // of the specified corruption severity.
12383 BOOL ExceptionNotifications::CanDelegateBeInvokedForException(OBJECTREF *pDelegate, CorruptionSeverity severity)
12390 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12391 PRECONDITION(severity > NotSet);
12395 // Notifications for CSE are only delivered if the delegate target follows CSE rules.
12396 BOOL fCanMethodHandleException = g_pConfig->LegacyCorruptedStateExceptionsPolicy() ? TRUE:(severity == NotCorrupting);
12397 if (!fCanMethodHandleException)
12401 // Get the MethodDesc of the delegate to be invoked
12402 MethodDesc *pMDDelegate = COMDelegate::GetMethodDesc(*pDelegate);
12403 _ASSERTE(pMDDelegate != NULL);
12405 // Check the callback target and see if it is following CSE rules or not.
12406 fCanMethodHandleException = CEHelper::CanMethodHandleException(severity, pMDDelegate);
12410 // Incase of any exceptions, pretend we cannot handle the exception
12411 fCanMethodHandleException = FALSE;
12412 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::CanDelegateBeInvokedForException: Exception while trying to determine if exception notification can be invoked or not.\n"));
12414 EX_END_CATCH(RethrowCorruptingExceptions); // Dont swallow any CSEs.
12417 return fCanMethodHandleException;
12419 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12421 // This method will make the actual delegate invocation for the exception notification to be delivered. If an
12422 // exception escapes out of the notification, our filter in ExceptionNotifications::DeliverNotification will
12424 void ExceptionNotifications::InvokeNotificationDelegate(ExceptionNotificationHandlerType notificationType, OBJECTREF *pDelegate, OBJECTREF *pEventArgs,
12425 OBJECTREF *pAppDomain
12426 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12427 , CorruptionSeverity severity
12428 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12436 PRECONDITION(pDelegate != NULL && IsProtectedByGCFrame(pDelegate) && (*pDelegate != NULL));
12437 PRECONDITION(pEventArgs != NULL && IsProtectedByGCFrame(pEventArgs));
12438 PRECONDITION(pAppDomain != NULL && IsProtectedByGCFrame(pAppDomain));
12439 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12440 PRECONDITION(severity > NotSet);
12441 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12442 // Unhandled Exception Notification is delivered via Unhandled Exception Processing
12444 PRECONDITION(notificationType != UnhandledExceptionHandler);
12448 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12449 // Notifications are delivered based upon corruption severity of the exception
12450 if (!ExceptionNotifications::CanDelegateBeInvokedForException(pDelegate, severity))
12452 LOG((LF_EH, LL_INFO100, "ExceptionNotifications::InvokeNotificationDelegate: Delegate cannot be invoked for corruption severity %d\n",
12456 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12458 // We've already exercised the prestub on this delegate's COMDelegate::GetMethodDesc,
12459 // as part of wiring up a reliable event sink in the BCL. Deliver the notification.
12460 ExceptionNotifications::DeliverExceptionNotification(notificationType, pDelegate, pAppDomain, pEventArgs);
12463 // This method returns a BOOL to indicate if the AppDomain is ready to receive exception notifications or not.
12464 BOOL ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(ExceptionNotificationHandlerType notificationType)
12472 PRECONDITION(GetThread() != NULL);
12473 PRECONDITION(notificationType != UnhandledExceptionHandler);
12477 Thread *pCurThread = GetThread();
12479 // Get the current AppDomain
12480 OBJECTREF oCurAppDomain = pCurThread->GetDomain()->GetRawExposedObject();
12481 if (oCurAppDomain == NULL)
12483 // Managed object for the current domain does not exist. Hence, no one
12484 // can wireup to exception notifications, let alone receive them.
12488 // Do we have handler(s) of the specific type wired up?
12489 if (notificationType == FirstChanceExceptionHandler)
12491 return (((APPDOMAINREF)oCurAppDomain)->GetFirstChanceExceptionNotificationHandler() != NULL);
12495 _ASSERTE(!"Invalid exception notification handler specified!");
12500 // This method wraps the call to the actual 'DeliverNotificationInternal' method in an SEH filter
12501 // so that if an exception escapes out of the notification callback, we will trigger failfast from
12503 void ExceptionNotifications::DeliverNotification(ExceptionNotificationHandlerType notificationType,
12504 OBJECTREF *pThrowable
12505 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12506 , CorruptionSeverity severity
12507 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12510 STATIC_CONTRACT_GC_TRIGGERS;
12511 STATIC_CONTRACT_NOTHROW; // NOTHROW because incase of an exception, we will FailFast.
12512 STATIC_CONTRACT_MODE_COOPERATIVE;
12516 ExceptionNotificationHandlerType notificationType;
12517 OBJECTREF *pThrowable;
12518 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12519 CorruptionSeverity severity;
12520 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12523 args.notificationType = notificationType;
12524 args.pThrowable = pThrowable;
12525 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12526 args.severity = severity;
12527 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12529 PAL_TRY(TryArgs *, pArgs, &args)
12531 // Make the call to the actual method that will invoke the callbacks
12532 ExceptionNotifications::DeliverNotificationInternal(pArgs->notificationType,
12534 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12536 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12539 PAL_EXCEPT_FILTER(ExceptionNotificationFilter)
12541 // We should never be entering this handler since there should be
12542 // no exception escaping out of a callback. If we are here,
12544 EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);
12549 // This method will deliver the exception notification to the current AppDomain.
12550 void ExceptionNotifications::DeliverNotificationInternal(ExceptionNotificationHandlerType notificationType,
12551 OBJECTREF *pThrowable
12552 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12553 , CorruptionSeverity severity
12554 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12563 // Unhandled Exception Notification is delivered via Unhandled Exception Processing
12565 PRECONDITION(notificationType != UnhandledExceptionHandler);
12566 PRECONDITION((pThrowable != NULL) && (*pThrowable != NULL));
12567 PRECONDITION(ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(notificationType));
12568 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12569 PRECONDITION(severity > NotSet); // Exception corruption severity must be valid at this point.
12570 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12574 Thread *pCurThread = GetThread();
12575 _ASSERTE(pCurThread != NULL);
12577 // Get the current AppDomain
12578 AppDomain *pCurDomain = GetAppDomain();
12579 _ASSERTE(pCurDomain != NULL);
12583 OBJECTREF oNotificationDelegate;
12584 PTRARRAYREF arrDelegates;
12585 OBJECTREF oInnerDelegate;
12586 OBJECTREF oEventArgs;
12587 OBJECTREF oCurrentThrowable;
12588 OBJECTREF oCurAppDomain;
12590 ZeroMemory(&gc, sizeof(gc));
12592 // This will hold the MethodDesc of the callback that will be invoked.
12593 MethodDesc *pMDDelegate = NULL;
12595 GCPROTECT_BEGIN(gc);
12597 // Protect the throwable to be passed to the delegate callback
12598 gc.oCurrentThrowable = *pThrowable;
12600 // We expect a valid exception object
12601 _ASSERTE(IsException(gc.oCurrentThrowable->GetMethodTable()));
12603 // Save the reference to the current AppDomain. If the user code has
12604 // wired upto this event, then the managed AppDomain object will exist.
12605 gc.oCurAppDomain = pCurDomain->GetRawExposedObject();
12606 _ASSERTE(gc.oCurAppDomain);
12608 // Get the reference to the delegate based upon the type of notification
12609 if (notificationType == FirstChanceExceptionHandler)
12611 gc.oNotificationDelegate = ((APPDOMAINREF)gc.oCurAppDomain)->GetFirstChanceExceptionNotificationHandler();
12615 gc.oNotificationDelegate = NULL;
12616 _ASSERTE(!"Invalid Exception Notification Handler specified!");
12619 if (gc.oNotificationDelegate != NULL)
12621 // Prevent any async exceptions from this moment on this thread
12622 ThreadPreventAsyncHolder prevAsync;
12624 gc.oEventArgs = NULL;
12626 // Get the arguments to be passed to the delegate callback. Incase of any
12627 // problem while allocating the event args, we will return a NULL.
12628 ExceptionNotifications::GetEventArgsForNotification(notificationType, &gc.oEventArgs,
12629 &gc.oCurrentThrowable);
12631 // Check if there are multiple callbacks registered? If there are, we will
12632 // loop through them, invoking each one at a time. Before invoking the target,
12633 // we will check if the target can be invoked based upon the corruption severity
12634 // for the active exception that was passed to us.
12635 gc.arrDelegates = (PTRARRAYREF) ((DELEGATEREF)(gc.oNotificationDelegate))->GetInvocationList();
12636 if (gc.arrDelegates == NULL || !gc.arrDelegates->GetMethodTable()->IsArray())
12638 ExceptionNotifications::InvokeNotificationDelegate(notificationType, &gc.oNotificationDelegate, &gc.oEventArgs,
12640 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12642 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12647 // The _invocationCount could be less than the array size, if we are sharing
12648 // immutable arrays cleverly.
12649 UINT_PTR cnt = ((DELEGATEREF)(gc.oNotificationDelegate))->GetInvocationCount();
12650 _ASSERTE(cnt <= gc.arrDelegates->GetNumComponents());
12652 for (UINT_PTR i=0; i<cnt; i++)
12654 gc.oInnerDelegate = gc.arrDelegates->m_Array[i];
12655 ExceptionNotifications::InvokeNotificationDelegate(notificationType, &gc.oInnerDelegate, &gc.oEventArgs,
12657 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12659 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12668 void ExceptionNotifications::DeliverFirstChanceNotification()
12678 // We check for FirstChance notification delivery after setting up the corruption severity
12679 // so that we can determine if the callback delegate can handle CSE (or not).
12681 // Deliver it only if not already done and someone has wiredup to receive it.
12683 // We do this provided this is the first frame of a new exception
12684 // that was thrown or a rethrown exception. We dont want to do this
12685 // processing for subsequent frames on the stack since FirstChance notification
12686 // will be delivered only when the exception is first thrown/rethrown.
12687 ThreadExceptionState *pCurTES = GetThread()->GetExceptionState();
12688 _ASSERTE(pCurTES->GetCurrentExceptionTracker());
12689 _ASSERTE(!(pCurTES->GetCurrentExceptionTracker()->DeliveredFirstChanceNotification()));
12692 if (ExceptionNotifications::CanDeliverNotificationToCurrentAppDomain(FirstChanceExceptionHandler))
12694 OBJECTREF oThrowable = NULL;
12695 GCPROTECT_BEGIN(oThrowable);
12697 oThrowable = pCurTES->GetThrowable();
12698 _ASSERTE(oThrowable != NULL);
12700 ExceptionNotifications::DeliverNotification(FirstChanceExceptionHandler, &oThrowable
12701 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
12702 , pCurTES->GetCurrentExceptionTracker()->GetCorruptionSeverity()
12703 #endif // FEATURE_CORRUPTING_EXCEPTIONS
12709 // Mark the exception tracker as having delivered the first chance notification
12710 pCurTES->GetCurrentExceptionTracker()->SetFirstChanceNotificationStatus(TRUE);
12715 #ifdef WIN64EXCEPTIONS
12716 struct TAResetStateCallbackData
12718 // Do we have more managed code up the stack?
12719 BOOL fDoWeHaveMoreManagedCodeOnStack;
12721 // StackFrame representing the crawlFrame above which
12722 // we are searching for presence of managed code.
12723 StackFrame sfSeedCrawlFrame;
12726 // This callback helps the 64bit EH attempt to determine if there is more managed code
12727 // up the stack (or not). Currently, it is used to conditionally reset the thread abort state
12728 // as the unwind passes by.
12729 StackWalkAction TAResetStateCallback(CrawlFrame* pCf, void* data)
12737 TAResetStateCallbackData *pTAResetStateCallbackData = static_cast<TAResetStateCallbackData *>(data);
12738 StackWalkAction retStatus = SWA_CONTINUE;
12740 if(pCf->IsFrameless())
12742 IJitManager* pJitManager = pCf->GetJitManager();
12743 _ASSERTE(pJitManager);
12744 if (pJitManager && (!pTAResetStateCallbackData->fDoWeHaveMoreManagedCodeOnStack))
12746 // The stackwalker can give us a callback for the seeding CrawlFrame (or other crawlframes)
12747 // depending upon which is closer to the leaf: the seeding crawlframe or the explicit frame
12748 // specified when starting the stackwalk.
12750 // Since we are interested in checking if there is more managed code up the stack from
12751 // the seeding crawlframe, we check if the current crawlframe is above it or not. If it is,
12752 // then we have found managed code up the stack and should stop the stack walk. Otherwise,
12753 // continue searching.
12754 StackFrame sfCurrentFrame = StackFrame::FromRegDisplay(pCf->GetRegisterSet());
12755 if (pTAResetStateCallbackData->sfSeedCrawlFrame < sfCurrentFrame)
12757 // We have found managed code on the stack. Flag it and stop the stackwalk.
12758 pTAResetStateCallbackData->fDoWeHaveMoreManagedCodeOnStack = TRUE;
12759 retStatus = SWA_ABORT;
12766 #endif // WIN64EXCEPTIONS
12768 // This function will reset the thread abort state agains the specified thread if it is determined that
12769 // there is no more managed code on the stack.
12771 // Note: This function should be invoked ONLY during unwind.
12772 #ifndef WIN64EXCEPTIONS
12773 void ResetThreadAbortState(PTR_Thread pThread, void *pEstablisherFrame)
12775 void ResetThreadAbortState(PTR_Thread pThread, CrawlFrame *pCf, StackFrame sfCurrentStackFrame)
12783 PRECONDITION(pThread != NULL);
12784 #ifndef WIN64EXCEPTIONS
12785 PRECONDITION(pEstablisherFrame != NULL);
12787 PRECONDITION(pCf != NULL);
12788 PRECONDITION(!sfCurrentStackFrame.IsNull());
12793 BOOL fResetThreadAbortState = FALSE;
12795 if (pThread->IsAbortRequested())
12797 #ifndef WIN64EXCEPTIONS
12798 if (GetNextCOMPlusSEHRecord(static_cast<EXCEPTION_REGISTRATION_RECORD *>(pEstablisherFrame)) == EXCEPTION_CHAIN_END)
12800 // Topmost handler and abort requested.
12801 fResetThreadAbortState = TRUE;
12802 LOG((LF_EH, LL_INFO100, "ResetThreadAbortState: Topmost handler resets abort as no more managed code beyond %p.\n", pEstablisherFrame));
12804 #else // !WIN64EXCEPTIONS
12805 // Get the active exception tracker
12806 PTR_ExceptionTracker pCurEHTracker = pThread->GetExceptionState()->GetCurrentExceptionTracker();
12807 _ASSERTE(pCurEHTracker != NULL);
12809 // We will check if thread abort state needs to be reset only for the case of exception caught in
12810 // native code. This will happen when:
12812 // 1) an unwind is triggered and
12813 // 2) current frame is the topmost frame we saw in the first pass and
12814 // 3) a thread abort is requested and
12815 // 4) we dont have address of the exception handler to be invoked.
12817 // (1), (2) and (4) above are checked for in ExceptionTracker::ProcessOSExceptionNotification from where we call this
12820 // Current frame should be the topmost frame we saw in the first pass
12821 _ASSERTE(pCurEHTracker->GetTopmostStackFrameFromFirstPass() == sfCurrentStackFrame);
12823 // If the exception has been caught in native code, then alongwith not having address of the handler to be
12824 // invoked, we also wont have the IL clause for the catch block and resume stack frame will be NULL as well.
12825 _ASSERTE((pCurEHTracker->GetCatchToCallPC() == NULL) &&
12826 (pCurEHTracker->GetCatchHandlerExceptionClauseToken() == NULL) &&
12827 (pCurEHTracker->GetResumeStackFrame().IsNull()));
12829 // Walk the frame chain to see if there is any more managed code on the stack. If not, then this is the last managed frame
12830 // on the stack and we can reset the thread abort state.
12832 // Get the frame from which to start the stack walk from
12833 Frame* pFrame = pCurEHTracker->GetLimitFrame();
12835 // At this point, we are at the topmost frame we saw during the first pass
12836 // before the unwind began. Walk the stack using the specified crawlframe and the topmost
12837 // explicit frame to determine if we have more managed code up the stack. If none is found,
12838 // we can reset the thread abort state.
12840 // Setup the data structure to be passed to the callback
12841 TAResetStateCallbackData dataCallback;
12842 dataCallback.fDoWeHaveMoreManagedCodeOnStack = FALSE;
12844 // At this point, the StackFrame in CrawlFrame should represent the current frame we have been called for.
12845 // _ASSERTE(sfCurrentStackFrame == StackFrame::FromRegDisplay(pCf->GetRegisterSet()));
12847 // Reference to the StackFrame beyond which we are looking for managed code.
12848 dataCallback.sfSeedCrawlFrame = sfCurrentStackFrame;
12850 pThread->StackWalkFramesEx(pCf->GetRegisterSet(), TAResetStateCallback, &dataCallback, QUICKUNWIND, pFrame);
12852 if (!dataCallback.fDoWeHaveMoreManagedCodeOnStack)
12854 // There is no more managed code on the stack, so reset the thread abort state.
12855 fResetThreadAbortState = TRUE;
12856 LOG((LF_EH, LL_INFO100, "ResetThreadAbortState: Resetting thread abort state since there is no more managed code beyond stack frames:\n"));
12857 LOG((LF_EH, LL_INFO100, "sf.SP = %p ", dataCallback.sfSeedCrawlFrame.SP));
12859 #endif // !WIN64EXCEPTIONS
12862 if (fResetThreadAbortState)
12864 pThread->EEResetAbort(Thread::TAR_Thread);
12867 #endif // !DACCESS_COMPILE
12869 #endif // !CROSSGEN_COMPILE
12871 //---------------------------------------------------------------------------------
12874 // EXCEPTION THROWING HELPERS
12877 //---------------------------------------------------------------------------------
12879 //---------------------------------------------------------------------------------
12880 // Funnel-worker for THROW_BAD_FORMAT and friends.
12882 // Note: The "cond" argument is there to tide us over during the transition from
12883 // BAD_FORMAT_ASSERT to THROW_BAD_FORMAT. It will go away soon.
12884 //---------------------------------------------------------------------------------
12885 VOID ThrowBadFormatWorker(UINT resID, LPCWSTR imageName DEBUGARG(__in_z const char *cond))
12891 INJECT_FAULT(COMPlusThrowOM(););
12896 #ifndef DACCESS_COMPILE
12899 if ((imageName != NULL) && (imageName[0] != 0))
12902 msgStr += imageName;
12907 if (resID == 0 || !resStr.LoadResource(CCompRC::Optional, resID))
12909 resStr.LoadResource(CCompRC::Error, MSG_FOR_URT_HR(COR_E_BADIMAGEFORMAT));
12914 if (0 != strcmp(cond, "FALSE"))
12916 msgStr += W(" (Failed condition: "); // this is in DEBUG only - not going to localize it.
12917 SString condStr(SString::Ascii, cond);
12923 ThrowHR(COR_E_BADIMAGEFORMAT, msgStr);
12924 #endif // #ifndef DACCESS_COMPILE
12927 UINT GetResourceIDForFileLoadExceptionHR(HRESULT hr)
12931 case CTL_E_FILENOTFOUND:
12932 hr = IDS_EE_FILE_NOT_FOUND;
12935 case (HRESULT)IDS_EE_PROC_NOT_FOUND:
12936 case (HRESULT)IDS_EE_PATH_TOO_LONG:
12937 case INET_E_OBJECT_NOT_FOUND:
12938 case INET_E_DATA_NOT_AVAILABLE:
12939 case INET_E_DOWNLOAD_FAILURE:
12940 case INET_E_UNKNOWN_PROTOCOL:
12941 case (HRESULT)IDS_INET_E_SECURITY_PROBLEM:
12942 case (HRESULT)IDS_EE_BAD_USER_PROFILE:
12943 case (HRESULT)IDS_EE_ALREADY_EXISTS:
12944 case IDS_EE_REFLECTIONONLY_LOADFAILURE:
12945 case IDS_CLASSLOAD_32BITCLRLOADING64BITASSEMBLY:
12949 hr = FUSION_E_INVALID_NAME;
12952 case INET_E_CONNECTION_TIMEOUT:
12953 hr = IDS_INET_E_CONNECTION_TIMEOUT;
12956 case INET_E_CANNOT_CONNECT:
12957 hr = IDS_INET_E_CANNOT_CONNECT;
12960 case INET_E_RESOURCE_NOT_FOUND:
12961 hr = IDS_INET_E_RESOURCE_NOT_FOUND;
12968 case NTE_BAD_ALGID:
12969 case NTE_BAD_FLAGS:
12970 case NTE_BAD_HASH_STATE:
12975 case NTE_BAD_SIGNATURE:
12976 case NTE_SIGNATURE_FILE_BAD:
12977 case CRYPT_E_HASH_VALUE:
12978 hr = IDS_EE_HASH_VAL_FAILED;
12982 hr = IDS_EE_FILELOAD_ERROR_GENERIC;
12990 #ifndef DACCESS_COMPILE
12992 //==========================================================================
12993 // Throw a runtime exception based on the last Win32 error (GetLastError())
12994 //==========================================================================
12995 VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
12998 // before we do anything else...
12999 DWORD err = ::GetLastError();
13004 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13009 RealCOMPlusThrowWin32(HRESULT_FROM_WIN32(err));
13010 } // VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
13012 //==========================================================================
13013 // Throw a runtime exception based on the last Win32 error (GetLastError())
13014 //==========================================================================
13015 VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32(HRESULT hr)
13020 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13025 // Force to ApplicationException for compatibility with previous versions. We would
13026 // prefer a "Win32Exception" here.
13027 EX_THROW(EEMessageException, (kApplicationException, hr, 0 /* resid*/,
13028 NULL /* szArg1 */, NULL /* szArg2 */, NULL /* szArg3 */, NULL /* szArg4 */,
13029 NULL /* szArg5 */, NULL /* szArg6 */));
13030 } // VOID DECLSPEC_NORETURN RealCOMPlusThrowWin32()
13033 //==========================================================================
13034 // Throw an OutOfMemoryError
13035 //==========================================================================
13036 VOID DECLSPEC_NORETURN RealCOMPlusThrowOM()
13041 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13049 ThrowOutOfMemory();
13052 //==========================================================================
13053 // Throw an undecorated runtime exception.
13054 //==========================================================================
13055 VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind)
13060 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13065 _ASSERTE((reKind != kExecutionEngineException) ||
13066 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
13068 EX_THROW(EEException, (reKind));
13071 //==========================================================================
13072 // Throw a decorated runtime exception.
13073 // Try using RealCOMPlusThrow(reKind, wszResourceName) instead.
13074 //==========================================================================
13075 VOID DECLSPEC_NORETURN RealCOMPlusThrowNonLocalized(RuntimeExceptionKind reKind, LPCWSTR wszTag)
13080 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13085 _ASSERTE((reKind != kExecutionEngineException) ||
13086 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
13088 EX_THROW(EEMessageException, (reKind, IDS_EE_GENERIC, wszTag));
13091 //==========================================================================
13092 // Throw a runtime exception based on an HResult
13093 //==========================================================================
13094 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, IErrorInfo* pErrInfo, Exception * pInnerException)
13099 GC_TRIGGERS; // because of IErrorInfo
13104 _ASSERTE (FAILED(hr));
13106 // Though we would like to assert this, it can happen in the following scenario:
13108 // MgdCode --RCW-> COM --CCW-> MgdCode2
13110 // If MgdCode2 throws EEE, when it reaches the RCW, it will invoking MarshalNative::ThrowExceptionForHr and thus,
13111 // reach here. Hence, we will need to keep the assert off, until user code is stopped for creating an EEE.
13113 //_ASSERTE((hr != COR_E_EXECUTIONENGINE) ||
13114 // !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
13116 #ifndef CROSSGEN_COMPILE
13117 #ifdef FEATURE_COMINTEROP
13118 // check for complus created IErrorInfo pointers
13119 if (pErrInfo != NULL)
13123 OBJECTREF oRetVal = NULL;
13124 GCPROTECT_BEGIN(oRetVal);
13125 GetExceptionForHR(hr, pErrInfo, &oRetVal);
13126 _ASSERTE(oRetVal != NULL);
13127 RealCOMPlusThrow(oRetVal);
13131 #endif // FEATURE_COMINTEROP
13133 if (pErrInfo != NULL)
13135 if (pInnerException == NULL)
13137 EX_THROW(EECOMException, (hr, pErrInfo, true, NULL, FALSE));
13141 EX_THROW_WITH_INNER(EECOMException, (hr, pErrInfo, true, NULL, FALSE), pInnerException);
13145 #endif // CROSSGEN_COMPILE
13147 if (pInnerException == NULL)
13149 EX_THROW(EEMessageException, (hr));
13153 EX_THROW_WITH_INNER(EEMessageException, (hr), pInnerException);
13158 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr)
13163 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13169 // ! COMPlusThrowHR(hr) no longer snags the IErrorInfo off the TLS (Too many places
13170 // ! call this routine where no IErrorInfo was set by the prior call.)
13172 // ! If you actually want to pull IErrorInfo off the TLS, call
13174 // ! COMPlusThrowHR(hr, kGetErrorInfo)
13176 RealCOMPlusThrowHR(hr, (IErrorInfo*)NULL);
13180 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, tagGetErrorInfo)
13185 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13190 // Get an IErrorInfo if one is available.
13191 IErrorInfo *pErrInfo = NULL;
13193 #ifndef CROSSGEN_COMPILE
13194 if (SafeGetErrorInfo(&pErrInfo) != S_OK)
13198 // Throw the exception.
13199 RealCOMPlusThrowHR(hr, pErrInfo);
13204 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, UINT resID, LPCWSTR wszArg1,
13205 LPCWSTR wszArg2, LPCWSTR wszArg3, LPCWSTR wszArg4,
13206 LPCWSTR wszArg5, LPCWSTR wszArg6)
13211 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13216 _ASSERTE (FAILED(hr));
13218 // Though we would like to assert this, it can happen in the following scenario:
13220 // MgdCode --RCW-> COM --CCW-> MgdCode2
13222 // If MgdCode2 throws EEE, when it reaches the RCW, it will invoking MarshalNative::ThrowExceptionForHr and thus,
13223 // reach here. Hence, we will need to keep the assert off, until user code is stopped for creating an EEE.
13225 //_ASSERTE((hr != COR_E_EXECUTIONENGINE) ||
13226 // !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
13228 EX_THROW(EEMessageException,
13229 (hr, resID, wszArg1, wszArg2, wszArg3, wszArg4, wszArg5, wszArg6));
13232 //==========================================================================
13233 // Throw a decorated runtime exception with a localized message.
13234 // Queries the ResourceManager for a corresponding resource value.
13235 //==========================================================================
13236 VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, LPCWSTR wszResourceName, Exception * pInnerException)
13241 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13243 PRECONDITION(CheckPointer(wszResourceName));
13247 _ASSERTE((reKind != kExecutionEngineException) ||
13248 !"ExecutionEngineException shouldn't be thrown. Use EEPolicy to failfast or a better exception. The caller of this function should modify their code.");
13250 // For some reason, the compiler complains about unreachable code if
13251 // we don't split the new from the throw. So we're left with this
13252 // unnecessarily verbose syntax.
13255 if (pInnerException == NULL)
13257 EX_THROW(EEResourceException, (reKind, wszResourceName));
13261 EX_THROW_WITH_INNER(EEResourceException, (reKind, wszResourceName), pInnerException);
13265 //==========================================================================
13266 // Used by the classloader to record a managed exception object to explain
13267 // why a classload got botched.
13269 // - Can be called with gc enabled or disabled.
13270 // This allows a catch-all error path to post a generic catchall error
13271 // message w/out bonking more specific error messages posted by inner functions.
13272 //==========================================================================
13273 VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCWSTR pFullTypeName,
13274 LPCWSTR pAssemblyName,
13275 LPCUTF8 pMessageArg,
13281 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13286 EX_THROW(EETypeLoadException, (pFullTypeName, pAssemblyName, pMessageArg, resIDWhy));
13290 //==========================================================================
13291 // Used by the classloader to post illegal layout
13292 //==========================================================================
13293 VOID DECLSPEC_NORETURN ThrowFieldLayoutError(mdTypeDef cl, // cl of the NStruct being loaded
13294 Module* pModule, // Module that defines the scope, loader and heap (for allocate FieldMarshalers)
13295 DWORD dwOffset, // Offset of field
13296 DWORD dwID) // Message id
13301 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13306 IMDInternalImport *pInternalImport = pModule->GetMDImport(); // Internal interface for the NStruct being loaded.
13308 LPCUTF8 pszName, pszNamespace;
13309 if (FAILED(pInternalImport->GetNameOfTypeDef(cl, &pszName, &pszNamespace)))
13311 pszName = pszNamespace = "Invalid TypeDef record";
13314 CHAR offsetBuf[16];
13315 sprintf_s(offsetBuf, COUNTOF(offsetBuf), "%d", dwOffset);
13316 offsetBuf[COUNTOF(offsetBuf) - 1] = '\0';
13318 pModule->GetAssembly()->ThrowTypeLoadException(pszNamespace,
13324 //==========================================================================
13325 // Throw an ArithmeticException
13326 //==========================================================================
13327 VOID DECLSPEC_NORETURN RealCOMPlusThrowArithmetic()
13332 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13337 RealCOMPlusThrow(kArithmeticException);
13340 //==========================================================================
13341 // Throw an ArgumentNullException
13342 //==========================================================================
13343 VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName, LPCWSTR wszResourceName)
13348 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13350 PRECONDITION(CheckPointer(wszResourceName));
13354 EX_THROW(EEArgumentException, (kArgumentNullException, argName, wszResourceName));
13358 VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentNull(LPCWSTR argName)
13363 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13368 EX_THROW(EEArgumentException, (kArgumentNullException, argName, W("ArgumentNull_Generic")));
13372 //==========================================================================
13373 // Throw an ArgumentOutOfRangeException
13374 //==========================================================================
13375 VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentOutOfRange(LPCWSTR argName, LPCWSTR wszResourceName)
13380 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13385 EX_THROW(EEArgumentException, (kArgumentOutOfRangeException, argName, wszResourceName));
13388 //==========================================================================
13389 // Throw an ArgumentException
13390 //==========================================================================
13391 VOID DECLSPEC_NORETURN RealCOMPlusThrowArgumentException(LPCWSTR argName, LPCWSTR wszResourceName)
13396 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13401 EX_THROW(EEArgumentException, (kArgumentException, argName, wszResourceName));
13404 //=========================================================================
13405 // Used by the classloader to record a managed exception object to explain
13406 // why a classload got botched.
13408 // - Can be called with gc enabled or disabled.
13409 // This allows a catch-all error path to post a generic catchall error
13410 // message w/out bonking more specific error messages posted by inner functions.
13411 //==========================================================================
13412 VOID DECLSPEC_NORETURN ThrowTypeLoadException(LPCUTF8 pszNameSpace,
13414 LPCWSTR pAssemblyName,
13415 LPCUTF8 pMessageArg,
13421 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13426 EX_THROW(EETypeLoadException, (pszNameSpace, pTypeName, pAssemblyName, pMessageArg, resIDWhy));
13429 //==========================================================================
13430 // Throw a decorated runtime exception.
13431 //==========================================================================
13432 VOID DECLSPEC_NORETURN RealCOMPlusThrow(RuntimeExceptionKind reKind, UINT resID,
13433 LPCWSTR wszArg1, LPCWSTR wszArg2, LPCWSTR wszArg3,
13434 LPCWSTR wszArg4, LPCWSTR wszArg5, LPCWSTR wszArg6)
13439 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13444 EX_THROW(EEMessageException,
13445 (reKind, resID, wszArg1, wszArg2, wszArg3, wszArg4, wszArg5, wszArg6));
13448 #ifdef FEATURE_COMINTEROP
13449 #ifndef CROSSGEN_COMPILE
13450 //==========================================================================
13451 // Throw a runtime exception based on an HResult, check for error info
13452 //==========================================================================
13453 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(HRESULT hr, IUnknown *iface, REFIID riid)
13458 GC_TRIGGERS; // because of IErrorInfo
13463 IErrorInfo *info = NULL;
13466 info = GetSupportedErrorInfo(iface, riid);
13468 RealCOMPlusThrowHR(hr, info);
13471 //==========================================================================
13472 // Throw a runtime exception based on an EXCEPINFO. This function will free
13473 // the strings in the EXCEPINFO that is passed in.
13474 //==========================================================================
13475 VOID DECLSPEC_NORETURN RealCOMPlusThrowHR(EXCEPINFO *pExcepInfo)
13480 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13485 EX_THROW(EECOMException, (pExcepInfo));
13487 #endif //CROSSGEN_COMPILE
13489 #endif // FEATURE_COMINTEROP
13492 #ifdef FEATURE_STACK_PROBE
13493 //==========================================================================
13494 // Throw a StackOverflowError
13495 //==========================================================================
13496 VOID DECLSPEC_NORETURN RealCOMPlusThrowSO()
13500 // This should be throws... But it isn't because a SO doesn't technically
13501 // fall into the same THROW/NOTHROW conventions as the rest of the contract
13505 DISABLED(GC_NOTRIGGER); // Must sanitize first pass handling to enable this
13511 // We only use BreakOnSO if we are in debug mode, so we'll only checking if the
13512 // _DEBUG flag is set.
13514 static int breakOnSO = -1;
13516 if (breakOnSO == -1)
13517 breakOnSO = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_BreakOnSO);
13519 if (breakOnSO != 0)
13521 _ASSERTE(!"SO occurred");
13525 ThrowStackOverflow();
13529 //==========================================================================
13530 // Throw an InvalidCastException
13531 //==========================================================================
13534 VOID GetAssemblyDetailInfo(SString &sType,
13535 SString &sAssemblyDisplayName,
13536 PEAssembly *pPEAssembly,
13537 SString &sAssemblyDetailInfo)
13539 WRAPPER_NO_CONTRACT;
13541 InlineSString<MAX_LONGPATH> sFormat;
13542 const WCHAR *pwzLoadContext = W("Default");
13544 if (pPEAssembly->GetPath().IsEmpty())
13546 sFormat.LoadResource(CCompRC::Debugging, IDS_EE_CANNOTCAST_HELPER_BYTE);
13548 sAssemblyDetailInfo.Printf(sFormat.GetUnicode(),
13549 sType.GetUnicode(),
13550 sAssemblyDisplayName.GetUnicode(),
13555 sFormat.LoadResource(CCompRC::Debugging, IDS_EE_CANNOTCAST_HELPER_PATH);
13557 sAssemblyDetailInfo.Printf(sFormat.GetUnicode(),
13558 sType.GetUnicode(),
13559 sAssemblyDisplayName.GetUnicode(),
13561 pPEAssembly->GetPath().GetUnicode());
13565 VOID CheckAndThrowSameTypeAndAssemblyInvalidCastException(TypeHandle thCastFrom,
13566 TypeHandle thCastTo)
13575 Module *pModuleTypeFrom = thCastFrom.GetModule();
13576 Module *pModuleTypeTo = thCastTo.GetModule();
13578 if ((pModuleTypeFrom != NULL) && (pModuleTypeTo != NULL))
13580 Assembly *pAssemblyTypeFrom = pModuleTypeFrom->GetAssembly();
13581 Assembly *pAssemblyTypeTo = pModuleTypeTo->GetAssembly();
13583 _ASSERTE(pAssemblyTypeFrom != NULL);
13584 _ASSERTE(pAssemblyTypeTo != NULL);
13586 PEAssembly *pPEAssemblyTypeFrom = pAssemblyTypeFrom->GetManifestFile();
13587 PEAssembly *pPEAssemblyTypeTo = pAssemblyTypeTo->GetManifestFile();
13589 _ASSERTE(pPEAssemblyTypeFrom != NULL);
13590 _ASSERTE(pPEAssemblyTypeTo != NULL);
13592 InlineSString<MAX_LONGPATH> sAssemblyFromDisplayName;
13593 InlineSString<MAX_LONGPATH> sAssemblyToDisplayName;
13595 pPEAssemblyTypeFrom->GetDisplayName(sAssemblyFromDisplayName);
13596 pPEAssemblyTypeTo->GetDisplayName(sAssemblyToDisplayName);
13598 // Found the culprit case. Now format the new exception text.
13599 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastFromName;
13600 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastToName;
13601 InlineSString<MAX_LONGPATH> sAssemblyDetailInfoFrom;
13602 InlineSString<MAX_LONGPATH> sAssemblyDetailInfoTo;
13604 thCastFrom.GetName(strCastFromName);
13605 thCastTo.GetName(strCastToName);
13607 SString typeA = SL(W("A"));
13608 GetAssemblyDetailInfo(typeA,
13609 sAssemblyFromDisplayName,
13610 pPEAssemblyTypeFrom,
13611 sAssemblyDetailInfoFrom);
13612 SString typeB = SL(W("B"));
13613 GetAssemblyDetailInfo(typeB,
13614 sAssemblyToDisplayName,
13616 sAssemblyDetailInfoTo);
13618 COMPlusThrow(kInvalidCastException,
13619 IDS_EE_CANNOTCASTSAME,
13620 strCastFromName.GetUnicode(),
13621 strCastToName.GetUnicode(),
13622 sAssemblyDetailInfoFrom.GetUnicode(),
13623 sAssemblyDetailInfoTo.GetUnicode());
13627 VOID RealCOMPlusThrowInvalidCastException(TypeHandle thCastFrom, TypeHandle thCastTo)
13635 // Use an InlineSString with a size of MAX_CLASSNAME_LENGTH + 1 to prevent
13636 // TypeHandle::GetName from having to allocate a new block of memory. This
13637 // significantly improves the performance of throwing an InvalidCastException.
13638 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastFromName;
13639 InlineSString<MAX_CLASSNAME_LENGTH + 1> strCastToName;
13641 thCastTo.GetName(strCastToName);
13643 thCastFrom.GetName(strCastFromName);
13644 // Attempt to catch the A.T != A.T case that causes so much user confusion.
13645 if (strCastFromName.Equals(strCastToName))
13647 CheckAndThrowSameTypeAndAssemblyInvalidCastException(thCastFrom, thCastTo);
13649 COMPlusThrow(kInvalidCastException, IDS_EE_CANNOTCAST, strCastFromName.GetUnicode(), strCastToName.GetUnicode());
13653 #ifndef CROSSGEN_COMPILE
13654 VOID RealCOMPlusThrowInvalidCastException(OBJECTREF *pObj, TypeHandle thCastTo)
13660 PRECONDITION(IsProtectedByGCFrame (pObj));
13663 TypeHandle thCastFrom = (*pObj)->GetTypeHandle();
13664 #ifdef FEATURE_COMINTEROP
13665 if (thCastFrom.GetMethodTable()->IsComObjectType())
13667 // Special case casting RCWs so we can give better error information when the
13669 ComObject::ThrowInvalidCastException(pObj, thCastTo.GetMethodTable());
13672 COMPlusThrowInvalidCastException(thCastFrom, thCastTo);
13674 #endif // CROSSGEN_COMPILE
13676 #endif // DACCESS_COMPILE
13678 #ifndef CROSSGEN_COMPILE // ???
13679 #ifdef FEATURE_COMINTEROP
13680 #include "comtoclrcall.h"
13681 #endif // FEATURE_COMINTEROP
13683 // Reverse COM interop IL stubs need to catch all exceptions and translate them into HRESULTs.
13684 // But we allow for CSEs to be rethrown. Our corrupting state policy gets applied to the
13685 // original user-visible method that triggered the IL stub to be generated. So we must be able
13686 // to map back from a given IL stub to the user-visible method. Here, we do that only when we
13687 // see a 'matching' ComMethodFrame further up the stack.
13688 MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut)
13695 PRECONDITION(pILStubMD->IsILStub());
13699 MethodDesc * pUserMD = pILStubMD;
13700 #ifdef FEATURE_COMINTEROP
13701 DynamicMethodDesc * pDMD = pILStubMD->AsDynamicMethodDesc();
13702 if (pDMD->IsCOMToCLRStub())
13704 // There are some differences across architectures for "which" SP is passed in.
13705 // On ARM, the SP is the SP on entry to the IL stub, on the other arches, it's
13706 // a post-prolog SP. But this doesn't matter here because the COM->CLR path
13707 // always pushes the Frame in a caller's stack frame.
13709 Frame * pCurFrame = pThread->GetFrame();
13710 while ((UINT_PTR)pCurFrame < uStubSP)
13712 pCurFrame = pCurFrame->PtrNextFrame();
13715 // The construction of the COM->CLR path ensures that our corresponding ComMethodFrame
13716 // should be present further up the stack. Normally, the ComMethodFrame in question is
13717 // simply the next stack frame; however, there are situations where there may be other
13718 // stack frames present (such as an optional ContextTransitionFrame if we switched
13719 // AppDomains, or an inlined stack frame from a QCall in the IL stub).
13720 while (pCurFrame->GetVTablePtr() != ComMethodFrame::GetMethodFrameVPtr())
13722 pCurFrame = pCurFrame->PtrNextFrame();
13725 ComMethodFrame * pComFrame = (ComMethodFrame *)pCurFrame;
13726 _ASSERTE((UINT_PTR)pComFrame > uStubSP);
13728 CONSISTENCY_CHECK_MSG(pComFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr(),
13729 "Expected to find a ComMethodFrame.");
13731 ComCallMethodDesc * pCMD = pComFrame->GetComCallMethodDesc();
13733 CONSISTENCY_CHECK_MSG(pILStubMD == ExecutionManager::GetCodeMethodDesc(pCMD->GetILStub()),
13734 "The ComMethodFrame that we found doesn't match the IL stub passed in.");
13736 pUserMD = pCMD->GetMethodDesc();
13737 *ppFrameOut = pComFrame;
13739 #endif // FEATURE_COMINTEROP
13742 #endif //CROSSGEN_COMPILE