summaryrefslogtreecommitdiff
path: root/src/vm/exinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/exinfo.cpp')
-rw-r--r--src/vm/exinfo.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/vm/exinfo.cpp b/src/vm/exinfo.cpp
new file mode 100644
index 0000000000..4cbc6d34c4
--- /dev/null
+++ b/src/vm/exinfo.cpp
@@ -0,0 +1,306 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+//
+
+#include "common.h"
+#include "exinfo.h"
+#include "dbginterface.h"
+
+#ifndef DACCESS_COMPILE
+//
+// Destroy the handle within an ExInfo. This respects the fact that we can have preallocated global handles living
+// in ExInfo's.
+//
+void ExInfo::DestroyExceptionHandle(void)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ // Never, ever destroy a preallocated exception handle.
+ if ((m_hThrowable != NULL) && !CLRException::IsPreallocatedExceptionHandle(m_hThrowable))
+ {
+ DestroyHandle(m_hThrowable);
+ }
+
+ m_hThrowable = NULL;
+}
+
+//
+// CopyAndClearSource copies the contents of the given ExInfo into the current ExInfo, then re-initializes the
+// given ExInfo.
+//
+void ExInfo::CopyAndClearSource(ExInfo *from)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ if (GetThread() != NULL) MODE_COOPERATIVE; else MODE_ANY;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+#ifdef _TARGET_X86_
+ LOG((LF_EH, LL_INFO100, "In ExInfo::CopyAndClearSource: m_dEsp=%08x, %08x <- [%08x], stackAddress = 0x%p <- 0x%p\n",
+ from->m_dEsp, &(this->m_dEsp), &from->m_dEsp, this->m_StackAddress, from->m_StackAddress));
+#endif // _TARGET_X86_
+
+ // If we have a handle to an exception object in this ExInfo already, then go ahead and destroy it before we
+ // loose it.
+ DestroyExceptionHandle();
+
+ // The stack address is handled differently. Save the original value.
+ void* stackAddress = this->m_StackAddress;
+
+ // Blast the entire record. Note: we're copying the handle from the source ExInfo to this object. That's okay,
+ // since we're going to clear out the source ExInfo right below this.
+ memcpy(this, from, sizeof(ExInfo));
+
+ // Preserve the stack address. It should never change.
+ m_StackAddress = stackAddress;
+
+ // This ExInfo just took ownership of the handle to the exception object, so clear out that handle in the
+ // source ExInfo.
+ from->m_hThrowable = NULL;
+
+ // Finally, initialize the source ExInfo.
+ from->Init();
+
+ // Clear the Watson Bucketing information as well since they
+ // have been transferred over by the "memcpy" above.
+ from->GetWatsonBucketTracker()->Init();
+}
+
+void ExInfo::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ FORBID_FAULT;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ m_ExceptionFlags.Init();
+ m_StackTraceInfo.Init();
+ m_DebuggerExState.Init();
+
+ m_pSearchBoundary = NULL;
+ STRESS_LOG3(LF_EH, LL_INFO10000, "ExInfo::Init: setting ExInfo:0x%p m_pBottomMostHandler from 0x%p to 0x%p\n",
+ this, m_pBottomMostHandler, NULL);
+ m_pBottomMostHandler = NULL;
+ m_pPrevNestedInfo = NULL;
+ m_ExceptionCode = 0xcccccccc;
+ m_pExceptionRecord = NULL;
+ m_pContext = NULL;
+ m_pShadowSP = NULL;
+ m_StackAddress = this;
+ DestroyExceptionHandle();
+ m_hThrowable = NULL;
+
+#ifdef FEATURE_CORRUPTING_EXCEPTIONS
+ // Initialize the default exception severity to NotCorrupting
+ m_CorruptionSeverity = NotSet;
+#endif // FEATURE_CORRUPTING_EXCEPTIONS
+
+#ifdef FEATURE_EXCEPTION_NOTIFICATIONS
+ // By default, mark the tracker as not having delivered the first
+ // chance exception notification
+ m_fDeliveredFirstChanceNotification = FALSE;
+#endif // FEATURE_EXCEPTION_NOTIFICATIONS
+
+ m_pTopMostHandlerDuringSO = NULL;
+
+#if defined(_TARGET_X86_) && defined(DEBUGGING_SUPPORTED)
+ m_InterceptionContext.Init();
+ m_ValidInterceptionContext = FALSE;
+#endif //_TARGET_X86_ && DEBUGGING_SUPPORTED
+}
+
+ExInfo::ExInfo()
+{
+ WRAPPER_NO_CONTRACT;
+
+ m_hThrowable = NULL;
+ Init();
+
+ // Init the WatsonBucketTracker
+ m_WatsonBucketTracker.Init();
+}
+
+//*******************************************************************************
+// When we hit an endcatch or an unwind and have nested handler info, either
+// 1) we have contained a nested exception and will continue handling the original
+// exception
+// - or -
+// 2) the nested exception was not contained, and was thrown beyond the original
+// bounds where the first exception occurred.
+//
+// The way we can tell this is from the stack pointer. The topmost nested handler is
+// installed at the point where the exception occurred. For a nested exception to be
+// contained, it must be caught within the scope of any code that is called after
+// the nested handler is installed. (remember: after is a lower stack address.)
+//
+// If it is caught by anything earlier on the stack, it was not contained, and we
+// unwind the nested handlers until we get to one that is higher on the stack
+// than the esp we will unwind to.
+//
+// If we still have a nested handler, then we have successfully handled a nested
+// exception and should restore the exception settings that we saved so that
+// processing of the original exception can continue.
+// Otherwise the nested exception has gone beyond where the original exception was
+// thrown and therefore replaces the original exception.
+//
+// We will always remove the current exception info from the chain.
+//
+void ExInfo::UnwindExInfo(VOID* limit)
+{
+ CONTRACTL
+ {
+ NOTHROW; // This function does not throw.
+ GC_NOTRIGGER;
+ if (GetThread() != NULL) MODE_COOPERATIVE; else MODE_ANY;
+ SO_TOLERANT;
+ }
+ CONTRACTL_END;
+
+ // We must be in cooperative mode to do the chaining below
+#ifdef DEBUGGING_SUPPORTED
+ // The debugger thread will be using this, even though it has no
+ // Thread object associated with it.
+ _ASSERTE((GetThread() != NULL && GetThread()->PreemptiveGCDisabled()) ||
+ ((g_pDebugInterface != NULL) && (g_pDebugInterface->GetRCThreadId() == GetCurrentThreadId())));
+#endif // DEBUGGING_SUPPORTED
+
+ LOG((LF_EH, LL_INFO100, "UnwindExInfo: unwind limit is 0x%p, prevNested is 0x%p\n", limit, m_pPrevNestedInfo));
+
+ ExInfo *pPrevNestedInfo = m_pPrevNestedInfo;
+
+ // At first glance, you would think that each nested exception has
+ // been unwound by it's corresponding NestedExceptionHandler. But that's
+ // not necessarily the case. The following assertion cannot be made here,
+ // and the loop is necessary.
+ //
+ //_ASSERTE(pPrevNestedInfo == 0 || (DWORD)pPrevNestedInfo >= limit);
+ //
+ // Make sure we've unwound any nested exceptions that we're going to skip over.
+ //
+ while (pPrevNestedInfo && pPrevNestedInfo->m_StackAddress < limit)
+ {
+ STRESS_LOG1(LF_EH, LL_INFO100, "UnwindExInfo: PopExInfo(): popping nested ExInfo at 0x%p\n", pPrevNestedInfo->m_StackAddress);
+
+ if (pPrevNestedInfo->m_hThrowable != NULL)
+ {
+ pPrevNestedInfo->DestroyExceptionHandle();
+ }
+
+ // Free the Watson bucket details when ExInfo
+ // is being released
+ pPrevNestedInfo->GetWatsonBucketTracker()->ClearWatsonBucketDetails();
+
+ pPrevNestedInfo->m_StackTraceInfo.FreeStackTrace();
+
+ #ifdef DEBUGGING_SUPPORTED
+ if (g_pDebugInterface != NULL)
+ {
+ g_pDebugInterface->DeleteInterceptContext(pPrevNestedInfo->m_DebuggerExState.GetDebuggerInterceptContext());
+ }
+ #endif
+
+ // Get the next nested handler detail...
+ ExInfo* pPrev = pPrevNestedInfo->m_pPrevNestedInfo;
+
+ if (pPrevNestedInfo->IsHeapAllocated())
+ {
+ delete pPrevNestedInfo;
+ }
+
+ pPrevNestedInfo = pPrev;
+ }
+
+ // either clear the one we're about to copy over or the topmost one
+ m_StackTraceInfo.FreeStackTrace();
+
+ if (pPrevNestedInfo)
+ {
+ // found nested handler info that is above the esp restore point so succesfully caught nested
+ STRESS_LOG2(LF_EH, LL_INFO100, "UnwindExInfo: resetting nested ExInfo to 0x%p stackaddress:0x%p\n", pPrevNestedInfo, pPrevNestedInfo->m_StackAddress);
+
+ // Remember if this ExInfo is heap allocated or not.
+ BOOL isHeapAllocated = pPrevNestedInfo->IsHeapAllocated();
+
+ // Copy pPrevNestedInfo to 'this', clearing pPrevNestedInfo in the process.
+ CopyAndClearSource(pPrevNestedInfo);
+
+ if (isHeapAllocated)
+ {
+ delete pPrevNestedInfo; // Now delete the old record if we needed to.
+ }
+ }
+ else
+ {
+ STRESS_LOG0(LF_EH, LL_INFO100, "UnwindExInfo: clearing topmost ExInfo\n");
+
+ // We just do a basic Init of the current top ExInfo here.
+ Init();
+
+ // Init the Watson buckets as well
+ GetWatsonBucketTracker()->ClearWatsonBucketDetails();
+ }
+}
+#endif // DACCESS_COMPILE
+
+
+#ifdef DACCESS_COMPILE
+
+void
+ExInfo::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ // ExInfo is embedded so don't enum 'this'.
+ OBJECTHANDLE_EnumMemoryRegions(m_hThrowable);
+
+ m_pExceptionRecord.EnumMem();
+ m_pContext.EnumMem();
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+void ExInfo::SetExceptionCode(const EXCEPTION_RECORD *pCER)
+{
+#ifndef DACCESS_COMPILE
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ _ASSERTE(pCER != NULL);
+ m_ExceptionCode = pCER->ExceptionCode;
+
+ if (IsInstanceTaggedSEHCode(pCER->ExceptionCode) && ::WasThrownByUs(pCER, pCER->ExceptionCode))
+ {
+ m_ExceptionFlags.SetWasThrownByUs();
+ }
+ else
+ {
+ m_ExceptionFlags.ResetWasThrownByUs();
+ }
+#else // DACCESS_COMPILE
+ // This method is invoked by the X86 version of CLR's exception handler for
+ // managed code. There is no reason why DAC would be invoking this.
+ DacError(E_UNEXPECTED);
+#endif // !DACCESS_COMPILE
+}