summaryrefslogtreecommitdiff
path: root/src/debug/daccess/dacdbiimplstackwalk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/daccess/dacdbiimplstackwalk.cpp')
-rw-r--r--src/debug/daccess/dacdbiimplstackwalk.cpp1313
1 files changed, 1313 insertions, 0 deletions
diff --git a/src/debug/daccess/dacdbiimplstackwalk.cpp b/src/debug/daccess/dacdbiimplstackwalk.cpp
new file mode 100644
index 0000000000..d3a2a63bbc
--- /dev/null
+++ b/src/debug/daccess/dacdbiimplstackwalk.cpp
@@ -0,0 +1,1313 @@
+// 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.
+//
+// DacDbiImplStackWalk.cpp
+//
+
+//
+// This file contains the implementation of the stackwalking-related functions on the DacDbiInterface.
+//
+// ======================================================================================
+
+#include "stdafx.h"
+#include "dacdbiinterface.h"
+#include "dacdbiimpl.h"
+#include "excepcpu.h"
+
+#if defined(FEATURE_COMINTEROP)
+#include "comtoclrcall.h"
+#include "comcallablewrapper.h"
+#endif // FEATURE_COMINTEROP
+
+typedef IDacDbiInterface::StackWalkHandle StackWalkHandle;
+
+
+// Persistent data needed to do a stackwalk. This is allocated on the forDbi heap.
+// It can survive across multiple DD calls.
+// However, it has data structures that have raw pointers into the DAC cache, and so it must
+// be re-iniatialized after each time the Dac cache is flushed.
+struct StackWalkData
+{
+public:
+ StackWalkData(Thread * pThread, Frame * pFrame, ULONG32 flags) :
+ m_iterator(pThread, NULL, flags)
+ { SUPPORTS_DAC;
+ }
+
+ // Unwrap a handle to get StackWalkData instance.
+ static StackWalkData * FromHandle(StackWalkHandle handle)
+ {
+ SUPPORTS_DAC;
+ _ASSERTE(handle != NULL);
+ return reinterpret_cast<StackWalkData *>(handle);
+ }
+
+ // The stackwalk iterator. This has lots of pointers into the DAC cache.
+ StackFrameIterator m_iterator;
+
+ // The context buffer, which can be pointed to by the RegDisplay.
+ T_CONTEXT m_context;
+
+ // A regdisplay used by the stackwalker.
+ REGDISPLAY m_regdisplay;
+};
+
+// Helper to allocate stackwalk datastructures for given parameters.
+// This is allocated on the local heap (and not via the forDbi allocatoror on the dac-cache), and then
+// freed via code:DacDbiInterfaceImpl::DeleteStackWalk
+//
+// Throws on error (mainly OOM).
+void AllocateStackwalk(StackWalkHandle * pHandle, Thread * pThread, Frame * pFrame, ULONG32 flags)
+{
+ SUPPORTS_DAC;
+
+ StackWalkData * p = new StackWalkData(pThread, NULL, flags); // throews
+
+ StackWalkHandle h = reinterpret_cast<StackWalkHandle>(p);
+ *pHandle = h;
+}
+void DeleteStackwalk(StackWalkHandle pHandle)
+{
+ SUPPORTS_DAC;
+
+ StackWalkData * pBuffer = (StackWalkData *) pHandle;
+ _ASSERTE(pBuffer != NULL);
+ delete pBuffer;
+}
+
+
+// Helper to get the StackFrameIterator from a Stackwalker handle
+StackFrameIterator * GetIteratorFromHandle(StackWalkHandle pSFIHandle)
+{
+ SUPPORTS_DAC;
+
+ StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
+ return &(pBuffer->m_iterator);
+}
+
+// Helper to get a RegDisplay from a Stackwalker handle
+REGDISPLAY * GetRegDisplayFromHandle(StackWalkHandle pSFIHandle)
+{
+ SUPPORTS_DAC;
+ StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
+ return &(pBuffer->m_regdisplay);
+}
+
+// Helper to get a Context buffer from a Stackwalker handle
+T_CONTEXT * GetContextBufferFromHandle(StackWalkHandle pSFIHandle)
+{
+ SUPPORTS_DAC;
+ StackWalkData * pBuffer = StackWalkData::FromHandle(pSFIHandle);
+ return &(pBuffer->m_context);
+}
+
+
+// Create and return a stackwalker on the specified thread.
+void DacDbiInterfaceImpl::CreateStackWalk(VMPTR_Thread vmThread,
+ DT_CONTEXT * pInternalContextBuffer,
+ StackWalkHandle * ppSFIHandle)
+{
+ DD_ENTER_MAY_THROW;
+
+ _ASSERTE(ppSFIHandle != NULL);
+
+ Thread * pThread = vmThread.GetDacPtr();
+
+ // Set the stackwalk flags. We pretty much want to stop at everything.
+ DWORD dwFlags = (NOTIFY_ON_U2M_TRANSITIONS |
+ NOTIFY_ON_NO_FRAME_TRANSITIONS |
+ NOTIFY_ON_INITIAL_NATIVE_CONTEXT);
+
+ // allocate memory for various stackwalker buffers (StackFrameIterator, RegDisplay, Context)
+ AllocateStackwalk(ppSFIHandle, pThread, NULL, dwFlags);
+
+ // initialize the the CONTEXT.
+ // SetStackWalk will initial the RegDisplay from this context.
+ GetContext(vmThread, pInternalContextBuffer);
+
+ // initialize the stackwalker
+ SetStackWalkCurrentContext(vmThread,
+ *ppSFIHandle,
+ SET_CONTEXT_FLAG_ACTIVE_FRAME,
+ pInternalContextBuffer);
+}
+
+// Delete the stackwalk object allocated by code:AllocateStackwalk
+void DacDbiInterfaceImpl::DeleteStackWalk(StackWalkHandle ppSFIHandle)
+{
+ DeleteStackwalk(ppSFIHandle);
+}
+
+// Get the CONTEXT of the current frame at which the stackwalker is stopped.
+void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackWalkHandle pSFIHandle,
+ DT_CONTEXT * pContext)
+{
+ DD_ENTER_MAY_THROW;
+
+ StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
+
+ GetStackWalkCurrentContext(pIter, pContext);
+}
+
+// Internal Worker for GetStackWalkCurrentContext().
+void DacDbiInterfaceImpl::GetStackWalkCurrentContext(StackFrameIterator * pIter,
+ DT_CONTEXT * pContext)
+{
+ // convert the current REGDISPLAY to a CONTEXT
+ CrawlFrame * pCF = &(pIter->m_crawl);
+ UpdateContextFromRegDisp(pCF->GetRegisterSet(), reinterpret_cast<T_CONTEXT *>(pContext));
+}
+
+
+
+// Set the stackwalker to the specified CONTEXT.
+void DacDbiInterfaceImpl::SetStackWalkCurrentContext(VMPTR_Thread vmThread,
+ StackWalkHandle pSFIHandle,
+ CorDebugSetContextFlag flag,
+ DT_CONTEXT * pContext)
+{
+ DD_ENTER_MAY_THROW;
+
+ StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
+ REGDISPLAY * pRD = GetRegDisplayFromHandle(pSFIHandle);
+
+#if defined(_DEBUG)
+ // The caller should have checked this already.
+ _ASSERTE(CheckContext(vmThread, pContext) == S_OK);
+#endif // _DEBUG
+
+ // DD can't keep pointers back into the RS address space.
+ // Allocate a context in DDImpl's memory space. DDImpl can't contain raw pointers back into
+ // the client space since that may not marshal.
+ T_CONTEXT * pContext2 = GetContextBufferFromHandle(pSFIHandle);
+ *pContext2 = *reinterpret_cast<T_CONTEXT *>(pContext); // memcpy
+
+ // update the REGDISPLAY with the given CONTEXT.
+ // Be sure that the context is in DDImpl's memory space and not the Right-sides.
+ FillRegDisplay(pRD, pContext2);
+ BOOL fSuccess = pIter->ResetRegDisp(pRD, (flag == SET_CONTEXT_FLAG_ACTIVE_FRAME));
+ if (!fSuccess)
+ {
+ // ResetRegDisp() may fail for the same reason Init() may fail, i.e.
+ // because the stackwalker tries to unwind one frame ahead of time,
+ // or because the stackwalker needs to filter out some frames based on the stackwalk flags.
+ ThrowHR(E_FAIL);
+ }
+}
+
+
+// Unwind the stackwalker to the next frame.
+BOOL DacDbiInterfaceImpl::UnwindStackWalkFrame(StackWalkHandle pSFIHandle)
+{
+ DD_ENTER_MAY_THROW;
+
+ StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
+
+ CrawlFrame * pCF = &(pIter->m_crawl);
+
+ if ((pIter->GetFrameState() == StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT) ||
+ (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME))
+ {
+ if (IsRuntimeUnwindableStub(GetControlPC(pCF->GetRegisterSet())))
+ {
+ // This is a native stack frame which the StackFrameIterator doesn't know how to unwind.
+ // Use our special unwind logic.
+ return UnwindRuntimeStackFrame(pIter);
+ }
+ }
+
+ // On x86, we need to adjust the stack pointer for the callee parameter adjustment.
+ // This requires us to save the number of bytes used for the stack parameters of the callee.
+ // Thus, let's save it here before we unwind.
+ DWORD cbStackParameterSize = 0;
+ if (pIter->GetFrameState() == StackFrameIterator::SFITER_FRAMELESS_METHOD)
+ {
+ cbStackParameterSize = GetStackParameterSize(pCF->GetCodeInfo());
+ }
+
+ // If the stackwalker is invalid to begin with, we'll just say that it is at the end of the stack.
+ BOOL fIsAtEndOfStack = TRUE;
+ while (pIter->IsValid())
+ {
+ StackWalkAction swa = pIter->Next();
+
+ if (swa == SWA_FAILED)
+ {
+ // The stackwalker is valid to begin with, so this must be a failure case.
+ ThrowHR(E_FAIL);
+ }
+ else if (swa == SWA_CONTINUE)
+ {
+ if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE)
+ {
+ // We are at the end of the stack. We will break at the end of the loop and fIsAtEndOfStack
+ // will be TRUE.
+ }
+ else if ((pIter->GetFrameState() == StackFrameIterator::SFITER_FRAME_FUNCTION) ||
+ (pIter->GetFrameState() == StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION))
+ {
+ // If the stackwalker is stopped at an explicit frame, unwind directly to the next frame.
+ // The V3 stackwalker doesn't stop on explicit frames.
+ continue;
+ }
+ else if (pIter->GetFrameState() == StackFrameIterator::SFITER_NO_FRAME_TRANSITION)
+ {
+ // No frame transitions are not exposed in V2.
+ // Just continue onto the next managed stack frame.
+ continue;
+ }
+ else
+ {
+ fIsAtEndOfStack = FALSE;
+ }
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+
+ // If we get here, then we want to stop at this current frame.
+ break;
+ }
+
+ if (fIsAtEndOfStack == FALSE)
+ {
+ // Currently the only case where we adjust the stack pointer is at M2U transitions.
+ if (pIter->GetFrameState() == StackFrameIterator::SFITER_NATIVE_MARKER_FRAME)
+ {
+ _ASSERTE(!pCF->IsActiveFrame());
+ AdjustRegDisplayForStackParameter(pCF->GetRegisterSet(),
+ cbStackParameterSize,
+ pCF->IsActiveFrame(),
+ kFromManagedToUnmanaged);
+ }
+ }
+
+ return (fIsAtEndOfStack == FALSE);
+}
+
+bool g_fSkipStackCheck = false;
+bool g_fSkipStackCheckInit = false;
+
+// Check whether the specified CONTEXT is valid. The only check we perform right now is whether the
+// SP in the specified CONTEXT is in the stack range of the thread.
+HRESULT DacDbiInterfaceImpl::CheckContext(VMPTR_Thread vmThread,
+ const DT_CONTEXT * pContext)
+{
+ DD_ENTER_MAY_THROW;
+
+ // If the SP in the CONTEXT isn't valid, then there's no point in checking.
+ if ((pContext->ContextFlags & CONTEXT_CONTROL) == 0)
+ {
+ return S_OK;
+ }
+
+ if (!g_fSkipStackCheckInit)
+ {
+ g_fSkipStackCheck = (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgSkipStackCheck) != 0);
+ g_fSkipStackCheckInit = true;
+ }
+
+ // Skip this check if the customer has set the reg key/env var. This is necessary for AutoCad. They
+ // enable fiber mode by calling the Win32 API ConvertThreadToFiber(), but when a managed debugger is
+ // attached, they don't actually call into our hosting APIs such as SwitchInLogicalThreadState(). This
+ // leads to the cached stack range on the Thread object being stale.
+ if (!g_fSkipStackCheck)
+ {
+ // We don't have the backing store boundaries stored on the thread, but this is just
+ // a sanity check anyway.
+ Thread * pThread = vmThread.GetDacPtr();
+ PTR_VOID sp = GetSP(reinterpret_cast<const T_CONTEXT *>(pContext));
+
+ if ((sp < pThread->GetCachedStackLimit()) || (pThread->GetCachedStackBase() <= sp))
+ {
+ return CORDBG_E_NON_MATCHING_CONTEXT;
+ }
+ }
+
+ return S_OK;
+}
+
+// Retrieve information about the current frame from the stackwalker.
+IDacDbiInterface::FrameType DacDbiInterfaceImpl::GetStackWalkCurrentFrameInfo(StackWalkHandle pSFIHandle,
+ DebuggerIPCE_STRData * pFrameData)
+{
+ DD_ENTER_MAY_THROW;
+
+ _ASSERTE(pSFIHandle != NULL);
+
+ StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
+
+ FrameType ftResult = kInvalid;
+ if (pIter->GetFrameState() == StackFrameIterator::SFITER_DONE)
+ {
+ _ASSERTE(!pIter->IsValid());
+ ftResult = kAtEndOfStack;
+ }
+ else
+ {
+ BOOL fInitFrameData = FALSE;
+ switch (pIter->GetFrameState())
+ {
+ case StackFrameIterator::SFITER_UNINITIALIZED:
+ ftResult = kInvalid;
+ break;
+
+ case StackFrameIterator::SFITER_FRAMELESS_METHOD:
+ ftResult = kManagedStackFrame;
+ fInitFrameData = TRUE;
+ break;
+
+ case StackFrameIterator::SFITER_FRAME_FUNCTION:
+ //
+ // fall through
+ //
+ case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
+ ftResult = kExplicitFrame;
+ fInitFrameData = TRUE;
+ break;
+
+ case StackFrameIterator::SFITER_NO_FRAME_TRANSITION:
+ // no-frame transition represents an ExInfo for a native exception on x86.
+ // For all intents and purposes this should be treated just like another explicit frame.
+ ftResult = kExplicitFrame;
+ fInitFrameData = TRUE;
+ break;
+
+ case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME:
+ //
+ // fall through
+ //
+ case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT:
+ if (IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet())))
+ {
+ ftResult = kNativeRuntimeUnwindableStackFrame;
+ fInitFrameData = TRUE;
+ }
+ else
+ {
+ ftResult = kNativeStackFrame;
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ if ((fInitFrameData == TRUE) && (pFrameData != NULL))
+ {
+ InitFrameData(pIter, ftResult, pFrameData);
+ }
+ }
+
+ return ftResult;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Return the number of internal frames on the specified thread.
+//
+// Arguments:
+// vmThread - the thread to be walked
+//
+// Return Value:
+// Return the number of interesting internal frames on the thread.
+//
+// Notes:
+// Internal frames are interesting if they are not of type STUBFRAME_NONE.
+//
+
+ULONG32 DacDbiInterfaceImpl::GetCountOfInternalFrames(VMPTR_Thread vmThread)
+{
+ DD_ENTER_MAY_THROW;
+
+ Thread * pThread = vmThread.GetDacPtr();
+ Frame * pFrame = pThread->GetFrame();
+
+ // We could call EnumerateInternalFrames() here, but it would be a lot of overhead for what we need.
+ ULONG32 uCount = 0;
+ while (pFrame != FRAME_TOP)
+ {
+ CorDebugInternalFrameType ift = GetInternalFrameType(pFrame);
+ if (ift != STUBFRAME_NONE)
+ {
+ uCount++;
+ }
+ pFrame = pFrame->Next();
+ }
+ return uCount;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Enumerate the internal frames on the specified thread and invoke the provided callback on each of them.
+//
+// Arguments:
+// vmThread - the thread to be walked
+// fpCallback - callback function to be invoked for each interesting internal frame
+// pUserData - user-defined custom data to be passed to the callback
+//
+
+void DacDbiInterfaceImpl::EnumerateInternalFrames(VMPTR_Thread vmThread,
+ FP_INTERNAL_FRAME_ENUMERATION_CALLBACK fpCallback,
+ void * pUserData)
+{
+ DD_ENTER_MAY_THROW;
+
+ DebuggerIPCE_STRData frameData;
+
+ Thread * pThread = vmThread.GetDacPtr();
+ Frame * pFrame = pThread->GetFrame();
+ AppDomain * pAppDomain = pThread->GetDomain(INDEBUG(TRUE));
+
+ // This used to be only true for Enter-Managed chains.
+ // Since we don't have chains anymore, this can always be false.
+ frameData.quicklyUnwound = false;
+ frameData.eType = DebuggerIPCE_STRData::cStubFrame;
+
+ while (pFrame != FRAME_TOP)
+ {
+ // check if the internal frame is interesting
+ frameData.stubFrame.frameType = GetInternalFrameType(pFrame);
+ if (frameData.stubFrame.frameType != STUBFRAME_NONE)
+ {
+ frameData.fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pFrame));
+
+ frameData.vmCurrentAppDomainToken.SetHostPtr(pAppDomain);
+
+ MethodDesc * pMD = pFrame->GetFunction();
+#if defined(FEATURE_COMINTEROP)
+ if (frameData.stubFrame.frameType == STUBFRAME_U2M)
+ {
+ _ASSERTE(pMD == NULL);
+
+ // U2M transition frame generally don't store the target MD because we know what the target
+ // is by looking at the callee stack frame. However, for reverse COM interop, we can try
+ // to get the MD for the interface.
+ //
+ // Note that some reverse COM interop cases don't have an intermediate interface MD, so
+ // pMD may still be NULL.
+ //
+ // Even if there is an MD on the ComMethodFrame, it could be in a different appdomain than
+ // the ComMethodFrame itself. The only known scenario is a cross-appdomain reverse COM
+ // interop call. We need to check for this case. The end result is that GetFunction() and
+ // GetFunctionToken() on ICDInternalFrame will return NULL.
+
+ // Minidumps without full memory don't guarantee to capture the CCW since we can do without
+ // it. In this case, pMD will remain NULL.
+ EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
+ {
+ if (pFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr())
+ {
+ ComMethodFrame * pCOMFrame = dac_cast<PTR_ComMethodFrame>(pFrame);
+ PTR_VOID pUnkStackSlot = pCOMFrame->GetPointerToArguments();
+ PTR_IUnknown pUnk = dac_cast<PTR_IUnknown>(*dac_cast<PTR_TADDR>(pUnkStackSlot));
+ ComCallWrapper * pCCW = ComCallWrapper::GetWrapperFromIP(pUnk);
+
+ if (!pCCW->NeedToSwitchDomains(pAppDomain->GetId()))
+ {
+ ComCallMethodDesc * pCMD = NULL;
+ pCMD = dac_cast<PTR_ComCallMethodDesc>(pCOMFrame->ComMethodFrame::GetDatum());
+ pMD = pCMD->GetInterfaceMethodDesc();
+ }
+ }
+ }
+ EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
+ }
+#endif // FEATURE_COMINTEROP
+
+ Module * pModule = (pMD ? pMD->GetModule() : NULL);
+ DomainFile * pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL);
+
+ if (frameData.stubFrame.frameType == STUBFRAME_FUNC_EVAL)
+ {
+ FuncEvalFrame * pFEF = dac_cast<PTR_FuncEvalFrame>(pFrame);
+ DebuggerEval * pDE = pFEF->GetDebuggerEval();
+
+ frameData.stubFrame.funcMetadataToken = pDE->m_methodToken;
+ frameData.stubFrame.vmDomainFile.SetHostPtr(
+ pDE->m_debuggerModule ? pDE->m_debuggerModule->GetDomainFile() : NULL);
+ frameData.stubFrame.vmMethodDesc = VMPTR_MethodDesc::NullPtr();
+ }
+ else
+ {
+ frameData.stubFrame.funcMetadataToken = (pMD == NULL ? NULL : pMD->GetMemberDef());
+ frameData.stubFrame.vmDomainFile.SetHostPtr(pDomainFile);
+ frameData.stubFrame.vmMethodDesc.SetHostPtr(pMD);
+ }
+
+ // invoke the callback
+ fpCallback(&frameData, pUserData);
+ }
+
+ // update the current appdomain if necessary
+ AppDomain * pRetDomain = pFrame->GetReturnDomain();
+ if (pRetDomain != NULL)
+ {
+ pAppDomain = pRetDomain;
+ }
+
+ // move on to the next internal frame
+ pFrame = pFrame->Next();
+ }
+}
+
+// Given the FramePointer of the parent frame and the FramePointer of the current frame,
+// check if the current frame is the parent frame.
+BOOL DacDbiInterfaceImpl::IsMatchingParentFrame(FramePointer fpToCheck, FramePointer fpParent)
+{
+ DD_ENTER_MAY_THROW;
+
+#ifdef WIN64EXCEPTIONS
+ StackFrame sfToCheck = StackFrame((UINT_PTR)fpToCheck.GetSPValue());
+
+ StackFrame sfParent = StackFrame((UINT_PTR)fpParent.GetSPValue());
+
+ // Ask the ExceptionTracker to figure out the answer.
+ // Don't try to compare the StackFrames/FramePointers ourselves.
+ return ExceptionTracker::IsUnwoundToTargetParentFrame(sfToCheck, sfParent);
+
+#else // !WIN64EXCEPTIONS
+ return FALSE;
+
+#endif // WIN64EXCEPTIONS
+}
+
+// Return the stack parameter size of the given method.
+ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(CORDB_ADDRESS controlPC)
+{
+ DD_ENTER_MAY_THROW;
+
+ PCODE currentPC = PCODE(controlPC);
+
+ EECodeInfo codeInfo(currentPC);
+ return GetStackParameterSize(&codeInfo);
+}
+
+// Return the FramePointer of the current frame at which the stackwalker is stopped.
+FramePointer DacDbiInterfaceImpl::GetFramePointer(StackWalkHandle pSFIHandle)
+{
+ DD_ENTER_MAY_THROW;
+
+ StackFrameIterator * pIter = GetIteratorFromHandle(pSFIHandle);
+ return GetFramePointerWorker(pIter);
+}
+
+// Internal helper for GetFramePointer.
+FramePointer DacDbiInterfaceImpl::GetFramePointerWorker(StackFrameIterator * pIter)
+{
+ CrawlFrame * pCF = &(pIter->m_crawl);
+ REGDISPLAY * pRD = pCF->GetRegisterSet();
+
+ FramePointer fp;
+ switch (pIter->GetFrameState())
+ {
+ // For managed methods, we have the full CONTEXT. Additionally, we also have the caller CONTEXT
+ // on WIN64.
+ case StackFrameIterator::SFITER_FRAMELESS_METHOD:
+ fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD));
+ break;
+
+ // In these cases, we only have the full CONTEXT, not the caller CONTEXT.
+ case StackFrameIterator::SFITER_NATIVE_MARKER_FRAME:
+ //
+ // fall through
+ //
+ case StackFrameIterator::SFITER_INITIAL_NATIVE_CONTEXT:
+ fp = FramePointer::MakeFramePointer(GetRegdisplayStackMark(pRD));
+ break;
+
+ // In these cases, we use the address of the explicit frame as the frame marker.
+ case StackFrameIterator::SFITER_FRAME_FUNCTION:
+ //
+ // fall through
+ //
+ case StackFrameIterator::SFITER_SKIPPED_FRAME_FUNCTION:
+ fp = FramePointer::MakeFramePointer(PTR_HOST_TO_TADDR(pCF->GetFrame()));
+ break;
+
+ // No-frame transition represents an ExInfo for a native exception on x86.
+ // For all intents and purposes this should be treated just like another explicit frame.
+ case StackFrameIterator::SFITER_NO_FRAME_TRANSITION:
+ fp = FramePointer::MakeFramePointer(pCF->GetNoFrameTransitionMarker());
+ break;
+
+ case StackFrameIterator::SFITER_UNINITIALIZED:
+ //
+ // fall through
+ //
+ default:
+ UNREACHABLE();
+ }
+
+ return fp;
+}
+
+// Return TRUE if the specified CONTEXT is the CONTEXT of the leaf frame.
+// @dbgtodo filter CONTEXT - Currently we check for the filter CONTEXT first.
+BOOL DacDbiInterfaceImpl::IsLeafFrame(VMPTR_Thread vmThread,
+ const DT_CONTEXT * pContext)
+{
+ DD_ENTER_MAY_THROW;
+
+ DT_CONTEXT ctxLeaf;
+ GetContext(vmThread, &ctxLeaf);
+
+ // Call a platform-specific helper to compare the two contexts.
+ return CompareControlRegisters(pContext, &ctxLeaf);
+}
+
+// This is a simple helper function to convert a CONTEXT to a DebuggerREGDISPLAY. We need to do this
+// inside DDI because the RS has no notion of REGDISPLAY.
+void DacDbiInterfaceImpl::ConvertContextToDebuggerRegDisplay(const DT_CONTEXT * pInContext,
+ DebuggerREGDISPLAY * pOutDRD,
+ BOOL fActive)
+{
+ DD_ENTER_MAY_THROW;
+
+ // This is a bit cumbersome. First we need to convert the CONTEXT into a REGDISPLAY. Then we need
+ // to convert the REGDISPLAY to a DebuggerREGDISPLAY.
+ REGDISPLAY rd;
+ FillRegDisplay(&rd, reinterpret_cast<T_CONTEXT *>(const_cast<DT_CONTEXT *>(pInContext)));
+ SetDebuggerREGDISPLAYFromREGDISPLAY(pOutDRD, &rd);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Fill in the structure with information about the current frame at which the stackwalker is stopped.
+//
+// Arguments:
+// pIter - the stackwalker
+// pFrameData - the structure to be filled out
+//
+
+void DacDbiInterfaceImpl::InitFrameData(StackFrameIterator * pIter,
+ FrameType ft,
+ DebuggerIPCE_STRData * pFrameData)
+{
+ CrawlFrame * pCF = &(pIter->m_crawl);
+
+ //
+ // do common initialization of DebuggerIPCE_STRData for both managed stack frames and explicit frames
+ //
+
+ pFrameData->fp = GetFramePointerWorker(pIter);
+
+ // This used to be only true for Enter-Managed chains.
+ // Since we don't have chains anymore, this can always be false.
+ pFrameData->quicklyUnwound = false;
+
+ AppDomain * pAppDomain = pCF->GetAppDomain();
+ pFrameData->vmCurrentAppDomainToken.SetHostPtr(pAppDomain);
+
+ if (ft == kNativeRuntimeUnwindableStackFrame)
+ {
+ pFrameData->eType = DebuggerIPCE_STRData::cRuntimeNativeFrame;
+
+ GetStackWalkCurrentContext(pIter, &(pFrameData->ctx));
+ }
+ else if (ft == kManagedStackFrame)
+ {
+ MethodDesc * pMD = pCF->GetFunction();
+ Module * pModule = (pMD ? pMD->GetModule() : NULL);
+ // Although MiniDumpNormal tries to dump all AppDomains, it's possible
+ // target corruption will keep one from being present. This should mean
+ // we'll just fail later, but struggle on for now.
+ DomainFile *pDomainFile = NULL;
+ EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
+ {
+ pDomainFile = (pModule ? pModule->GetDomainFile(pAppDomain) : NULL);
+ _ASSERTE(pDomainFile != NULL);
+ }
+ EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
+
+ //
+ // This is a managed stack frame.
+ //
+
+ _ASSERTE(pMD != NULL);
+ _ASSERTE(pModule != NULL);
+
+ //
+ // initialize the rest of the DebuggerIPCE_STRData
+ //
+
+ pFrameData->eType = DebuggerIPCE_STRData::cMethodFrame;
+
+ SetDebuggerREGDISPLAYFromREGDISPLAY(&(pFrameData->rd), pCF->GetRegisterSet());
+
+ GetStackWalkCurrentContext(pIter, &(pFrameData->ctx));
+
+ //
+ // initialize the fields in DebuggerIPCE_STRData::v
+ //
+
+ // These fields will be filled in later. We don't have the sequence point mapping information here.
+ pFrameData->v.ILOffset = (SIZE_T)(-1);
+ pFrameData->v.mapping = MAPPING_NO_INFO;
+
+ // Check if this is a vararg method by getting the managed calling convention from the signature.
+ // Strictly speaking, we can do this in CordbJITILFrame::Init(), but it's just easier and more
+ // efficiently to do it here. CordbJITILFrame::Init() will initialize the other vararg-related
+ // fields. We don't have the native var info here to fully initialize everything.
+ pFrameData->v.fVarArgs = (pMD->IsVarArg() == TRUE);
+
+ pFrameData->v.fNoMetadata = (pMD->IsNoMetadata() == TRUE);
+
+ pFrameData->v.taAmbientESP = pCF->GetAmbientSPFromCrawlFrame();
+ if (pMD->IsSharedByGenericInstantiations())
+ {
+ // This method has a generic type token which is required to figure out the exact instantiation
+ // of the method. CrawlFrame::GetExactGenericArgsToken() can't always successfully retrieve
+ // the token because the JIT doesn't generate the required information all the time. As such,
+ // we need to save the variable index of the generic type token in order to do the look up later.
+ ALLOW_DATATARGET_MISSING_MEMORY(
+ pFrameData->v.exactGenericArgsToken = (GENERICS_TYPE_TOKEN)(dac_cast<TADDR>(pCF->GetExactGenericArgsToken()));
+ );
+
+ if (pMD->AcquiresInstMethodTableFromThis())
+ {
+ // The generic type token is the "this" object.
+ pFrameData->v.dwExactGenericArgsTokenIndex = 0;
+ }
+ else
+ {
+ // The generic type token is one of the secret arguments.
+ pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::TYPECTXT_ILNUM;
+ }
+ }
+ else
+ {
+ pFrameData->v.exactGenericArgsToken = NULL;
+ pFrameData->v.dwExactGenericArgsTokenIndex = (DWORD)ICorDebugInfo::MAX_ILNUM;
+ }
+
+ //
+ // initialize the DebuggerIPCE_FuncData and DebuggerIPCE_JITFuncData
+ //
+
+ DebuggerIPCE_FuncData * pFuncData = &(pFrameData->v.funcData);
+ DebuggerIPCE_JITFuncData * pJITFuncData = &(pFrameData->v.jitFuncData);
+
+ //
+ // initialize the "easy" fields of DebuggerIPCE_FuncData
+ //
+
+ pFuncData->funcMetadataToken = pMD->GetMemberDef();
+ pFuncData->vmDomainFile.SetHostPtr(pDomainFile);
+
+ // PERF: this is expensive to get so I stopped fetching it eagerly
+ // It is only needed if we haven't already got a cached copy
+ pFuncData->classMetadataToken = mdTokenNil;
+
+ //
+ // initialize the remaining fields of DebuggerIPCE_FuncData to the default values
+ //
+
+ pFuncData->ilStartAddress = NULL;
+ pFuncData->ilSize = 0;
+ pFuncData->currentEnCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
+ pFuncData->localVarSigToken = mdSignatureNil;
+
+ //
+ // inititalize the fields of DebuggerIPCE_JITFuncData
+ //
+
+ // For MiniDumpNormal, we do not guarantee method region info for all JIT tokens
+ // is present in the dump.
+ ALLOW_DATATARGET_MISSING_MEMORY(
+ pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(pCF->GetCodeInfo()->GetStartAddress());
+ );
+
+ // PERF: this is expensive to get so I stopped fetching it eagerly
+ // It is only needed if we haven't already got a cached copy
+ pJITFuncData->nativeHotSize = 0;
+ pJITFuncData->nativeStartAddressColdPtr = 0;
+ pJITFuncData->nativeColdSize = 0;
+
+ pJITFuncData->nativeOffset = pCF->GetRelOffset();
+
+ // Here we detect (and set the appropriate flag) if the nativeOffset in the current frame points to the return address of IL_Throw()
+ // (or other exception related JIT helpers like IL_Throw, IL_Rethrow, JIT_RngChkFail, IL_VerificationError, JIT_Overflow etc).
+ // Since return addres point to the next(!) instruction after [call IL_Throw] this sometimes can lead to incorrect exception stacktraces
+ // where a next source line is spotted as an exception origin. This happends when the next instruction after [call IL_Throw] belongs to
+ // a sequence point and a source line different from a sequence point and a source line of [call IL_Throw].
+ // Later on this flag is used in order to adjust nativeOffset and make ICorDebugILFrame::GetIP return IL offset withing
+ // the same sequence point as an actuall IL throw instruction.
+
+ // Here is how we detect it:
+ // We can assume that nativeOffset points to an the instruction after [call IL_Throw] when these conditioins are met:
+ // 1. pCF->IsInterrupted() - Exception has been thrown by this managed frame (frame attr FRAME_ATTR_EXCEPTION)
+ // 2. !pCF->HasFaulted() - It wasn't a "hardware" exception (Access violation, dev by 0, etc.)
+ // 3. !pCF->IsIPadjusted() - It hasn't been previously adjusted to point to [call IL_Throw]
+ // 4. pJITFuncData->nativeOffset != 0 - nativeOffset contains something that looks like a real return address.
+ pJITFuncData->jsutAfterILThrow = pCF->IsInterrupted()
+ && !pCF->HasFaulted()
+ && !pCF->IsIPadjusted()
+ && pJITFuncData->nativeOffset != 0;
+
+ pJITFuncData->nativeCodeJITInfoToken.Set(NULL);
+ pJITFuncData->vmNativeCodeMethodDescToken.SetHostPtr(pMD);
+
+ InitParentFrameInfo(pCF, pJITFuncData);
+
+ ALLOW_DATATARGET_MISSING_MEMORY(
+ pJITFuncData->isInstantiatedGeneric = pMD->HasClassOrMethodInstantiation();
+ );
+ pJITFuncData->enCVersion = CorDB_DEFAULT_ENC_FUNCTION_VERSION;
+
+ // PERF: this is expensive to get so I stopped fetching it eagerly
+ // It is only needed if we haven't already got a cached copy
+ pFuncData->localVarSigToken = 0;
+ pFuncData->ilStartAddress = 0;
+ pFuncData->ilSize = 0;
+
+
+ // See the comment for LookupEnCVersions().
+ // PERF: this is expensive to get so I stopped fetching it eagerly
+ pFuncData->currentEnCVersion = 0;
+ pJITFuncData->enCVersion = 0;
+ }
+ else
+ {
+ _ASSERTE(!"DDII::InitFrameData() - We should never stop at internal frames.");
+ ThrowHR(CORDBG_E_TARGET_INCONSISTENT);
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Initialize the address and the size of the jitted code, including both hot and cold regions.
+//
+// Arguments:
+// methodToken - METHODTOKEN of the method in question; this should actually be the CodeHeader address
+// pJITFuncData - structure to be filled out
+//
+
+void DacDbiInterfaceImpl::InitNativeCodeAddrAndSize(TADDR taStartAddr,
+ DebuggerIPCE_JITFuncData * pJITFuncData)
+{
+ PTR_CORDB_ADDRESS_TYPE pAddr = dac_cast<PTR_CORDB_ADDRESS_TYPE>(taStartAddr);
+ CodeRegionInfo crInfo = CodeRegionInfo::GetCodeRegionInfo(NULL, NULL, pAddr);
+
+ pJITFuncData->nativeStartAddressPtr = PCODEToPINSTR(crInfo.getAddrOfHotCode());
+ pJITFuncData->nativeHotSize = crInfo.getSizeOfHotCode();
+
+ pJITFuncData->nativeStartAddressColdPtr = PCODEToPINSTR(crInfo.getAddrOfColdCode());
+ pJITFuncData->nativeColdSize = crInfo.getSizeOfColdCode();
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Initialize the funclet-related fields of DebuggerIPCE_JITFuncData. This is an nop on non-WIN64 platforms.
+//
+// Arguments:
+// pCF - the CrawlFrame for the current frame
+// pJITFuncData - the structure to be filled out
+//
+
+void DacDbiInterfaceImpl::InitParentFrameInfo(CrawlFrame * pCF,
+ DebuggerIPCE_JITFuncData * pJITFuncData)
+{
+#ifdef WIN64EXCEPTIONS
+ pJITFuncData->fIsFilterFrame = pCF->IsFilterFunclet();
+
+ if (pCF->IsFunclet())
+ {
+ DWORD dwParentOffset;
+ StackFrame sfParent = ExceptionTracker::FindParentStackFrameEx(pCF, &dwParentOffset, NULL);
+
+ //
+ // For funclets, fpParentOrSelf is the FramePointer of the parent.
+ // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back
+ // to the ExceptionTracker when we are checking if a particular frame is the parent frame.
+ //
+
+ pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfParent.SP);
+ pJITFuncData->parentNativeOffset = dwParentOffset;
+ }
+ else
+ {
+ StackFrame sfSelf = ExceptionTracker::GetStackFrameForParentCheck(pCF);
+
+ //
+ // For non-funclets, fpParentOrSelf is the FramePointer of the current frame itself.
+ // Don't mess around with this FramePointer. The only thing we can do with it is to pass it back
+ // to the ExceptionTracker when we are checking if a particular frame is the parent frame.
+ //
+
+ pJITFuncData->fpParentOrSelf = FramePointer::MakeFramePointer(sfSelf.SP);
+ pJITFuncData->parentNativeOffset = 0;
+ }
+#endif // WIN64EXCEPTIONS
+}
+
+// Return the stack parameter size of the given method.
+// Refer to the full comment for the overloaded version.
+ULONG32 DacDbiInterfaceImpl::GetStackParameterSize(EECodeInfo * pCodeInfo)
+{
+ return pCodeInfo->GetCodeManager()->GetStackParameterSize(pCodeInfo);
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// Adjust the stack pointer in the CONTEXT for the stack parameters.
+// This is a nop on non-x86 platforms.
+//
+// Arguments:
+// pRD - the REGDISPLAY to be adjusted
+// cbStackParameterSize - the number of bytes for the stack parameters
+// fIsActiveFrame - whether the CONTEXT is for an active frame
+// StackAdjustmentDirection - whether we are changing a CONTEXT from the managed convention
+// to the unmanaged convention
+//
+// Notes:
+// Consider this code:
+//
+// push 1
+// push 2
+// call Foo
+// -> inc eax
+//
+// Here we are assuming that the return instruction in Foo() pops the stack arguments.
+//
+// Suppose the IP in the CONTEXT is at the arrow. The question is, where should the stack pointer be?
+//
+// 0x0 ret addr for Foo
+// 0x4 2
+// 0x8 1
+// 0xc .....
+//
+// If the CONTEXT is the active frame, i.e. the IP is the active instruction,
+// not the instruction at the return address, then the SP should be at 0xc.
+// However, if the CONTEXT is not active, then the SP can be at either 0x4 or 0xc, depending on
+// the convention used by the stackwalker. The managed stackwalker reports 0xc, but dbghelp reports
+// 0x4. To bridge the gap we have to shim it in the DDI.
+//
+// Currently, we have no way to reliably shim the CONTEXT in all cases. Consider this stack,
+// where U* are native stack frames and M* are managed stack frames:
+//
+// [leaf]
+// U2
+// U1
+// ------- (M2U transition)
+// M2
+// M1
+// M0
+// ------- (U2M transition)
+// U0
+// [root]
+//
+// There are only two transition cases where we can reliably adjust for the callee stack parameter size:
+// 1) when the debugger calls SetContext() with the CONTEXT of the first managed stack frame in a
+// managed stack chain (i.e. SetContext() with M2's CONTEXT)
+// - the M2U transition is protected by an explicit frame (aka Frame-chain frame)
+// 2) when the debugger calls GetContext() on the first native stack frame in a native stack chain
+// (i.e. GetContext() at U0)
+// - we unwind from M0 to U0, so we know the stack parameter size of M0
+//
+// If we want to do the adjustment in all cases, we need to ask the JIT to store the callee stack
+// parameter size in either the unwind info.
+//
+
+void DacDbiInterfaceImpl::AdjustRegDisplayForStackParameter(REGDISPLAY * pRD,
+ DWORD cbStackParameterSize,
+ BOOL fIsActiveFrame,
+ StackAdjustmentDirection direction)
+{
+#if defined(_TARGET_X86_)
+ // If the CONTEXT is active then no adjustment is needed.
+ if (!fIsActiveFrame)
+ {
+ UINT_PTR sp = GetRegdisplaySP(pRD);
+ if (direction == kFromManagedToUnmanaged)
+ {
+ // The CONTEXT comes from the managed world.
+ sp -= cbStackParameterSize;
+ }
+ else
+ {
+ _ASSERTE(!"Currently, we should not hit this case.\n");
+
+ // The CONTEXT comes from the unmanaged world.
+ sp += cbStackParameterSize;
+ }
+ SetRegdisplaySP(pRD, reinterpret_cast<LPVOID>(sp));
+ }
+#endif // _TARGET_X86_
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given an explicit frame, return its frame type in terms of CorDebugInternalFrameType.
+//
+// Arguments:
+// pFrame - the explicit frame in question
+//
+// Return Value:
+// Return the CorDebugInternalFrameType of the explicit frame
+//
+// Notes:
+// I wish this function were simpler, but it's not. The logic in this function is adopted
+// from the logic in the old in-proc debugger stackwalker.
+//
+
+CorDebugInternalFrameType DacDbiInterfaceImpl::GetInternalFrameType(Frame * pFrame)
+{
+ CorDebugInternalFrameType resultType = STUBFRAME_NONE;
+
+ Frame::ETransitionType tt = pFrame->GetTransitionType();
+ Frame::Interception it = pFrame->GetInterception();
+ int ft = pFrame->GetFrameType();
+
+ switch (tt)
+ {
+ case Frame::TT_NONE:
+ if (it == Frame::INTERCEPTION_CLASS_INIT)
+ {
+ resultType = STUBFRAME_CLASS_INIT;
+ }
+ else if (it == Frame::INTERCEPTION_EXCEPTION)
+ {
+ resultType = STUBFRAME_EXCEPTION;
+ }
+ else if (it == Frame::INTERCEPTION_SECURITY)
+ {
+ resultType = STUBFRAME_SECURITY;
+ }
+ else if (it == Frame::INTERCEPTION_PRESTUB)
+ {
+ resultType = STUBFRAME_JIT_COMPILATION;
+ }
+ else
+ {
+ if (ft == Frame::TYPE_FUNC_EVAL)
+ {
+ resultType = STUBFRAME_FUNC_EVAL;
+ }
+ else if (ft == Frame::TYPE_EXIT)
+ {
+ if ((pFrame->GetVTablePtr() != InlinedCallFrame::GetMethodFrameVPtr()) ||
+ InlinedCallFrame::FrameHasActiveCall(pFrame))
+ {
+ resultType = STUBFRAME_M2U;
+ }
+ }
+ }
+ break;
+
+ case Frame::TT_M2U:
+ // Refer to the comment in DebuggerWalkStackProc() for StubDispatchFrame.
+ if (pFrame->GetVTablePtr() != StubDispatchFrame::GetMethodFrameVPtr())
+ {
+ if (it == Frame::INTERCEPTION_SECURITY)
+ {
+ resultType = STUBFRAME_SECURITY;
+ }
+ else
+ {
+ resultType = STUBFRAME_M2U;
+ }
+ }
+ break;
+
+ case Frame::TT_U2M:
+ resultType = STUBFRAME_U2M;
+ break;
+
+ case Frame::TT_AppDomain:
+ resultType = STUBFRAME_APPDOMAIN_TRANSITION;
+ break;
+
+ case Frame::TT_InternalCall:
+ if (it == Frame::INTERCEPTION_EXCEPTION)
+ {
+ resultType = STUBFRAME_EXCEPTION;
+ }
+ else
+ {
+ resultType = STUBFRAME_INTERNALCALL;
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ return resultType;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// This is just a simpler helper function to convert a REGDISPLAY to a CONTEXT.
+//
+// Arguments:
+// pRegDisp - the REGDISPLAY to be converted
+// pContext - the buffer for storing the converted CONTEXT
+//
+
+void DacDbiInterfaceImpl::UpdateContextFromRegDisp(REGDISPLAY * pRegDisp,
+ T_CONTEXT * pContext)
+{
+#if defined(_TARGET_X86_)
+ // Do a partial copy first.
+ pContext->ContextFlags = (CONTEXT_INTEGER | CONTEXT_CONTROL);
+
+ pContext->Edi = *pRegDisp->pEdi;
+ pContext->Esi = *pRegDisp->pEsi;
+ pContext->Ebx = *pRegDisp->pEbx;
+ pContext->Ebp = *pRegDisp->pEbp;
+ pContext->Eax = *pRegDisp->pEax;
+ pContext->Ecx = *pRegDisp->pEcx;
+ pContext->Edx = *pRegDisp->pEdx;
+ pContext->Esp = pRegDisp->Esp;
+ pContext->Eip = pRegDisp->ControlPC;
+
+ // If we still have the pointer to the leaf CONTEXT, and the leaf CONTEXT is the same as the CONTEXT for
+ // the current frame (i.e. the stackwalker is at the leaf frame), then we do a full copy.
+ if ((pRegDisp->pContext != NULL) &&
+ (CompareControlRegisters(const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pContext)),
+ const_cast<const DT_CONTEXT *>(reinterpret_cast<DT_CONTEXT *>(pRegDisp->pContext)))))
+ {
+ *pContext = *pRegDisp->pContext;
+ }
+#else
+ *pContext = *pRegDisp->pCurrentContext;
+#endif
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given the REGDISPLAY of a stack frame for one of the redirect functions, retrieve the original CONTEXT
+// before the thread redirection.
+//
+// Arguments:
+// pRD - the REGDISPLAY of the stack frame in question
+//
+// Return Value:
+// Return the original CONTEXT before the thread got redirected.
+//
+// Assumptions:
+// The caller has checked that the REGDISPLAY is indeed for one of the redirect functions.
+//
+
+PTR_CONTEXT DacDbiInterfaceImpl::RetrieveHijackedContext(REGDISPLAY * pRD)
+{
+ CORDB_ADDRESS ContextPointerAddr = NULL;
+
+ TADDR controlPC = PCODEToPINSTR(GetControlPC(pRD));
+
+ // Check which thread redirection mechanism is used.
+ if (g_pDebugger->m_rgHijackFunction[Debugger::kUnhandledException].IsInRange(controlPC))
+ {
+ // The thread is redirected because of an unhandled exception.
+
+ // The CONTEXT pointer is the last thing pushed onto the stack.
+ // So just read the stack slot at ESP. That will be the TADDR to the CONTEXT.
+ ContextPointerAddr = PTR_TO_CORDB_ADDRESS(GetRegdisplaySP(pRD));
+
+ // Read the CONTEXT from OOP.
+ return *dac_cast<PTR_PTR_CONTEXT>((TADDR)ContextPointerAddr);
+ }
+ else
+ {
+ // The thread is redirected by the EE via code:Thread::RedirectThreadAtHandledJITCase.
+
+ // Convert the REGDISPLAY to a CONTEXT;
+ T_CONTEXT * pContext = NULL;
+
+#if defined(_TARGET_X86_)
+ T_CONTEXT ctx;
+ pContext = &ctx;
+ UpdateContextFromRegDisp(pRD, pContext);
+#else
+ pContext = pRD->pCurrentContext;
+#endif
+
+ // Retrieve the original CONTEXT.
+ return GetCONTEXTFromRedirectedStubStackFrame(pContext);
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Unwind special native stack frame which the runtime knows how to unwind.
+//
+// Arguments:
+// pIter - the StackFrameIterator we are currently using to walk the stack
+//
+// Return Value:
+// Return TRUE if there are more frames to walk, i.e. if we are NOT at the end of the stack.
+//
+// Assumptions:
+// pIter is currently stopped at a special stub which the runtime knows how to unwind.
+//
+// Notes:
+// * Refer to code:DacDbiInterfaceImpl::IsRuntimeUnwindableStub to see how we determine whether a control
+// PC is in a runtime-unwindable stub
+//
+
+BOOL DacDbiInterfaceImpl::UnwindRuntimeStackFrame(StackFrameIterator * pIter)
+{
+ _ASSERTE(IsRuntimeUnwindableStub(GetControlPC(pIter->m_crawl.GetRegisterSet())));
+
+ T_CONTEXT * pContext = NULL;
+ REGDISPLAY * pRD = pIter->m_crawl.GetRegisterSet();
+
+ //
+ // Retrieve the CONTEXT to unwind to and unwind the REGDISPLAY.
+ //
+ pContext = RetrieveHijackedContext(pRD);
+
+ FillRegDisplay(pRD, pContext);
+
+ // Update the StackFrameIterator.
+ BOOL fSuccess = pIter->ResetRegDisp(pRD, true);
+ if (!fSuccess)
+ {
+ // ResetRegDisp() may fail for the same reason Init() may fail, i.e.
+ // because the stackwalker tries to unwind one frame ahead of time,
+ // or because the stackwalker needs to filter out some frames based on the stackwalk flags.
+ ThrowHR(E_FAIL);
+ }
+
+ // Currently we only unwind the hijack function, which will never be the last stack frame.
+ // So return TRUE to indicate that this is not the end of stack.
+ return TRUE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// To aid in doing the stack walk, the shim needs to know if either TS_SyncSuspended or
+// TS_Hijacked is set on a given thread. This DAC helper provides that access.
+//
+// Arguments:
+// vmThread - Thread on which to check the TS_SyncSuspended & TS_Hijacked states
+//
+// Return Value:
+// Return true iff TS_SyncSuspended or TS_Hijacked is set on the specified thread.
+//
+
+bool DacDbiInterfaceImpl::IsThreadSuspendedOrHijacked(VMPTR_Thread vmThread)
+{
+ DD_ENTER_MAY_THROW;
+
+ Thread * pThread = vmThread.GetDacPtr();
+ Thread::ThreadState ts = pThread->GetSnapshotState();
+ if ((ts & Thread::TS_SyncSuspended) != 0)
+ {
+ return true;
+ }
+
+#ifdef FEATURE_HIJACK
+ if ((ts & Thread::TS_Hijacked) != 0)
+ {
+ return true;
+ }
+#endif
+
+ return false;
+}