summaryrefslogtreecommitdiff
path: root/src/debug/ee/frameinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/ee/frameinfo.cpp')
-rw-r--r--src/debug/ee/frameinfo.cpp2211
1 files changed, 2211 insertions, 0 deletions
diff --git a/src/debug/ee/frameinfo.cpp b/src/debug/ee/frameinfo.cpp
new file mode 100644
index 0000000000..35e5bb9a09
--- /dev/null
+++ b/src/debug/ee/frameinfo.cpp
@@ -0,0 +1,2211 @@
+// 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.
+//*****************************************************************************
+// File: frameinfo.cpp
+//
+
+//
+// Code to find control info about a stack frame.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+// Include so we can get information out of ComMethodFrame
+#ifdef FEATURE_COMINTEROP
+#include "COMToClrCall.h"
+#endif
+
+// Get a frame pointer from a RegDisplay.
+// This is mostly used for chains and stub frames (i.e. internal frames), where we don't need an exact
+// frame pointer. This is why it is okay to use the current SP instead of the caller SP on IA64.
+// We should really rename this and possibly roll it into GetFramePointer() when we move the stackwalker
+// to OOP.
+FramePointer GetSP(REGDISPLAY * pRDSrc)
+{
+ FramePointer fp = FramePointer::MakeFramePointer(
+ (LPVOID)GetRegdisplaySP(pRDSrc));
+
+ return fp;
+}
+
+// Get a frame pointer from a RegDisplay.
+FramePointer GetFramePointer(REGDISPLAY * pRDSrc)
+{
+ return FramePointer::MakeFramePointer(GetRegdisplaySP(pRDSrc));
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Convert a FramePointer to a StackFrame and return it.
+//
+// Arguments:
+// fp - the FramePointer to be converted
+//
+// Return Value:
+// a StackFrame equivalent to the given FramePointer
+//
+// Notes:
+// We really should consolidate the two abstractions for "stack frame identifiers"
+// (StackFrame and FramePointer) when we move the debugger stackwalker to OOP.
+//
+
+FORCEINLINE StackFrame ConvertFPToStackFrame(FramePointer fp)
+{
+ return StackFrame((UINT_PTR)fp.GetSPValue());
+}
+
+/* ------------------------------------------------------------------------- *
+ * DebuggerFrameInfo routines
+ * ------------------------------------------------------------------------- */
+
+//struct DebuggerFrameData: Contains info used by the DebuggerWalkStackProc
+// to do a stack walk. The info and pData fields are handed to the pCallback
+// routine at each frame,
+struct DebuggerFrameData
+{
+ // Initialize this struct. Only done at the start of a stackwalk.
+ void Init(
+ Thread * _pThread,
+ FramePointer _targetFP,
+ BOOL fIgnoreNonmethodFrames, // generally true for stackwalking and false for stepping
+ DebuggerStackCallback _pCallback,
+ void *_pData
+ )
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ this->pCallback = _pCallback;
+ this->pData = _pData;
+
+ this->cRealCounter = 0;
+
+ this->thread = _pThread;
+ this->targetFP = _targetFP;
+ this->targetFound = (_targetFP == LEAF_MOST_FRAME);
+
+ this->ignoreNonmethodFrames = fIgnoreNonmethodFrames;
+
+ // For now, we can tie these to flags together.
+ // In everett, we disable SIS (For backwards compat).
+ this->fProvideInternalFrames = (fIgnoreNonmethodFrames != 0);
+
+ this->fNeedToSendEnterManagedChain = false;
+ this->fTrackingUMChain = false;
+ this->fHitExitFrame = false;
+
+ this->info.eStubFrameType = STUBFRAME_NONE;
+ this->info.quickUnwind = false;
+
+ this->info.frame = NULL;
+ this->needParentInfo = false;
+
+#ifdef WIN64EXCEPTIONS
+ this->fpParent = LEAF_MOST_FRAME;
+ this->info.fIsLeaf = true;
+ this->info.fIsFunclet = false;
+ this->info.fIsFilter = false;
+#endif // WIN64EXCEPTIONS
+
+ // Look strange? Go to definition of this field. I dare you.
+ this->info.fIgnoreThisFrameIfSuppressingUMChainFromComPlusMethodFrameGeneric = false;
+
+#if defined(_DEBUG)
+ this->previousFP = LEAF_MOST_FRAME;
+#endif // _DEBUG
+ }
+
+ // True if we need the next CrawlFrame to fill out part of this FrameInfo's data.
+ bool needParentInfo;
+
+ // The FrameInfo that we'll dispatch to the pCallback. This matches against
+ // the CrawlFrame for that frame that the callback belongs too.
+ FrameInfo info;
+
+ // Regdisplay that the EE stackwalker is updating.
+ REGDISPLAY regDisplay;
+
+
+#ifdef WIN64EXCEPTIONS
+ // This is used to skip funclets in a stackwalk. It marks the frame pointer to which we should skip.
+ FramePointer fpParent;
+#endif // WIN64EXCEPTIONS
+#if defined(_DEBUG)
+ // For debugging, track the previous FramePointer so we can assert that we're
+ // making progress through the stack.
+ FramePointer previousFP;
+#endif // _DEBUG
+
+ // whether we have hit an exit frame or not (i.e. a M2U frame)
+ bool fHitExitFrame;
+
+private:
+ // The scope of this field is each section of managed method frames on the stack.
+ bool fNeedToSendEnterManagedChain;
+
+ // Flag set when we first stack-walk to decide if we want to ignore certain frames.
+ // Stepping doesn't ignore these frames; end user stacktraces do.
+ BOOL ignoreNonmethodFrames;
+
+ // Do we want callbacks for internal frames?
+ // Steppers generally don't. User stack-walk does.
+ bool fProvideInternalFrames;
+
+ // Info for tracking unmanaged chains.
+ // We track the starting (leaf) context for an unmanaged chain, as well as the
+ // ending (root) framepointer.
+ bool fTrackingUMChain;
+ REGDISPLAY rdUMChainStart;
+ FramePointer fpUMChainEnd;
+
+ // Thread that the stackwalk is for.
+ Thread *thread;
+
+
+ // Target FP indicates at what point in the stackwalk we'll start dispatching callbacks.
+ // Naturally, if this is LEAF_MOST_FRAME, then all callbacks will be dispatched
+ FramePointer targetFP;
+ bool targetFound;
+
+ // Count # of callbacks we could have dispatched (assuming targetFP==LEAF_MOST_FRAME).
+ // Useful for detecting leaf.
+ int cRealCounter;
+
+ // Callback & user-data supplied to that callback.
+ DebuggerStackCallback pCallback;
+ void *pData;
+
+ private:
+
+ // Raw invoke. This just does some consistency asserts,
+ // and invokes the callback if we're in the requested target range.
+ StackWalkAction RawInvokeCallback(FrameInfo * pInfo)
+ {
+#ifdef _DEBUG
+ _ASSERTE(pInfo != NULL);
+ MethodDesc * md = pInfo->md;
+ // Invoke the callback to the user. Log what we're invoking.
+ LOG((LF_CORDB, LL_INFO10000, "DSWCallback: MD=%s,0x%p, Chain=%x, Stub=%x, Frame=0x%p, Internal=%d\n",
+ ((md == NULL) ? "None" : md->m_pszDebugMethodName), md,
+ pInfo->chainReason,
+ pInfo->eStubFrameType,
+ pInfo->frame, pInfo->internal));
+
+ // Make sure we're providing a valid FrameInfo for the callback.
+ pInfo->AssertValid();
+#endif
+ // Update counter. This provides a convenient check for leaf FrameInfo.
+ this->cRealCounter++;
+
+
+ // Only invoke if we're past the target.
+ if (!this->targetFound && IsEqualOrCloserToLeaf(this->targetFP, this->info.fp))
+ {
+ this->targetFound = true;
+ }
+
+ if (this->targetFound)
+ {
+ return (pCallback)(pInfo, pData);
+ }
+ else
+ {
+ LOG((LF_CORDB, LL_INFO10000, "Not invoking yet.\n"));
+ }
+
+ return SWA_CONTINUE;
+ }
+
+public:
+ // Invoke a callback. This may do extra logic to preserve the interface between
+ // the LS stackwalker and the LS:
+ // - don't invoke if we're not at the target yet
+ // - send EnterManagedChains if we need it.
+ StackWalkAction InvokeCallback(FrameInfo * pInfo)
+ {
+ // Track if we've sent any managed code yet.
+ // If we haven't, then don't send the enter-managed chain. This catches cases
+ // when we have leaf-most unmanaged chain.
+ if ((pInfo->frame == NULL) && (pInfo->md != NULL))
+ {
+ this->fNeedToSendEnterManagedChain = true;
+ }
+
+
+ // Do tracking to decide if we need to send a Enter-Managed chain.
+ if (pInfo->HasChainMarker())
+ {
+ if (pInfo->managed)
+ {
+ // If we're dispatching a managed-chain, then we don't need to send another one.
+ fNeedToSendEnterManagedChain = false;
+ }
+ else
+ {
+ // If we're dispatching an UM chain, then send the Managed one.
+ // Note that the only unmanaged chains are ThreadStart chains and UM chains.
+ if (fNeedToSendEnterManagedChain)
+ {
+ fNeedToSendEnterManagedChain = false;
+
+ FrameInfo f;
+
+ // Assume entry chain's FP is one pointer-width after the upcoming UM chain.
+ FramePointer fpRoot = FramePointer::MakeFramePointer(
+ (BYTE*) GetRegdisplaySP(&pInfo->registers) - sizeof(DWORD*));
+
+ f.InitForEnterManagedChain(fpRoot);
+ if (RawInvokeCallback(&f) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+ }
+ }
+
+ return RawInvokeCallback(pInfo);
+ }
+
+ // Note that we should start tracking an Unmanaged Chain.
+ void BeginTrackingUMChain(FramePointer fpRoot, REGDISPLAY * pRDSrc)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(!this->fTrackingUMChain);
+
+ CopyREGDISPLAY(&this->rdUMChainStart, pRDSrc);
+
+ this->fTrackingUMChain = true;
+ this->fpUMChainEnd = fpRoot;
+ this->fHitExitFrame = false;
+
+ LOG((LF_CORDB, LL_EVERYTHING, "UM Chain starting at Frame=0x%p\n", this->fpUMChainEnd.GetSPValue()));
+
+ // This UM chain may get cancelled later, so don't even worry about toggling the fNeedToSendEnterManagedChain bit here.
+ // Invoke() will track whether to send an Enter-Managed chain or not.
+ }
+
+ // For various heuristics, we may not want to send an UM chain.
+ void CancelUMChain()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(this->fTrackingUMChain);
+ this->fTrackingUMChain = false;
+ }
+
+ // True iff we're currently tracking an unmanaged chain.
+ bool IsTrackingUMChain()
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ return this->fTrackingUMChain;
+ }
+
+
+
+ // Get/Set Regdisplay that starts an Unmanaged chain.
+ REGDISPLAY * GetUMChainStartRD()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(fTrackingUMChain);
+ return &rdUMChainStart;
+ }
+
+ // Get/Set FramePointer that ends an unmanaged chain.
+ void SetUMChainEnd(FramePointer fp)
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(fTrackingUMChain);
+ fpUMChainEnd = fp;
+ }
+
+ FramePointer GetUMChainEnd()
+ {
+ LIMITED_METHOD_CONTRACT;
+ _ASSERTE(fTrackingUMChain);
+ return fpUMChainEnd;
+ }
+
+ // Get thread we're currently tracing.
+ Thread * GetThread()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return thread;
+ }
+
+ // Returns true if we're on the leaf-callback (ie, we haven't dispatched a callback yet.
+ bool IsLeafCallback()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return cRealCounter == 0;
+ }
+
+ bool ShouldProvideInternalFrames()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return fProvideInternalFrames;
+ }
+ bool ShouldIgnoreNonmethodFrames()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return ignoreNonmethodFrames != 0;
+ }
+};
+
+
+//---------------------------------------------------------------------------------------
+//
+// On IA64, the offset given by the OS during stackwalking is actually the offset at the call instruction.
+// This is different from x86 and X64, where the offset is immediately after the call instruction. In order
+// to have a uniform behaviour, we need to do adjust the relative offset on IA64. This function is a nop on
+// other platforms.
+//
+// Arguments:
+// pCF - the CrawlFrame for the current method frame
+// pInfo - This is the FrameInfo for the current method frame. We need to use the fIsLeaf field,
+// since no adjustment is necessary for leaf frames.
+//
+// Return Value:
+// returns the adjusted relative offset
+//
+
+inline ULONG AdjustRelOffset(CrawlFrame *pCF,
+ FrameInfo *pInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ PRECONDITION(pCF != NULL);
+ }
+ CONTRACTL_END;
+
+#if defined(_TARGET_ARM_)
+ return pCF->GetRelOffset() & ~THUMB_CODE;
+#else
+ return pCF->GetRelOffset();
+#endif
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// Even when there is an exit frame in the explicit frame chain, it does not necessarily mean that we have
+// actually called out to unmanaged code yet or that we actually have a managed call site. Given an exit
+// frame, this function determines if we have a managed call site and have already called out to unmanaged
+// code. If we have, then we return the caller SP as the potential frame pointer. Otherwise we return
+// LEAF_MOST_FRAME.
+//
+// Arguments:
+// pFrame - the exit frame to be checked
+// pData - the state of the current frame maintained by the debugger stackwalker
+// pPotentialFP - This is an out parameter. It returns the caller SP of the last managed caller if
+// there is a managed call site and we have already called out to unmanaged code.
+// Otherwise, LEAF_MOST_FRAME is returned.
+//
+// Return Value:
+// true - we have a managed call site and we have called out to unmanaged code
+// false - otherwise
+//
+
+bool HasExitRuntime(Frame *pFrame, DebuggerFrameData *pData, FramePointer *pPotentialFP)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER; // Callers demand this function be GC_NOTRIGGER.
+ MODE_ANY;
+ PRECONDITION(pFrame->GetFrameType() == Frame::TYPE_EXIT);
+ }
+ CONTRACTL_END;
+
+#ifdef _TARGET_X86_
+ TADDR returnIP, returnSP;
+
+ EX_TRY
+ {
+ // This is a real issue. This may be called while holding GC-forbid locks, and so
+ // this function can't trigger a GC. However, the only impl we have calls GC-trigger functions.
+ CONTRACT_VIOLATION(GCViolation);
+ pFrame->GetUnmanagedCallSite(NULL, &returnIP, &returnSP);
+ }
+ EX_CATCH
+ {
+ // We never expect an actual exception here (maybe in oom).
+ // If we get an exception, then simulate the default behavior for GetUnmanagedCallSite.
+ returnIP = NULL;
+ returnSP = NULL; // this will cause us to return true.
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ LOG((LF_CORDB, LL_INFO100000,
+ "DWSP: TYPE_EXIT: returnIP=0x%08x, returnSP=0x%08x, frame=0x%08x, threadFrame=0x%08x, regSP=0x%08x\n",
+ returnIP, returnSP, pFrame, pData->GetThread()->GetFrame(), GetRegdisplaySP(&pData->regDisplay)));
+
+ if (pPotentialFP != NULL)
+ {
+ *pPotentialFP = FramePointer::MakeFramePointer((void*)returnSP);
+ }
+
+ return ((pFrame != pData->GetThread()->GetFrame()) ||
+ (returnSP == NULL) ||
+ ((TADDR)GetRegdisplaySP(&pData->regDisplay) <= returnSP));
+
+#else // _TARGET_X86_
+ // DebuggerExitFrame always return a NULL returnSP on x86.
+ if (pFrame->GetVTablePtr() == DebuggerExitFrame::GetMethodFrameVPtr())
+ {
+ if (pPotentialFP != NULL)
+ {
+ *pPotentialFP = LEAF_MOST_FRAME;
+ }
+ return true;
+ }
+ else if (pFrame->GetVTablePtr() == InlinedCallFrame::GetMethodFrameVPtr())
+ {
+ InlinedCallFrame *pInlinedFrame = static_cast<InlinedCallFrame *>(pFrame);
+ LPVOID sp = (LPVOID)pInlinedFrame->GetCallSiteSP();
+
+ // The sp returned below is the sp of the caller, which is either an IL stub in the normal case
+ // or a normal managed method in the inlined pinvoke case.
+ // This sp may be the same as the frame's address, so we need to use the largest
+ // possible bsp value to make sure that this frame pointer is closer to the root than
+ // the frame pointer made from the frame address itself.
+ if (pPotentialFP != NULL)
+ {
+ *pPotentialFP = FramePointer::MakeFramePointer( (LPVOID)sp );
+ }
+
+ return ((pFrame != pData->GetThread()->GetFrame()) ||
+ InlinedCallFrame::FrameHasActiveCall(pInlinedFrame));
+
+ }
+ else
+ {
+ // It'll be nice if there's a way to assert that the current frame is indeed of a
+ // derived class of TransitionFrame.
+ TransitionFrame *pTransFrame = static_cast<TransitionFrame*>(pFrame);
+ LPVOID sp = (LPVOID)pTransFrame->GetSP();
+
+ // The sp returned below is the sp of the caller, which is either an IL stub in the normal case
+ // or a normal managed method in the inlined pinvoke case.
+ // This sp may be the same as the frame's address, so we need to use the largest
+ // possible bsp value to make sure that this frame pointer is closer to the root than
+ // the frame pointer made from the frame address itself.
+ if (pPotentialFP != NULL)
+ {
+ *pPotentialFP = FramePointer::MakeFramePointer( (LPVOID)sp );
+ }
+
+ return true;
+ }
+#endif // _TARGET_X86_
+}
+
+#ifdef _DEBUG
+
+//-----------------------------------------------------------------------------
+// Debug helpers to get name of Frame.
+//-----------------------------------------------------------------------------
+LPCUTF8 FrameInfo::DbgGetClassName()
+{
+ return (md == NULL) ? ("None") : (md->m_pszDebugClassName);
+}
+LPCUTF8 FrameInfo::DbgGetMethodName()
+{
+ return (md == NULL) ? ("None") : (md->m_pszDebugMethodName);
+}
+
+
+//-----------------------------------------------------------------------------
+// Debug helper to asserts invariants about a FrameInfo before we dispatch it.
+//-----------------------------------------------------------------------------
+void FrameInfo::AssertValid()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ bool fMethod = this->HasMethodFrame();
+ bool fStub = this->HasStubFrame();
+ bool fChain = this->HasChainMarker();
+
+ // Can't be both Stub & Chain
+ _ASSERTE(!fStub || !fChain);
+
+ // Must be at least a Method, Stub or Chain or Internal
+ _ASSERTE(fMethod || fStub || fChain || this->internal);
+
+ // Check Managed status is consistent
+ if (fMethod)
+ {
+ _ASSERTE(this->managed); // We only report managed methods
+ }
+ if (fChain)
+ {
+ if (!managed)
+ {
+ // Only certain chains can be unmanaged
+ _ASSERTE((this->chainReason == CHAIN_THREAD_START) ||
+ (this->chainReason == CHAIN_ENTER_UNMANAGED));
+ }
+ else
+ {
+ // UM chains can never be managed.
+ _ASSERTE((this->chainReason != CHAIN_ENTER_UNMANAGED));
+ }
+
+ }
+
+ // FramePointer should be valid
+ _ASSERTE(this->fp != LEAF_MOST_FRAME);
+ _ASSERTE((this->fp != ROOT_MOST_FRAME) || (chainReason== CHAIN_THREAD_START) || (chainReason == CHAIN_ENTER_UNMANAGED));
+
+ // If we have a Method, then we need an AppDomain.
+ // (RS will need it to do lookup)
+ if (fMethod)
+ {
+ _ASSERTE(currentAppDomain != NULL);
+ _ASSERTE(managed);
+ // Stubs may have a method w/o any code (eg, PInvoke wrapper).
+ // @todo - Frame::TYPE_TP_METHOD_FRAME breaks this assert. Are there other cases too?
+ //_ASSERTE(fStub || (pIJM != NULL));
+ }
+
+ if (fStub)
+ {
+ // All stubs (except LightWeightFunctions) match up w/a Frame.
+ _ASSERTE(this->frame || (eStubFrameType == STUBFRAME_LIGHTWEIGHT_FUNCTION));
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Get the DJI associated w/ this frame. This is a convenience function.
+// This is recommended over using MethodDescs because DJI's are version-aware.
+//-----------------------------------------------------------------------------
+DebuggerJitInfo * FrameInfo::GetJitInfoFromFrame()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Not all FrameInfo objects correspond to actual code.
+ if (HasChainMarker() || HasStubFrame() || (frame != NULL))
+ {
+ return NULL;
+ }
+
+ DebuggerJitInfo *ji = NULL;
+
+ // @todo - we shouldn't need both a MD and an IP here.
+ EX_TRY
+ {
+ _ASSERTE(this->md != NULL);
+ ji = g_pDebugger->GetJitInfo(this->md, (const BYTE*)GetControlPC(&(this->registers)));
+ _ASSERTE(ji != NULL);
+ _ASSERTE(ji->m_fd == this->md);
+ }
+ EX_CATCH
+ {
+ ji = NULL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return ji;
+}
+
+//-----------------------------------------------------------------------------
+// Get the DMI associated w/ this frame. This is a convenience function.
+// DMIs are 1:1 with the (token, module) pair.
+//-----------------------------------------------------------------------------
+DebuggerMethodInfo * FrameInfo::GetMethodInfoFromFrameOrThrow()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ MethodDesc * pDesc = this->md;
+ mdMethodDef token = pDesc-> GetMemberDef();
+ Module * pRuntimeModule = pDesc->GetModule();
+
+ DebuggerMethodInfo *dmi = g_pDebugger->GetOrCreateMethodInfo(pRuntimeModule, token);
+ return dmi;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init a FrameInfo for a UM chain.
+// We need a stackrange to give to an unmanaged debugger.
+// pRDSrc->Esp will provide the start (leaf) marker.
+// fpRoot will provide the end (root) portion.
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForUMChain(FramePointer fpRoot, REGDISPLAY * pRDSrc)
+{
+ _ASSERTE(pRDSrc != NULL);
+
+ // Mark that we're an UM Chain (and nothing else).
+ this->frame = NULL;
+ this->md = NULL;
+
+ // Fp will be the end (root) of the stack range.
+ // pRDSrc->Sp will be the start (leaf) of the stack range.
+ CopyREGDISPLAY(&(this->registers), pRDSrc);
+ this->fp = fpRoot;
+
+ this->quickUnwind = false;
+ this->internal = false;
+ this->managed = false;
+
+ // These parts of the FrameInfo can be ignored for a UM chain.
+ this->relOffset = 0;
+ this->pIJM = NULL;
+ this->MethodToken = METHODTOKEN(NULL, 0);
+ this->currentAppDomain = NULL;
+ this->exactGenericArgsToken = NULL;
+
+ InitForScratchFrameInfo();
+
+ this->chainReason = CHAIN_ENTER_UNMANAGED;
+ this->eStubFrameType = STUBFRAME_NONE;
+
+#ifdef _DEBUG
+ FramePointer fpLeaf = GetSP(pRDSrc);
+ _ASSERTE(IsCloserToLeaf(fpLeaf, fpRoot));
+#endif
+
+#ifdef _DEBUG
+ // After we just init it, it had better be valid.
+ this->AssertValid();
+#endif
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// This is just a small helper to initialize the fields which are specific to 64-bit. Note that you should
+// only call this function on a scratch FrameInfo. Never call it on the FrameInfo used by the debugger
+// stackwalker to store information on the current frame.
+//
+
+void FrameInfo::InitForScratchFrameInfo()
+{
+#ifdef WIN64EXCEPTIONS
+ // The following flags cannot be trashed when we are calling this function on the curret FrameInfo
+ // (the one we keep track of across multiple stackwalker callbacks). Thus, make sure you do not call
+ // this function from InitForDynamicMethod(). In all other cases, we can call this method after we
+ // call InitFromStubHelper() because we are working on a local scratch variable.
+ this->fIsLeaf = false;
+ this->fIsFunclet = false;
+ this->fIsFilter = false;
+#endif // WIN64EXCEPTIONS
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Init a FrameInfo for a stub. Stub frames map to internal frames on the RS. Stubs which we care about
+// usually contain an explicit frame which translates to an internal frame on the RS. Dynamic method is
+// the sole exception.
+//
+// Arguments:
+// pCF - the CrawlFrame containing the state of the current frame
+// pMDHint - some stubs have associated MethodDesc but others don't,
+// which is why this argument can be NULL
+// type - the type of the stub/internal frame
+//
+
+void FrameInfo::InitFromStubHelper(
+ CrawlFrame * pCF,
+ MethodDesc * pMDHint, // NULL ok
+ CorDebugInternalFrameType type
+)
+{
+ _ASSERTE(pCF != NULL);
+
+ Frame * pFrame = pCF->GetFrame();
+
+ LOG((LF_CORDB, LL_EVERYTHING, "InitFromStubHelper. Frame=0x%p, type=%d\n", pFrame, type));
+
+ // All Stubs have a Frame except for LightWeight methods
+ _ASSERTE((type == STUBFRAME_LIGHTWEIGHT_FUNCTION) || (pFrame != NULL));
+ REGDISPLAY *pRDSrc = pCF->GetRegisterSet();
+
+ this->frame = pFrame;
+
+ // Stub frames may be associated w/ a Method (as a hint). However this method
+ // will never have a JitManager b/c it will never have IL (if it had IL, we'd be a
+ // regulare frame, not a stub frame)
+ this->md = pMDHint;
+
+ CopyREGDISPLAY(&this->registers, pRDSrc);
+
+ // FramePointer must match up w/ an EE Frame b/c that's how we match
+ // we Exception callbacks.
+ if (pFrame != NULL)
+ {
+ this->fp = FramePointer::MakeFramePointer(
+ (LPVOID) pFrame);
+ }
+ else
+ {
+ this->fp = GetSP(pRDSrc);
+ }
+
+ this->quickUnwind = false;
+ this->internal = false;
+ this->managed = true;
+ this->relOffset = 0;
+ this->ambientSP = NULL;
+
+
+ // Method associated w/a stub will never have a JitManager.
+ this->pIJM = NULL;
+ this->MethodToken = METHODTOKEN(NULL, 0);
+ this->currentAppDomain = pCF->GetAppDomain();
+ this->exactGenericArgsToken = NULL;
+
+ // Stub frames are mutually exclusive with chain markers.
+ this->chainReason = CHAIN_NONE;
+ this->eStubFrameType = type;
+
+#ifdef _DEBUG
+ // After we just init it, it had better be valid.
+ this->AssertValid();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Initialize a FrameInfo to be used for an "InternalFrame"
+// Frame should be a derived class of FramedMethodFrame.
+// FrameInfo's MethodDesc will be for managed wrapper for native call.
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForM2UInternalFrame(CrawlFrame * pCF)
+{
+ // For a M2U call, there's a managed method wrapping the unmanaged call. Use that.
+ Frame * pFrame = pCF->GetFrame();
+ _ASSERTE(pFrame->GetTransitionType() == Frame::TT_M2U);
+ FramedMethodFrame * pM2U = static_cast<FramedMethodFrame*> (pFrame);
+ MethodDesc * pMDWrapper = pM2U->GetFunction();
+
+ // Soem M2U transitions may not have a function associated w/ them,
+ // so pMDWrapper may be NULL. PInvokeCalliFrame is an example.
+
+ InitFromStubHelper(pCF, pMDWrapper, STUBFRAME_M2U);
+ InitForScratchFrameInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Initialize for the U2M case...
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForU2MInternalFrame(CrawlFrame * pCF)
+{
+ PREFIX_ASSUME(pCF != NULL);
+ MethodDesc * pMDHint = NULL;
+
+#ifdef FEATURE_COMINTEROP
+ Frame * pFrame = pCF->GetFrame();
+ PREFIX_ASSUME(pFrame != NULL);
+
+
+ // For regular U2M PInvoke cases, we don't care about MD b/c it's just going to
+ // be the next frame.
+ // If we're a COM2CLR call, perhaps we can get the MD for the interface.
+ if (pFrame->GetVTablePtr() == ComMethodFrame::GetMethodFrameVPtr())
+ {
+ ComMethodFrame* pCOMFrame = static_cast<ComMethodFrame*> (pFrame);
+ ComCallMethodDesc* pCMD = reinterpret_cast<ComCallMethodDesc *> (pCOMFrame->ComMethodFrame::GetDatum());
+ pMDHint = pCMD->GetInterfaceMethodDesc();
+
+ // Some COM-interop cases don't have an intermediate interface method desc, so
+ // pMDHint may be null.
+ }
+#endif
+
+ InitFromStubHelper(pCF, pMDHint, STUBFRAME_U2M);
+ InitForScratchFrameInfo();
+}
+
+//-----------------------------------------------------------------------------
+// Init for an AD transition
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForADTransition(CrawlFrame * pCF)
+{
+ Frame * pFrame;
+ pFrame = pCF->GetFrame();
+ _ASSERTE(pFrame->GetTransitionType() == Frame::TT_AppDomain);
+ MethodDesc * pMDWrapper = NULL;
+
+ InitFromStubHelper(pCF, pMDWrapper, STUBFRAME_APPDOMAIN_TRANSITION);
+ InitForScratchFrameInfo();
+}
+
+
+//-----------------------------------------------------------------------------
+// Init frame for a dynamic method.
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForDynamicMethod(CrawlFrame * pCF)
+{
+ // These are just stack markers that there's a dynamic method on the callstack.
+ InitFromStubHelper(pCF, NULL, STUBFRAME_LIGHTWEIGHT_FUNCTION);
+ // Do not call InitForScratchFrameInfo() here! Please refer to the comment in that function.
+}
+
+//-----------------------------------------------------------------------------
+// Init an internal frame to mark a func-eval.
+//-----------------------------------------------------------------------------
+void FrameInfo::InitForFuncEval(CrawlFrame * pCF)
+{
+ // We don't store a MethodDesc hint referring to the method we're going to invoke because
+ // uses of stub frames will assume the MD is relative to the AppDomain the frame is in.
+ // For cross-AD funcevals, we're invoking a method in a domain other than the one this frame
+ // is in.
+ MethodDesc * pMDHint = NULL;
+
+ // Add a stub frame here to mark that there is a FuncEvalFrame on the stack.
+ InitFromStubHelper(pCF, pMDHint, STUBFRAME_FUNC_EVAL);
+ InitForScratchFrameInfo();
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// Initialize a FrameInfo for sending the CHAIN_THREAD_START reason.
+// The common case is that the chain is NOT managed, since the lowest (closest to the root) managed method
+// is usually called from unmanaged code. In fact, in Whidbey, we should never have a managed chain.
+//
+// Arguments:
+// pRDSrc - a REGDISPLAY for the beginning (the leafmost frame) of the chain
+//
+void FrameInfo::InitForThreadStart(Thread * pThread, REGDISPLAY * pRDSrc)
+{
+ this->frame = (Frame *) FRAME_TOP;
+ this->md = NULL;
+ CopyREGDISPLAY(&(this->registers), pRDSrc);
+ this->fp = FramePointer::MakeFramePointer(pThread->GetCachedStackBase());
+ this->quickUnwind = false;
+ this->internal = false;
+ this->managed = false;
+ this->relOffset = 0;
+ this->pIJM = NULL;
+ this->MethodToken = METHODTOKEN(NULL, 0);
+
+ this->currentAppDomain = NULL;
+ this->exactGenericArgsToken = NULL;
+
+ InitForScratchFrameInfo();
+
+ this->chainReason = CHAIN_THREAD_START;
+ this->eStubFrameType = STUBFRAME_NONE;
+
+#ifdef _DEBUG
+ // After we just init it, it had better be valid.
+ this->AssertValid();
+#endif
+}
+
+
+//---------------------------------------------------------------------------------------
+//
+// Initialize a FrameInfo for sending a CHAIN_ENTER_MANAGED.
+// A Enter-Managed chain is always sent immediately before an UM chain, meaning that the Enter-Managed chain
+// is closer to the leaf than the UM chain.
+//
+// Arguments:
+// fpRoot - This is the frame pointer for the Enter-Managed chain. It is currently arbitrarily set
+// to be one stack slot higher (closer to the leaf) than the frame pointer of the beginning
+// of the upcoming UM chain.
+//
+
+void FrameInfo::InitForEnterManagedChain(FramePointer fpRoot)
+{
+ // Nobody should use a EnterManagedChain's Frame*, but there's no
+ // good value to enforce that.
+ this->frame = (Frame *) FRAME_TOP;
+ this->md = NULL;
+ memset((void *)&this->registers, 0, sizeof(this->registers));
+ this->fp = fpRoot;
+
+ this->quickUnwind = true;
+ this->internal = false;
+ this->managed = true;
+ this->relOffset = 0;
+ this->pIJM = NULL;
+ this->MethodToken = METHODTOKEN(NULL, 0);
+
+ this->currentAppDomain = NULL;
+ this->exactGenericArgsToken = NULL;
+
+ InitForScratchFrameInfo();
+
+ this->chainReason = CHAIN_ENTER_MANAGED;
+ this->eStubFrameType = STUBFRAME_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// Do tracking for UM chains.
+// This may invoke the UMChain callback and M2U callback.
+//-----------------------------------------------------------------------------
+StackWalkAction TrackUMChain(CrawlFrame *pCF, DebuggerFrameData *d)
+{
+ Frame *frame = g_pEEInterface->GetFrame(pCF);
+
+ // If we encounter an ExitFrame out in the wild, then we'll convert it to an UM chain.
+ if (!d->IsTrackingUMChain())
+ {
+ if ((frame != NULL) && (frame != FRAME_TOP) && (frame->GetFrameType() == Frame::TYPE_EXIT))
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "DWSP. ExitFrame while not tracking\n"));
+ REGDISPLAY* pRDSrc = pCF->GetRegisterSet();
+
+ d->BeginTrackingUMChain(GetSP(pRDSrc), pRDSrc);
+
+ // fall through and we'll send the UM chain.
+ }
+ else
+ {
+ return SWA_CONTINUE;
+ }
+ }
+
+ _ASSERTE(d->IsTrackingUMChain());
+
+
+ // If we're tracking an UM chain, then we need to:
+ // - possibly refine the start & end values as we get new information in the stacktrace.
+ // - possibly cancel the UM chain for various heuristics.
+ // - possibly dispatch if we've hit managed code again.
+
+ bool fDispatchUMChain = false;
+ // UM Chain stops when managed code starts again.
+ if (frame != NULL)
+ {
+ // If it's just a EE Frame, then update this as a possible end of stack range for the UM chain.
+ // (The end of a stack range is closer to the root.)
+ d->SetUMChainEnd(FramePointer::MakeFramePointer((LPVOID)(frame)));
+
+
+ Frame::ETransitionType t = frame->GetTransitionType();
+ int ft = frame->GetFrameType();
+
+
+ // Sometimes we may not want to show an UM chain b/c we know it's just
+ // code inside of mscorwks. (Eg: Funcevals & AD transitions both fall into this category).
+ // These are perfectly valid UM chains and we could give them if we wanted to.
+ if ((t == Frame::TT_AppDomain) || (ft == Frame::TYPE_FUNC_EVAL))
+ {
+ d->CancelUMChain();
+ return SWA_CONTINUE;
+ }
+
+ // If we hit an M2U frame, then go ahead and dispatch the UM chain now.
+ // This will likely also be an exit frame.
+ if (t == Frame::TT_M2U)
+ {
+ fDispatchUMChain = true;
+ }
+
+ // If we get an Exit frame, we can use that to "prune" the UM chain to a more friendly state.
+ // This heuristic is optional, it just eliminates lots of internal mscorwks frames from the callstack.
+ // Note that this heuristic is only useful if we get a callback on the entry frame
+ // (e.g. UMThkCallFrame) between the callback on the native marker and the callback on the exit frame.
+ // Otherwise the REGDISPLAY will be the same.
+ if (ft == Frame::TYPE_EXIT)
+ {
+ // If we have a valid reg-display (non-null IP) then update it.
+ // We may have an invalid reg-display if we have an exit frame on an inactive thread.
+ REGDISPLAY * pNewRD = pCF->GetRegisterSet();
+ if (GetControlPC(pNewRD) != NULL)
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "DWSP. updating RD while tracking UM chain\n"));
+ CopyREGDISPLAY(d->GetUMChainStartRD(), pNewRD);
+ }
+
+ FramePointer fpLeaf = GetSP(d->GetUMChainStartRD());
+ _ASSERTE(IsCloserToLeaf(fpLeaf, d->GetUMChainEnd()));
+
+
+ _ASSERTE(!d->fHitExitFrame); // should only have 1 exit frame per UM chain code.
+ d->fHitExitFrame = true;
+
+ FramePointer potentialFP;
+
+ FramePointer fpNewChainEnd = d->GetUMChainEnd();
+
+ // Check to see if we are inside the unmanaged call. We want to make sure we only report an exit frame after
+ // we've really exited. There is a short period between where we setup the frame and when we actually exit
+ // the runtime. This check is intended to ensure we're actually outside now.
+ if (HasExitRuntime(frame, d, &potentialFP))
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "HasExitRuntime. potentialFP=0x%p\n", potentialFP.GetSPValue()));
+
+ // If we have no call site, manufacture a FP using the current frame.
+ // If we do have a call site, then the FP is actually going to be the caller SP,
+ // where the caller is the last managed method before calling out to unmanaged code.
+ if (potentialFP == LEAF_MOST_FRAME)
+ {
+ fpNewChainEnd = FramePointer::MakeFramePointer((LPVOID)((BYTE*)frame - sizeof(LPVOID)));
+ }
+ else
+ {
+ fpNewChainEnd = potentialFP;
+ }
+
+ }
+ // For IL stubs, we may actually push an uninitialized InlinedCallFrame frame onto the frame chain
+ // in jitted managed code, and then later on initialize it in a native runtime helper. In this case, if
+ // HasExitRuntime() is false (meaning the frame is uninitialized), then we are actually still in managed
+ // code and have not made the call to native code yet, so we should report an unmanaged chain.
+ else
+ {
+ d->CancelUMChain();
+ return SWA_CONTINUE;
+ }
+
+ fDispatchUMChain = true;
+
+ // If we got a valid chain end, then prune the UM chain accordingly.
+ // Note that some EE Frames will give invalid info back so we have to check.
+ // PInvokeCalliFrame is one example (when doing MC++ function pointers)
+ if (IsCloserToRoot(fpNewChainEnd, fpLeaf))
+ {
+ d->SetUMChainEnd(fpNewChainEnd);
+ }
+ else
+ {
+ _ASSERTE(IsCloserToLeaf(fpLeaf, d->GetUMChainEnd()));
+ }
+ } // end ExitFrame
+
+ // Only CLR internal code / stubs can push Frames onto the Frame chain.
+ // So if we hit a raw interceptor frame before we hit any managed frame, then this whole
+ // UM chain must still be in CLR internal code.
+ // Either way, this UM chain has ended (and some new chain based off the frame has started)
+ // so we need to either Cancel the chain or dispatch it.
+ if (frame->GetInterception() != Frame::INTERCEPTION_NONE)
+ {
+ // Interceptors may contain calls out to unmanaged code (such as unmanaged dllmain when
+ // loading a new dll), so we need to dispatch these.
+ // These extra UM chains don't show in Everett, and so everett debuggers on whidbey
+ // may see new chains.
+ // We need to ensure that whidbey debuggers are updated first.
+ fDispatchUMChain = true;
+ }
+ }
+ else
+ {
+ // If it's a real method (not just an EE Frame), then the UM chain is over.
+ fDispatchUMChain = true;
+ }
+
+
+ if (fDispatchUMChain)
+ {
+ // Check if we should cancel the UM chain.
+
+ // We need to discriminate between the following 2 cases:
+ // 1) Managed -(a)-> mscorwks -(b)-> Managed (leaf)
+ // 2) Native -(a)-> mscorwks -(b)-> Managed (leaf)
+ //
+ // --INCORRECT RATIONALE SEE "CORRECTION" BELOW--
+ // Case 1 could happen if a managed call injects a stub (such as w/ delegates).
+ // In both cases, the (mscorwks-(b)->managed) transition causes a IsNativeMarker callback
+ // which initiates a UM chain. In case 1, we want to cancel the UM chain, but
+ // in case 2 we want to dispatch it.
+ // The difference is case #2 will have some EE Frame at (b) and case #1 won't.
+ // That EE Frame should have caused us to dispatch the call for the managed method, and
+ // thus by the time we get around to dispatching the UM Chain, we shouldn't have a managed
+ // method waiting to be dispatched in the DebuggerFrameData.
+ // --END INCORRECT RATIONALE--
+ //
+ // This is kind of messed up. First of all, the assertions on case 2 is not true on 64-bit.
+ // We won't have an explicit frame at (b). Secondly, case 1 is not always true either.
+ // Consider the case where we are calling a cctor at prestub time. This is what the stack may
+ // look like: managed -> PrestubMethodFrame -> GCFrame -> managed (cctor) (leaf). In this case,
+ // we will actually send the UM chain because we will have dispatched the call for the managed
+ // method (the cctor) when we get a callback for the GCFrame.
+ //
+ // --INCORRECT SEE "CORRECTION" BELOW--
+ // Keep in mind that this is just a heuristic to reduce the number of UM chains we are sending
+ // over to the RS.
+ // --END INCORRECT --
+ //
+ // CORRECTION: These UM chains also feed into the results of at least ControllerStackInfo and probably other
+ // places. Issue 650903 is a concrete example of how not filtering a UM chain causes correctness
+ // issues in the LS. This code may still have bugs in it based on those incorrect assumptions.
+ // A narrow fix for 650903 is the only thing that was changed at the time of adding this comment.
+ if (d->needParentInfo && d->info.HasMethodFrame())
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "Cancelling UM Chain b/c it's internal\n"));
+ d->CancelUMChain();
+ return SWA_CONTINUE;
+ }
+
+ // If we're NOT ignoring non-method frames, and we didn't get an explicit ExitFrame somewhere
+ // in this chain, then don't send the non-leaf UM chain.
+ // The practical cause here is that w/o an exit frame, we don't know where the UM chain
+ // is starting (could be from anywhere in mscorwks). And we can't patch any random spot in
+ // mscorwks.
+ // Sending leaf-UM chains is OK b/c we can't step-out to them (they're the leaf, duh).
+ // (ignoreNonmethodFrames is generally false for stepping and true for regular
+ // end-user stacktraces.)
+ //
+ // This check is probably unnecessary. The client of the debugger stackwalker should make
+ // the decision themselves as to what to do with the UM chain callbacks.
+ //
+ // -- INCORRECT SEE SEE "CORRECTION" BELOW --
+ // Currently, both
+ // ControllerStackInfo and InterceptorStackInfo ignore UM chains completely anyway.
+ // (For an example, refer to the cctor example in the previous comment.)
+ // -- END INCORRECT --
+ //
+ // CORRECTION: See issue 650903 for a concrete example of ControllerStackInfo getting a different
+ // result based on a UM chain that wasn't filtered. This code may still have issues in
+ // it based on those incorrect assumptions. A narrow fix for 650903 is the only thing
+ // that was changed at the time of adding this comment.
+ if (!d->fHitExitFrame && !d->ShouldIgnoreNonmethodFrames() && !d->IsLeafCallback())
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "Cancelling UM Chain b/c it's stepper not requested\n"));
+ d->CancelUMChain();
+ return SWA_CONTINUE;
+ }
+
+
+ // Ok, we haven't cancelled it yet, so go ahead and send the UM chain.
+ FrameInfo f;
+ FramePointer fpRoot = d->GetUMChainEnd();
+ FramePointer fpLeaf = GetSP(d->GetUMChainStartRD());
+
+ // If we didn't actually get any range, then don't bother sending it.
+ if (fpRoot == fpLeaf)
+ {
+ d->CancelUMChain();
+ return SWA_CONTINUE;
+ }
+
+ f.InitForUMChain(fpRoot, d->GetUMChainStartRD());
+
+#ifdef FEATURE_COMINTEROP
+ if ((frame != NULL) &&
+ (frame->GetVTablePtr() == ComPlusMethodFrame::GetMethodFrameVPtr()))
+ {
+ // This condition is part of the fix for 650903. (See
+ // code:ControllerStackInfo::WalkStack and code:DebuggerStepper::TrapStepOut
+ // for the other parts.) Here, we know that the frame we're looking it may be
+ // a ComPlusMethodFrameGeneric (this info is not otherwise plubmed down into
+ // the walker; even though the walker does get to see "f.frame", that may not
+ // be "frame"). Given this, if the walker chooses to ignore these frames
+ // (while doing a Step Out during managed-only debugging), then it can ignore
+ // this frame.
+ f.fIgnoreThisFrameIfSuppressingUMChainFromComPlusMethodFrameGeneric = true;
+ }
+#endif // FEATURE_COMINTEROP
+
+ if (d->InvokeCallback(&f) == SWA_ABORT)
+ {
+ // don't need to cancel if they abort.
+ return SWA_ABORT;
+ }
+ d->CancelUMChain(); // now that we've sent it, we're done.
+
+
+ // Check for a M2U internal frame.
+ if (d->ShouldProvideInternalFrames() && (frame != NULL) && (frame != FRAME_TOP))
+ {
+ // We want to dispatch a M2U transition right after we dispatch the UM chain.
+ Frame::ETransitionType t = frame->GetTransitionType();
+ if (t == Frame::TT_M2U)
+ {
+ // Frame for a M2U transition.
+ FrameInfo fM2U;
+ fM2U.InitForM2UInternalFrame(pCF);
+ if (d->InvokeCallback(&fM2U) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+ }
+
+
+ }
+
+ return SWA_CONTINUE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// A frame pointer is a unique identifier for a particular stack location. This function returns the
+// frame pointer for the current frame, whether it is a method frame or an explicit frame.
+//
+// Arguments:
+// pData - the state of the current frame maintained by the debugger stackwalker
+// pCF - the CrawlFrame for the current callback by the real stackwalker (i.e. StackWalkFramesEx());
+// this is NULL for the case where we fake an extra callbakc to top off a debugger stackwalk
+//
+// Return Value:
+// the frame pointer for the current frame
+//
+
+FramePointer GetFramePointerForDebugger(DebuggerFrameData* pData, CrawlFrame* pCF)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ MODE_ANY;
+ }
+ CONTRACTL_END;
+
+ FramePointer fpResult;
+
+#if defined(WIN64EXCEPTIONS)
+ if (pData->info.frame == NULL)
+ {
+ // This is a managed method frame.
+ fpResult = FramePointer::MakeFramePointer((LPVOID)GetRegdisplayStackMark(&pData->info.registers));
+ }
+ else
+ {
+ // This is an actual frame.
+ fpResult = FramePointer::MakeFramePointer((LPVOID)(pData->info.frame));
+ }
+
+#else // !WIN64EXCEPTIONS
+ if ((pCF == NULL || !pCF->IsFrameless()) && pData->info.frame != NULL)
+ {
+ //
+ // If we're in an explicit frame now, and the previous frame was
+ // also an explicit frame, pPC will not have been updated. So
+ // use the address of the frame itself as fp.
+ //
+ fpResult = FramePointer::MakeFramePointer((LPVOID)(pData->info.frame));
+
+ LOG((LF_CORDB, LL_INFO100000, "GFPFD: Two explicit frames in a row; using frame address 0x%p\n",
+ pData->info.frame));
+ }
+ else
+ {
+ //
+ // Otherwise use pPC as the frame pointer, as this will be
+ // pointing to the return address on the stack.
+ //
+ fpResult = FramePointer::MakeFramePointer((LPVOID)GetRegdisplayStackMark(&(pData->regDisplay)));
+ }
+
+#endif // !WIN64EXCEPTIONS
+
+ LOG((LF_CORDB, LL_INFO100000, "GFPFD: Frame pointer is 0x%p\n", fpResult.GetSPValue()));
+
+ return fpResult;
+}
+
+
+#ifdef WIN64EXCEPTIONS
+//---------------------------------------------------------------------------------------
+//
+// This function is called to determine if we should start skipping funclets. If we should, then we return the
+// frame pointer for the parent method frame. Otherwise we return LEAF_MOST_FRAME. If we are already skipping
+// frames, then we return the current frame pointer for the parent method frame.
+//
+// The return value of this function corresponds to the return value of ExceptionTracker::FindParentStackFrame().
+// Refer to that function for more information.
+//
+// Arguments:
+// fpCurrentParentMarker - This is the current frame pointer of the parent method frame. It can be
+// LEAF_MOST_FRAME if we are not currently skipping funclets.
+// pCF - the CrawlFrame for the current callback from the real stackwalker
+// fIsNonFilterFuncletFrame - whether the current frame is a non-filter funclet frame
+//
+// Return Value:
+// LEAF_MOST_FRAME - skipping not required
+// ROOT_MOST_FRAME - skip one frame and try again
+// anything else - skip all frames up to but not including the returned frame pointer
+//
+
+inline FramePointer CheckForParentFP(FramePointer fpCurrentParentMarker, CrawlFrame* pCF, bool fIsNonFilterFuncletFrame)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (fpCurrentParentMarker == LEAF_MOST_FRAME)
+ {
+ // When we encounter a funclet, we simply stop processing frames until we hit the parent
+ // of the funclet. Funclets and their parents have the same MethodDesc pointers, and they
+ // should really be treated as one frame. However, we report both of them and let the callers
+ // decide what they want to do with them. For example, DebuggerThread::TraceAndSendStack()
+ // should never report both frames, but ControllerStackInfo::GetStackInfo() may need both to
+ // determine where to put a patch. We use the fpParent as a flag to indicate if we are
+ // searching for a parent of a funclet.
+ //
+ // Note that filter funclets are an exception. We don't skip them.
+ if (fIsNonFilterFuncletFrame)
+ {
+ // We really should be using the same structure, but FramePointer is used everywhere in the debugger......
+ StackFrame sfParent = g_pEEInterface->FindParentStackFrame(pCF);
+ return FramePointer::MakeFramePointer((LPVOID)sfParent.SP);
+ }
+ else
+ {
+ return LEAF_MOST_FRAME;
+ }
+ }
+ else
+ {
+ // Just return the current marker if we are already skipping frames.
+ return fpCurrentParentMarker;
+ }
+}
+#endif // WIN64EXCEPTIONS
+
+
+//-----------------------------------------------------------------------------
+// StackWalkAction DebuggerWalkStackProc(): This is the callback called
+// by the EE stackwalker.
+// Note that since we don't know what the frame pointer for frame
+// X is until we've looked at the caller of frame X, we actually end up
+// stashing the info and pData pointers in the DebuggerFrameDat struct, and
+// then invoking pCallback when we've moved up one level, into the caller's
+// frame. We use the needParentInfo field to indicate that the previous frame
+// needed this (parental) info, and so when it's true we should invoke
+// pCallback.
+// What happens is this: if the previous frame set needParentInfo, then we
+// do pCallback (and set needParentInfo to false).
+// Then we look at the current frame - if it's frameless (ie,
+// managed), then we set needParentInfo to callback in the next frame.
+// Otherwise we must be at a chain boundary, and so we set the chain reason
+// appropriately. We then figure out what type of frame it is, setting
+// flags depending on the type. If the user should see this frame, then
+// we'll set needParentInfo to record it's existence. Lastly, if we're in
+// a funky frame, we'll explicitly update the register set, since the
+// CrawlFrame doesn't do it automatically.
+//-----------------------------------------------------------------------------
+StackWalkAction DebuggerWalkStackProc(CrawlFrame *pCF, void *data)
+{
+ DebuggerFrameData *d = (DebuggerFrameData *)data;
+
+ if (pCF->IsNativeMarker())
+ {
+#ifdef WIN64EXCEPTIONS
+ // The tricky part here is that we want to skip all frames between a funclet method frame
+ // and the parent method frame UNLESS the funclet is a filter. Moreover, we should never
+ // let a native marker execute the rest of this method, so we just short-circuit it here.
+ if ((d->fpParent != LEAF_MOST_FRAME) || d->info.IsNonFilterFuncletFrame())
+ {
+ return SWA_CONTINUE;
+ }
+#endif // WIN64EXCEPTIONS
+
+ // This REGDISPLAY is for the native method immediately following the managed method for which
+ // we have received the previous callback, i.e. the native caller of the last managed method
+ // we have encountered.
+ REGDISPLAY* pRDSrc = pCF->GetRegisterSet();
+ d->BeginTrackingUMChain(GetSP(pRDSrc), pRDSrc);
+
+ return SWA_CONTINUE;
+ }
+
+ // Note that a CrawlFrame may have both a methoddesc & an EE Frame.
+ Frame *frame = g_pEEInterface->GetFrame(pCF);
+ MethodDesc *md = pCF->GetFunction();
+
+ LOG((LF_CORDB, LL_EVERYTHING, "Calling DebuggerWalkStackProc. Frame=0x%p, md=0x%p(%s), native_marker=%d\n",
+ frame, md, (md == NULL || md == (MethodDesc*)POISONC) ? "null" : md->m_pszDebugMethodName, pCF->IsNativeMarker() ));
+
+ // The fp for a frame must be obtained from the _next_ frame. Fill it in now for the previous frame, if appropriate.
+ if (d->needParentInfo)
+ {
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: NeedParentInfo.\n"));
+
+ d->info.fp = GetFramePointerForDebugger(d, pCF);
+
+#if defined(_DEBUG) && !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_)
+ // Make sure the stackwalk is making progress.
+ // On ARM this is invalid as the stack pointer does necessarily have to move when unwinding a frame.
+ _ASSERTE(IsCloserToLeaf(d->previousFP, d->info.fp));
+
+ d->previousFP = d->info.fp;
+#endif // _DEBUG && !_TARGET_ARM_
+
+ d->needParentInfo = false;
+
+ {
+ // Don't invoke Stubs if we're not asking for internal frames.
+ bool fDoInvoke = true;
+ if (!d->ShouldProvideInternalFrames())
+ {
+ if (d->info.HasStubFrame())
+ {
+ fDoInvoke = false;
+ }
+ }
+
+ LOG((LF_CORDB, LL_INFO1000000, "DWSP: handling our target\n"));
+
+ if (fDoInvoke)
+ {
+ if (d->InvokeCallback(&d->info) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+
+ // @todo - eventually we should be initing our frame-infos properly
+ // and thus should be able to remove this.
+ d->info.eStubFrameType = STUBFRAME_NONE;
+ }
+ } // if (d->needParentInfo)
+
+
+#ifdef WIN64EXCEPTIONS
+ // The tricky part here is that we want to skip all frames between a funclet method frame
+ // and the parent method frame UNLESS the funclet is a filter. We only have to check for fpParent
+ // here (instead of checking d->info.fIsFunclet and d->info.fIsFilter as well, as in the beginning of
+ // this method) is because at this point, fpParent is already set by the code above.
+ if (d->fpParent == LEAF_MOST_FRAME)
+#endif // WIN64EXCEPTIONS
+ {
+ // Track the UM chain after we flush any managed goo from the last iteration.
+ if (TrackUMChain(pCF, d) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+
+
+ // Track if we want to send a callback for this Frame / Method
+ bool use=false;
+
+ //
+ // Examine the frame.
+ //
+
+ // We assume that the stack walker is just updating the
+ // register display we passed in - assert it to be sure
+ _ASSERTE(pCF->GetRegisterSet() == &d->regDisplay);
+
+#ifdef WIN64EXCEPTIONS
+ Frame* pPrevFrame = d->info.frame;
+
+ // Here we need to determine if we are in a non-leaf frame, in which case we want to adjust the relative offset.
+ // Also, we need to check if this frame has faulted (throws a native exception), since if it has, then it should be
+ // considered the leaf frame (and thus we don't need to update the relative offset).
+ if (pCF->IsActiveFrame() || pCF->HasFaulted())
+ {
+ d->info.fIsLeaf = true;
+ }
+ else if ( (pPrevFrame != NULL) &&
+ (pPrevFrame->GetFrameType() == Frame::TYPE_EXIT) &&
+ !HasExitRuntime(pPrevFrame, d, NULL) )
+ {
+ // This is for the inlined NDirectMethodFrameGeneric case. We have not exit the runtime yet, so the current
+ // frame should still be regarded as the leaf frame.
+ d->info.fIsLeaf = true;
+ }
+ else
+ {
+ d->info.fIsLeaf = false;
+ }
+
+ d->info.fIsFunclet = pCF->IsFunclet();
+ d->info.fIsFilter = false;
+ if (d->info.fIsFunclet)
+ {
+ d->info.fIsFilter = pCF->IsFilterFunclet();
+ }
+
+ if (pCF->IsFrameless())
+ {
+ // Check if we are skipping.
+ if (d->fpParent != LEAF_MOST_FRAME)
+ {
+ // If fpParent is ROOT_MOST_FRAME, then we just need to skip one frame. Otherwise, we should stop
+ // skipping if the current frame pointer matches fpParent. In either case, clear fpParent, and
+ // then check again.
+ if ((d->fpParent == ROOT_MOST_FRAME) ||
+ ExceptionTracker::IsUnwoundToTargetParentFrame(pCF, ConvertFPToStackFrame(d->fpParent)))
+ {
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Stopping to skip funclet at 0x%p.\n", d->fpParent.GetSPValue()));
+
+ d->fpParent = LEAF_MOST_FRAME;
+ d->fpParent = CheckForParentFP(d->fpParent, pCF, d->info.IsNonFilterFuncletFrame());
+ }
+ }
+ }
+
+#endif // WIN64EXCEPTIONS
+
+ d->info.frame = frame;
+ d->info.ambientSP = NULL;
+
+ // Record the appdomain that the thread was in when it
+ // was running code for this frame.
+ d->info.currentAppDomain = pCF->GetAppDomain();
+
+ // Grab all the info from CrawlFrame that we need to
+ // check for "Am I in an exeption code blob?" now.
+
+#ifdef WIN64EXCEPTIONS
+ // We are still searching for the parent of the last funclet we encounter.
+ if (d->fpParent != LEAF_MOST_FRAME)
+ {
+ // We do nothing here.
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Skipping to parent method frame at 0x%p.\n", d->fpParent.GetSPValue()));
+ }
+ else
+#endif // WIN64EXCEPTIONS
+ // We should ignore IL stubs with no frames in our stackwalking.
+ // The only exception is dynamic methods. We want to report them when SIS is turned on.
+ if ((md != NULL) && md->IsILStub() && pCF->IsFrameless())
+ {
+#ifdef FEATURE_STUBS_AS_IL
+ if(md->AsDynamicMethodDesc()->IsMulticastStub())
+ {
+ use = true;
+ d->info.managed = true;
+ d->info.internal = false;
+ }
+#endif
+ // We do nothing here.
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Skip frameless IL stub.\n"));
+ }
+ else
+ // For frames w/o method data, send them as an internal stub frame.
+ if ((md != NULL) && md->IsDynamicMethod())
+ {
+ // Only Send the frame if "InternalFrames" are requested.
+ // Else completely ignore it.
+ if (d->ShouldProvideInternalFrames())
+ {
+ d->info.InitForDynamicMethod(pCF);
+
+ // We'll loop around to get the FramePointer. Only modification to FrameInfo
+ // after this is filling in framepointer and resetting MD.
+ use = true;
+ }
+ }
+ else if (pCF->IsFrameless())
+ {
+ // Regular managed-method.
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Is frameless.\n"));
+ use = true;
+ d->info.managed = true;
+ d->info.internal = false;
+ d->info.chainReason = CHAIN_NONE;
+ d->needParentInfo = true; // Possibly need chain reason
+ d->info.relOffset = AdjustRelOffset(pCF, &(d->info));
+ d->info.pIJM = pCF->GetJitManager();
+ d->info.MethodToken = pCF->GetMethodToken();
+
+#ifdef _TARGET_X86_
+ // This is collecting the ambientSP a lot more than we actually need it. Only time we need it is
+ // inspecting local vars that are based off the ambient esp.
+ d->info.ambientSP = pCF->GetAmbientSPFromCrawlFrame();
+#endif
+ }
+ else
+ {
+ d->info.pIJM = NULL;
+ d->info.MethodToken = METHODTOKEN(NULL, 0);
+
+ //
+ // Retrieve any interception info
+ //
+
+ // Each interception type in the switch statement below is associated with a chain reason.
+ // The other chain reasons are:
+ // CHAIN_INTERCEPTION - not used
+ // CHAIN_PROCESS_START - not used
+ // CHAIN_THREAD_START - thread start
+ // CHAIN_ENTER_MANAGED - managed chain
+ // CHAIN_ENTER_UNMANAGED - unmanaged chain
+ // CHAIN_DEBUGGER_EVAL - not used
+ // CHAIN_CONTEXT_SWITCH - not used
+ // CHAIN_FUNC_EVAL - funceval
+
+ switch (frame->GetInterception())
+ {
+ case Frame::INTERCEPTION_CLASS_INIT:
+ //
+ // Fall through
+ //
+
+ // V2 assumes that the only thing the prestub intercepts is the class constructor
+ case Frame::INTERCEPTION_PRESTUB:
+ d->info.chainReason = CHAIN_CLASS_INIT;
+ break;
+
+ case Frame::INTERCEPTION_EXCEPTION:
+ d->info.chainReason = CHAIN_EXCEPTION_FILTER;
+ break;
+
+ case Frame::INTERCEPTION_CONTEXT:
+ d->info.chainReason = CHAIN_CONTEXT_POLICY;
+ break;
+
+ case Frame::INTERCEPTION_SECURITY:
+ d->info.chainReason = CHAIN_SECURITY;
+ break;
+
+ default:
+ d->info.chainReason = CHAIN_NONE;
+ }
+
+ //
+ // Look at the frame type to figure out how to treat it.
+ //
+
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Chain reason is 0x%X.\n", d->info.chainReason));
+
+ switch (frame->GetFrameType())
+ {
+ case Frame::TYPE_ENTRY: // We now ignore entry + exit frames.
+ case Frame::TYPE_EXIT:
+ case Frame::TYPE_HELPER_METHOD_FRAME:
+ case Frame::TYPE_INTERNAL:
+
+ /* If we have a specific interception type, use it. However, if this
+ is the top-most frame (with a specific type), we can ignore it
+ and it wont appear in the stack-trace */
+#define INTERNAL_FRAME_ACTION(d, use) \
+ (d)->info.managed = true; \
+ (d)->info.internal = false; \
+ use = true
+
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_INTERNAL.\n"));
+ if (d->info.chainReason == CHAIN_NONE || pCF->IsActiveFrame())
+ {
+ use = false;
+ }
+ else
+ {
+ INTERNAL_FRAME_ACTION(d, use);
+ }
+ break;
+
+ case Frame::TYPE_INTERCEPTION:
+ case Frame::TYPE_SECURITY: // Security is a sub-type of interception
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_INTERCEPTION/TYPE_SECURITY.\n"));
+ d->info.managed = true;
+ d->info.internal = true;
+ use = true;
+ break;
+
+ case Frame::TYPE_CALL:
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_CALL.\n"));
+ // In V4, StubDispatchFrame is only used on 64-bit (and PPC?) but not on x86. x86 uses a
+ // different code path which sets up a HelperMethodFrame instead. In V4.5, x86 and ARM
+ // both use the 64-bit code path and they set up a StubDispatchFrame as well. This causes
+ // a problem in the debugger stackwalker (see Dev11 Issue 13229) since the two frame types
+ // are treated differently. More specifically, a StubDispatchFrame causes the debugger
+ // stackwalk to make an invalid callback, i.e. a callback which is not for a managed method,
+ // an explicit frame, or a chain.
+ //
+ // Ideally we would just change the StubDispatchFrame to behave like a HMF, but it's
+ // too big of a change for an in-place release. For now I'm just making surgical fixes in
+ // the debugger stackwalker. This may introduce behavioural changes in on X64, but the
+ // chance of that is really small. StubDispatchFrame is only used in the virtual stub
+ // disptch code path. It stays on the stack in a small time window and it's not likely to
+ // be on the stack while some managed methods closer to the leaf are on the stack. There is
+ // only one scenario I know of, and that's the repro for Dev11 13229, but that's for x86 only.
+ // The jitted code on X64 behaves differently.
+ //
+ // Note that there is a corresponding change in DacDbiInterfaceImpl::GetInternalFrameType().
+ if (frame->GetVTablePtr() == StubDispatchFrame::GetMethodFrameVPtr())
+ {
+ use = false;
+ }
+ else
+ {
+ d->info.managed = true;
+ d->info.internal = false;
+ use = true;
+ }
+ break;
+
+ case Frame::TYPE_FUNC_EVAL:
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_FUNC_EVAL.\n"));
+ d->info.managed = true;
+ d->info.internal = true;
+ // This is actually a nop. We reset the chain reason in InitForFuncEval() below.
+ // So is a FuncEvalFrame a chain or an internal frame?
+ d->info.chainReason = CHAIN_FUNC_EVAL;
+
+ {
+ // We only show a FuncEvalFrame if the funceval is not trying to abort the thread.
+ FuncEvalFrame *pFuncEvalFrame = static_cast<FuncEvalFrame *>(frame);
+ use = pFuncEvalFrame->ShowFrame() ? true : false;
+ }
+
+ // Send Internal frame. This is "inside" (leafmost) the chain, so we send it first
+ // since sending starts from the leaf.
+ if (use && d->ShouldProvideInternalFrames())
+ {
+ FrameInfo f;
+ f.InitForFuncEval(pCF);
+ if (d->InvokeCallback(&f) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+
+ break;
+
+ // Put frames we want to ignore here:
+ case Frame::TYPE_MULTICAST:
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_MULTICAST.\n"));
+ if (d->ShouldIgnoreNonmethodFrames())
+ {
+ // Multicast frames exist only to gc protect the arguments
+ // between invocations of a delegate. They don't have code that
+ // we can (currently) show the user (we could change this with
+ // work, but why bother? It's an internal stub, and even if the
+ // user could see it, they can't modify it).
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Skipping frame 0x%x b/c it's "
+ "a multicast frame!\n", frame));
+ use = false;
+ }
+ else
+ {
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: NOT Skipping frame 0x%x even thought it's "
+ "a multicast frame!\n", frame));
+ INTERNAL_FRAME_ACTION(d, use);
+ }
+ break;
+
+#ifdef FEATURE_REMOTING
+ case Frame::TYPE_TP_METHOD_FRAME:
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Frame type is TYPE_TP_METHOD_FRAME.\n"));
+ if (d->ShouldIgnoreNonmethodFrames())
+ {
+ // Transparant Proxies push a frame onto the stack that they
+ // use to figure out where they're really going; this frame
+ // doesn't actually contain any code, although it does have
+ // enough info into fooling our routines into thinking it does:
+ // Just ignore these.
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Skipping frame 0x%x b/c it's "
+ "a transparant proxy frame!\n", frame));
+ use = false;
+ }
+ else
+ {
+ // Otherwise do the same thing as for internal frames
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: NOT Skipping frame 0x%x even though it's "
+ "a transparant proxy frame!\n", frame));
+ INTERNAL_FRAME_ACTION(d, use);
+ }
+ break;
+#endif
+ default:
+ _ASSERTE(!"Invalid frame type!");
+ break;
+ }
+ }
+
+
+ // Check for ICorDebugInternalFrame stuff.
+ // These callbacks are dispatched out of band.
+ if (d->ShouldProvideInternalFrames() && (frame != NULL) && (frame != FRAME_TOP))
+ {
+ Frame::ETransitionType t = frame->GetTransitionType();
+ FrameInfo f;
+ bool fUse = false;
+
+ if (t == Frame::TT_U2M)
+ {
+ // We can invoke the Internal U2M frame now.
+ f.InitForU2MInternalFrame(pCF);
+ fUse = true;
+ }
+ else if (t == Frame::TT_AppDomain)
+ {
+ // Internal frame for an Appdomain transition.
+ // We used to ignore frames for ADs which we hadn't sent a Create event for yet. In V3 we send AppDomain
+ // create events immediately (before any assemblies are loaded), so this should no longer be an issue.
+ f.InitForADTransition(pCF);
+ fUse = true;
+ }
+
+ // Frame's setup. Now invoke the callback.
+ if (fUse)
+ {
+ if (d->InvokeCallback(&f) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+ } // should we give frames?
+
+
+
+ if (use)
+ {
+ //
+ // If we are returning a complete stack walk from the helper thread, then we
+ // need to gather information to instantiate generics. However, a stepper doing
+ // a stackwalk does not need this information, so skip in that case.
+ //
+ if (d->ShouldIgnoreNonmethodFrames())
+ {
+ // Finding sizes of value types on the argument stack while
+ // looking for the arg runs the class loader in non-load mode.
+ ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
+ d->info.exactGenericArgsToken = pCF->GetExactGenericArgsToken();
+ }
+ else
+ {
+ d->info.exactGenericArgsToken = NULL;
+ }
+
+ d->info.md = md;
+ CopyREGDISPLAY(&(d->info.registers), &(d->regDisplay));
+
+#if defined(_TARGET_AMD64_)
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Saving REGDISPLAY with sp = 0x%p, pc = 0x%p.\n",
+ GetRegdisplaySP(&(d->info.registers)),
+ GetControlPC(&(d->info.registers))));
+#endif // _TARGET_AMD64_
+
+ d->needParentInfo = true;
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: Setting needParentInfo\n"));
+ }
+
+#if defined(WIN64EXCEPTIONS)
+ d->fpParent = CheckForParentFP(d->fpParent, pCF, d->info.IsNonFilterFuncletFrame());
+#endif // WIN64EXCEPTIONS
+
+ //
+ // The stackwalker doesn't update the register set for the
+ // case where a non-frameless frame is returning to another
+ // non-frameless frame. Cover this case.
+ //
+ // !!! This assumes that updating the register set multiple times
+ // for a given frame times is not a bad thing...
+ //
+ if (!pCF->IsFrameless())
+ {
+ LOG((LF_CORDB, LL_INFO100000, "DWSP: updating regdisplay.\n"));
+ pCF->GetFrame()->UpdateRegDisplay(&d->regDisplay);
+ }
+
+ return SWA_CONTINUE;
+}
+
+#if defined(_TARGET_X86_) && defined(FEATURE_INTEROP_DEBUGGING)
+// Helper to get the Wait-Sleep-Join bit from the thread
+bool IsInWaitSleepJoin(Thread * pThread)
+{
+ // Partial User state is sufficient because that has the bit we're checking against.
+ CorDebugUserState cts = g_pEEInterface->GetPartialUserState(pThread);
+ return ((cts & USER_WAIT_SLEEP_JOIN) != 0);
+}
+
+//-----------------------------------------------------------------------------
+// Decide if we should send an UM leaf chain.
+// This goes through a bunch of heuristics.
+// The driving guidelines here are:
+// - we try not to send an UM chain if it's just internal mscorwks stuff
+// and we know it can't have native user code.
+// (ex, anything beyond a filter context, various hijacks, etc).
+// - If it may have native user code, we send it anyway.
+//-----------------------------------------------------------------------------
+bool ShouldSendUMLeafChain(Thread * pThread)
+{
+ // If we're in shutodown, don't bother trying to sniff for an UM leaf chain.
+ // @todo - we'd like to never even be trying to stack trace on shutdown, this
+ // comes up when we do helper thread duty on shutdown.
+ if (g_fProcessDetach)
+ {
+ return false;
+ }
+
+ if (pThread->IsUnstarted() || pThread->IsDead())
+ {
+ return false;
+ }
+
+ // If a thread is suspended for sync purposes, it was suspended from managed
+ // code and the only native code is a mscorwks hijack.
+ // There are a few caveats here:
+ // - This means a thread will lose it's UM chain. But what if a user inactive thread
+ // enters the CLR from native code and hits a GC toggle? We'll lose that entire
+ // UM chain.
+ // - at a managed-only stop, preemptive threads are still live. Thus a thread
+ // may not have this state set, run a little, try to enter the GC, and then get
+ // this state set. Thus we'll lose the UM chain right out from under our noses.
+ Thread::ThreadState ts = pThread->GetSnapshotState();
+ if ((ts & Thread::TS_SyncSuspended) != 0)
+ {
+ // If we've been stopped inside the runtime (eg, at a gc-toggle) but
+ // not actually at a stopping context, then the thread must have some
+ // leafframes in mscorwks.
+ // We can detect this case by checking if GetManagedStoppedCtx(pThread) == NULL.
+ // This is very significant for notifcations (like LogMessage) that are
+ // dispatches from within mscorwks w/o a filter context.
+ // We don't send a UM chain for these cases because that would
+ // cause managed debug events to be dispatched w/ UM chains on the callstack.
+ // And that just seems wrong ...
+
+ return false;
+ }
+
+#ifdef FEATURE_HIJACK
+ if ((ts & Thread::TS_Hijacked) != 0)
+ {
+ return false;
+ }
+#endif
+
+ // This is pretty subjective. If we have a thread stopped in a managed sleep,
+ // managed wait, or managed join, then don't bother showing the native end of the
+ // stack. This check can be removed w/o impacting correctness.
+ // @todo - may be a problem if Sleep/Wait/Join go through a hosting interface
+ // which lands us in native user code.
+ // Partial User state is sufficient because that has the bit we're checking against.
+ if (IsInWaitSleepJoin(pThread))
+ {
+ return false;
+ }
+
+ // If we're tracing ourselves, we must be in managed code.
+ // Native user code can't initiate a managed stackwalk.
+ if (pThread == GetThread())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Prepare a Leaf UM chain. This assumes we should send an UM leaf chain.
+// Returns true if we actually prep for an UM leaf,
+// false if we don't.
+//-----------------------------------------------------------------------------
+bool PrepareLeafUMChain(DebuggerFrameData * pData, CONTEXT * pCtxTemp)
+{
+ // Get the current user context (depends on if we're the active thread or not).
+ Thread * thread = pData->GetThread();
+ REGDISPLAY * pRDSrc = NULL;
+ REGDISPLAY rdTemp;
+
+
+#ifdef _DEBUG
+ // Anybody stopped at an native debug event (and hijacked) should have a filter ctx.
+ if (thread->GetInteropDebuggingHijacked() && (thread->GetFrame() != NULL) && (thread->GetFrame() != FRAME_TOP))
+ {
+ _ASSERTE(g_pEEInterface->GetThreadFilterContext(thread) != NULL);
+ }
+#endif
+
+ // If we're hijacked, then we assume we're in native code. This covers the active thread case.
+ if (g_pEEInterface->GetThreadFilterContext(thread) != NULL)
+ {
+ LOG((LF_CORDB, LL_EVERYTHING, "DWS - sending special case UM Chain.\n"));
+
+ // This will get it from the filter ctx.
+ pRDSrc = &(pData->regDisplay);
+ }
+ else
+ {
+ // For inactive thread, we may not be hijacked. So just get the current ctx.
+ // This will use a filter ctx if we have one.
+ // We may suspend a thread in native code w/o hijacking it, so it's still at it's live context.
+ // This can happen when we get a debug event on 1 thread; and then switch to look at another thread.
+ // This is very common when debugging apps w/ cross-thread causality (including COM STA objects)
+ pRDSrc = &rdTemp;
+
+ bool fOk;
+
+
+ // We need to get thread's context (InitRegDisplay will do that under the covers).
+ // If this is our thread, we're in bad shape. Fortunately that should never happen.
+ _ASSERTE(thread != GetThread());
+
+ Thread::SuspendThreadResult str = thread->SuspendThread();
+ if (str != Thread::STR_Success)
+ {
+ return false;
+ }
+
+ // @todo - this context is less important because the RS will overwrite it with the live context.
+ // We don't need to even bother getting it. We can just intialize the regdisplay w/ a sentinal.
+ fOk = g_pEEInterface->InitRegDisplay(thread, pRDSrc, pCtxTemp, false);
+ thread->ResumeThread();
+
+ if (!fOk)
+ {
+ return false;
+ }
+ }
+
+ // By now we have a Regdisplay from somewhere (filter ctx, current ctx, etc).
+ _ASSERTE(pRDSrc != NULL);
+
+ // If we're stopped in mscorwks (b/c of a handler for a managed BP), then the filter ctx will
+ // still be set out in jitted code.
+ // If our regdisplay is out in UM code , then send a UM chain.
+ BYTE* ip = (BYTE*) GetControlPC(pRDSrc);
+ if (g_pEEInterface->IsManagedNativeCode(ip))
+ {
+ return false;
+ }
+
+ LOG((LF_CORDB, LL_EVERYTHING, "DWS - sending leaf UM Chain.\n"));
+
+ // Get the ending fp. We may not have any managed goo on the stack (eg, native thread called
+ // into a managed method and then returned from it).
+ FramePointer fpRoot;
+ Frame * pFrame = thread->GetFrame();
+ if ((pFrame != NULL) && (pFrame != FRAME_TOP))
+ {
+ fpRoot = FramePointer::MakeFramePointer((void*) pFrame);
+ }
+ else
+ {
+ fpRoot= ROOT_MOST_FRAME;
+ }
+
+
+ // Start tracking an UM chain. We won't actually send the UM chain until
+ // we hit managed code. Since this is the leaf, we don't need to send an
+ // Enter-Managed chain either.
+ pData->BeginTrackingUMChain(fpRoot, pRDSrc);
+
+ return true;
+}
+#endif // defined(_TARGET_X86_) && defined(FEATURE_INTEROP_DEBUGGING)
+
+//-----------------------------------------------------------------------------
+// Entry function for the debugger's stackwalking layer.
+// This will invoke pCallback(FrameInfo * pInfo, pData) for each 'frame'
+//-----------------------------------------------------------------------------
+StackWalkAction DebuggerWalkStack(Thread *thread,
+ FramePointer targetFP,
+ CONTEXT *context,
+ BOOL contextValid,
+ DebuggerStackCallback pCallback,
+ void *pData,
+ BOOL fIgnoreNonmethodFrames)
+{
+ _ASSERTE(context != NULL);
+
+ DebuggerFrameData data;
+
+ StackWalkAction result = SWA_CONTINUE;
+ bool fRegInit = false;
+
+ LOG((LF_CORDB, LL_EVERYTHING, "DebuggerWalkStack called\n"));
+
+ if(contextValid || g_pEEInterface->GetThreadFilterContext(thread) != NULL)
+ {
+ fRegInit = g_pEEInterface->InitRegDisplay(thread, &data.regDisplay, context, contextValid != 0);
+ _ASSERTE(fRegInit);
+ }
+
+ if (!fRegInit)
+ {
+#if defined(CONTEXT_EXTENDED_REGISTERS)
+
+ // Note: the size of a CONTEXT record contains the extended registers, but the context pointer we're given
+ // here may not have room for them. Therefore, we only set the non-extended part of the context to 0.
+ memset((void *)context, 0, offsetof(CONTEXT, ExtendedRegisters));
+#else
+ memset((void *)context, 0, sizeof(CONTEXT));
+#endif
+ memset((void *)&data, 0, sizeof(data));
+
+#if defined(_TARGET_X86_)
+ // @todo - this seems pointless. context->Eip will be 0; and when we copy it over to the DebuggerRD,
+ // the context will be completely null.
+ data.regDisplay.ControlPC = context->Eip;
+ data.regDisplay.PCTAddr = (TADDR)&(context->Eip);
+
+#else
+ //
+ // @TODO: this should be the code for all platforms now that it uses FillRegDisplay,
+ // which encapsulates the platform variances. This could all be avoided if we used
+ // StackWalkFrames instead of StackWalkFramesEx.
+ //
+ ::SetIP(context, 0);
+ ::SetSP(context, 0);
+ FillRegDisplay(&data.regDisplay, context);
+
+ ::SetSP(data.regDisplay.pCallerContext, 0);
+#endif
+ }
+
+ data.Init(thread, targetFP, fIgnoreNonmethodFrames, pCallback, pData);
+
+
+#if defined(_TARGET_X86_) && defined(FEATURE_INTEROP_DEBUGGING)
+ CONTEXT ctxTemp; // Temp context for Leaf UM chain. Need it here so that it stays alive for whole stackwalk.
+
+ // Important case for Interop Debugging -
+ // We may be stopped in Native Code (perhaps at a BP) w/ no Transition frame on the stack!
+ // We still need to send an UM Chain for this case.
+ if (ShouldSendUMLeafChain(thread))
+ {
+ // It's possible this may fail (eg, GetContext fails on win9x), so we're not guaranteed
+ // to be sending an UM chain even though we want to.
+ PrepareLeafUMChain(&data, &ctxTemp);
+
+ }
+#endif // defined(_TARGET_X86_) && defined(FEATURE_INTEROP_DEBUGGING)
+
+ if ((result != SWA_FAILED) && !thread->IsUnstarted() && !thread->IsDead())
+ {
+ int flags = 0;
+
+ result = g_pEEInterface->StackWalkFramesEx(thread, &data.regDisplay,
+ DebuggerWalkStackProc,
+ &data, flags | HANDLESKIPPEDFRAMES | NOTIFY_ON_U2M_TRANSITIONS | ALLOW_ASYNC_STACK_WALK);
+ }
+ else
+ {
+ result = SWA_DONE;
+ }
+
+ if (result == SWA_DONE || result == SWA_FAILED) // SWA_FAILED if no frames
+ {
+ // Since Debugger StackWalk callbacks are delayed 1 frame from EE stackwalk callbacks, we
+ // have to touch up the 1 leftover here.
+ //
+ // This is safe only because we use the REGDISPLAY of the native marker callback for any subsequent
+ // explicit frames which do not update the REGDISPLAY. It's kind of fragile. If we can change
+ // the x86 real stackwalker to unwind one frame ahead of time, we can get rid of this code.
+ if (data.needParentInfo)
+ {
+ data.info.fp = GetFramePointerForDebugger(&data, NULL);
+
+ if (data.InvokeCallback(&data.info) == SWA_ABORT)
+ {
+ return SWA_ABORT;
+ }
+ }
+
+ //
+ // Top off the stack trace as necessary w/ a thread-start chain.
+ //
+ REGDISPLAY * pRegDisplay = &(data.regDisplay);
+ if (data.IsTrackingUMChain())
+ {
+ // This is the common case b/c managed code gets called from native code.
+ pRegDisplay = data.GetUMChainStartRD();
+ }
+
+
+ // All Thread starts in unmanaged code (at something like kernel32!BaseThreadStart),
+ // so all ThreadStart chains must be unmanaged.
+ // InvokeCallback will fabricate the EnterManaged chain if we haven't already sent one.
+ data.info.InitForThreadStart(thread, pRegDisplay);
+ result = data.InvokeCallback(&data.info);
+
+ }
+ return result;
+}