// The function verifies that the stackwalker for this thread is "current". The stack-walker will become // invalid when a debugger performs an operation that invalidats active stackwalkers. Examples of such // operations are: // - calling Continue(), calling SetIP() or calling RefreshStack() methods. // // If the current stack-walker is not current, a new stack-walker is created for the thread. The function // also sets the current frame if the stack walker is refreshed. // private void EnsureCurrentStackWalker() { lock (m_stackLock) { if (m_stackWalker != null) { return; } m_stackWalker = new FrameCache(m_threadMgr.FrameFactory.EnumerateFrames(this), this); // initialize the frame index to be the invalid index. -1 is a special value here. m_currentFrameIndex = -1; int idx = 0; try { while (true) { MDbgFrame f = null; try { // set m_currentFrame to first non-Internal frame f = m_stackWalker.GetFrame(idx); } catch { // It is acceptable that the above might throw an exception, if the // reason is that the OS thread is not in the dump. This // can happen because the CLR debugging API can notify us of managed // threads whose OS counterpart has already been destroyed, and is thus // no longer in the dump. In such a case, just fall through and return // without having found a current frame for this thread's stack walk. // Otherwise, propagate the exception. if (m_threadMgr.m_process.DumpReader == null) { // Not debugging a dump, so we wouldn't expect an exception here. // Don't swallow throw; } if (IsOSThreadPresentInDump()) { // Looks like we're trying to get a stackwalk of a valid thread in the dump, // meaning the exception that was thrown was a bad one. Rethrow it. throw; } } if (f == null) { // Hit the end of the stack without finding a frame that we can set as the // current frame. Should still be set to "no current frame" Debug.Assert(m_currentFrameIndex == -1); return; } // Skip InfoOnly frames. if (!f.IsInfoOnly) { m_currentFrameIndex = idx; return; } idx++; } } catch (NotImplementedException) { // If any of the stackwalking APIs are not implemented, then we have no // stackwalker, and act like there's no current frame. } } }
/// <summary> /// Clears the stack walker's frame cache, which will force the stack /// to be walked again with the next call that depends on the stack. /// </summary> public void InvalidateStackWalker() { lock (m_stackLock) { if (m_stackWalker != null) { m_stackWalker.Invalidate(); } m_stackWalker = null; } }
/// <summary> /// A function that returns the new MdbgFrame. The function is expected to be overriden by derived implementations. /// </summary> /// <param name="index">0 based index from top of the stack</param> /// <returns>frame from the stack</returns> protected override MDbgFrame GetFrameImpl(int index) { if (index < FrameCache.Count) { return(FrameCache[index]); } MDbgFrame frameToReturn; if (index == 0) { // special case the first frame frameToReturn = ReturnLeafFrame(); } else { // use recursion... MDbgFrame prevFrame = GetFrameImpl(index - 1); if (prevFrame == null) { throw new ArgumentException(); } frameToReturn = GetFrameCaller(prevFrame); if (frameToReturn == null) { // we need to get the next frame from the following chain CorChain chain = GetFrameChain(prevFrame); Debug.Assert(chain != null); // 1. find next chain while (true) { chain = chain.Caller; if (chain == null) { break; } if (chain.IsManaged) { CorFrame f = chain.ActiveFrame; if (f != null) { frameToReturn = new MDbgILFrame(Thread, f); break; } } else { frameToReturn = FillAndGetLeafFrameFromNativeChain(chain); if (frameToReturn != null) { break; } } } } } // store and return frameToReturn if (frameToReturn != null) { Debug.Assert(FrameCache.Count >= index); if (FrameCache.Count == index) { FrameCache.Add(frameToReturn); } else { Debug.Assert(FrameCache[index] == frameToReturn, "List of frames pre-filled with incorrect frame"); } } return(frameToReturn); }