} // end ChangeDebuggeeState() public int ChangeEngineState(DEBUG_CES Flags, ulong Argument) { LogManager.Trace("ChangeEngineState: {0}, 0x{1:x}", Flags, Argument); try { var eventArgs = new EngineStateChangedEventArgs(m_debugger, Flags, Argument); if (eventArgs.Flags.HasFlag(DEBUG_CES.EVENT_FILTERS)) { // Need to refresh our filters. if (eventArgs.Argument == DbgEventArgs.DEBUG_ANY_ID) { m_debugger._ResetFilters(); } else { m_debugger._RefreshSingleFilter((uint)eventArgs.Argument); } } if (eventArgs.Flags.HasFlag(DEBUG_CES.BREAKPOINTS)) { // Need to refresh our breakpoints. m_debugger.FixupBpStuff(eventArgs.Argument); } if ((eventArgs.Flags.HasFlag(DEBUG_CES.SYSTEMS))) { // DbgEng doesn't seem to set up its symbol path until you first // attach to something, but it doesn't raise the event saying that // the symbol path has changed when that happens. As a workaround, // we'll dump our cached symbol path here. m_debugger.m_sympath = null; // Another oddity with the symbol path: before attaching to any // system, dbgeng will say that the symbol path is blank. Then, // once it gets attached to something, it will append the value of // the environment variable _NT_SYMBOL_PATH to it. if ((DEBUG_ANY_ID != (uint)eventArgs.Argument)) { // We're adding a system. This might be a handy spot to do // something, so I'm leaving the condition here even though I'm // not currently using it. // (this gets traced by the eventArgs constructor) } else { // Removing a system. (this gets traced by the eventArgs // constructor) } } if (eventArgs.Flags.HasFlag(DEBUG_CES.EFFECTIVE_PROCESSOR)) { //DbgProvider.ForceRebuildNamespace(); LogManager.Trace("Effective processor changed; queueing request to rebuild namespace."); DbgProvider.RequestExecuteBeforeNextPrompt("[MS.Dbg.DbgProvider]::ForceRebuildNamespace()"); } if (eventArgs.Flags.HasFlag(DEBUG_CES.EXECUTION_STATUS)) { m_executionStatusCookie++; } int retVal = _RaiseEvent(m_debugger.EngineStateChanged, eventArgs); if (_ShouldOutput(retVal, eventArgs)) { _PsPipe.WriteObject(eventArgs); } if (DEBUG_CES.EXECUTION_STATUS == Flags) { var status = (DEBUG_STATUS)(Argument & (ulong)DEBUG_STATUS.MASK); bool insideWait = false; bool waitTimedOut = false; if (status != (DEBUG_STATUS)Argument) { insideWait = 0 != (Argument & (ulong)DEBUG_STATUS_FLAGS.INSIDE_WAIT); waitTimedOut = 0 != (Argument & (ulong)DEBUG_STATUS_FLAGS.WAIT_TIMEOUT); } LogManager.Trace("EXECUTION_STATUS changed. Argument: 0x{0:x} ({1}{2}{3})", Argument, status, insideWait ? " + INSIDE_WAIT" : "", waitTimedOut ? " + WAIT_TIMEOUT" : ""); DbgEngDebugger._GlobalDebugger.DbgEngCookie++; if (0 == ((ulong)DEBUG_STATUS_FLAGS.INSIDE_WAIT & Argument)) { // Formerly, we would use this code to signal the currently- // running cmdlet to exit its message loop. The idea was that // there had been problems with more notification events arriving // /after/ WaitForEvent returned. But I can't currently repro // that. (There /are/ some notifications that occur after // WaitForEvent returns, but they are caused by my own actions-- // setting assembly options and text aliases.) // // There is also the difference that now I call WaitForEvent on // the dbgeng thread, whereas originally I had been calling it on // a threadpool thread. I'm not sure how that would have made a // difference, though. // // For now, I'm going to get rid of this stuff. It seems fragile // and horribly complicated--I really hope I don't have to bring // it back. // // // TODO: This seems terribly fragile. But I don't see how else to make // // sure we process all callbacks before exiting ProcessRecord. I'm probably // // going to have to add more cases... :( // if( DEBUG_STATUS.BREAK == (DEBUG_STATUS) Argument ) // { // if( _SignalPipeOnBreak ) // { // LogManager.Trace( "EXECUTION_STATUS is BREAK; signaling pipeline." ); // _PsPipe.SignalDone(); // } // else // { // LogManager.Trace( "EXECUTION_STATUS is BREAK, but we're ignoring it." ); // } // } if (DEBUG_STATUS.NO_DEBUGGEE == (DEBUG_STATUS)Argument) { // We have no target. This might be a handy spot to do // something, so I'm leaving the condition here even // though I'm not currently using it. LogManager.Trace("No debuggee."); } } // end if( inside wait ) } // end if( exec status ) return(retVal); } catch (Exception e) { Util.FailFast("Unexpected exception during event callback.", e); return(0); } } // end ChangeEngineState()
} // end property Frames public IEnumerable <DbgStackFrameInfo> EnumerateStackFrames() { if (null != m_frames) { return(m_frames); } m_frames = new List <DbgStackFrameInfo>(); bool noException = false; try { return(Debugger.ExecuteOnDbgEngThread(() => { using (new DbgEngContextSaver(Debugger, Context)) { WDebugControl dc = (WDebugControl)Debugger.DebuggerInterface; /* TODO: I guess frameOffset doesn't do what I think it does... or maybe I need to file a bug. * //int stride = 64; * int stride = 3; * ulong frameOffset = 0; * int hr = 0; * DEBUG_STACK_FRAME_EX[] frames = new DEBUG_STACK_FRAME_EX[ stride ]; * while( true ) * { * uint framesFilled = 0; * hr = dc.GetStackTraceEx( frameOffset, 0, 0, frames, frames.Length, out framesFilled ); * * CheckHr( hr ); * * if( 0 == framesFilled ) * break; * * for( int i = 0; i < framesFilled; i++ ) * { * var newFrame = new StackFrameInfo( Debugger, frames[ i ] ); * m_frames.Add( newFrame ); * //yield return newFrame; hmm, can't mix an iterator with the straight 'return' above * } * * frameOffset += 3; * } // while( keep requesting more frames ) */ DEBUG_STACK_FRAME_EX[] frames; int hr = dc.GetStackTraceEx(0, // frameOffset 0, // stackOffset 0, // instructionOffset MaxFrameCount, out frames); CheckHr(hr); int[] managedFrameIndices; try { managedFrameIndices = new int[Thread.ManagedThreads.Count]; } catch (InvalidOperationException ioe) { // // Likely "Mismatched architecture between this process // and the dac." No managed code information for you, sir. // DbgProvider.RequestExecuteBeforeNextPrompt( Util.Sprintf("Write-Warning 'Could not get managed thread/frame information: {0}'", System.Management.Automation.Language.CodeGeneration.EscapeSingleQuotedStringContent(Util.GetExceptionMessages(ioe)))); managedFrameIndices = new int[0]; // (and remember not to keep trying to get managed info) Debugger.ClrMdDisabled = true; } foreach (var nativeFrame in frames) { ClrStackFrame managedFrame = null; for (int i = 0; i < managedFrameIndices.Length; i++) { var mThread = Thread.ManagedThreads[i]; int mFrameIdx = managedFrameIndices[i]; // It's possible that a thread is marked as a managed // thread, but has no stack frames. (I've seen this, // for instance, at the final breakpoint of a managed // app.) if (0 == mThread.StackTrace.Count) { continue; } if (mFrameIdx >= mThread.StackTrace.Count) { // We've exhausted the managed frames for this // particular managed thread. continue; } var mFrame = mThread.StackTrace[mFrameIdx]; while ((0 == mFrame.InstructionPointer) && (mFrameIdx < (mThread.StackTrace.Count - 1))) // still at least one more frame below? { // It's some sort of "helper" or GC frame or // something, which we need to skip. mFrameIdx++; managedFrameIndices[i] = mFrameIdx; mFrame = mThread.StackTrace[mFrameIdx]; } if (nativeFrame.InstructionOffset == mFrame.InstructionPointer) { managedFrame = mFrame; managedFrameIndices[i] += 1; break; } } var newFrame = new DbgStackFrameInfo(Debugger, m_thread, nativeFrame, managedFrame); m_frames.Add(newFrame); } noException = true; return m_frames; } // end using( context saver ) })); } finally { if (!noException) { m_frames = null; // so we can try again (handy for debugging) } } } // end EnumerateStackFrames()