protected override void ProcessRecord() { base.ProcessRecord(); try { if (Increment) { var ctx = Debugger.GetCurrentDbgEngContext(); _Worker(ctx.FrameIndex + 1, "CannotIncrementPastTopOfStack"); DbgProvider.SetAutoRepeatCommand(".f+"); } else if (Decrement) { var ctx = Debugger.GetCurrentDbgEngContext(); _Worker(ctx.FrameIndex - 1, "CannotDecrementPastBottomOfStack"); DbgProvider.SetAutoRepeatCommand(".f-"); } else { _Worker(FrameNumber, "StackFrameOutsideOfActualStack"); } } catch (DbgProviderException dpe) { WriteError(dpe); } DbgProvider.SetLocationByDebuggerContext(); } // end ProcessRecord()
// NOTE: If a derived class needs to override BeginProcessing, it should be sure // to call base.BeginProcessing() /before/ calling any of the SafeWrite* methods. protected override void BeginProcessing() { base.BeginProcessing(); m_pipelineThread = Thread.CurrentThread; if (LogCmdletOutline) { LogManager.Trace("---------------------------"); LogManager.Trace("BeginProcessing for command {0}: {1}", this, GetCommand()); } // You might think that the host should be the one to clear the auto-repeat // command, and in general, you'd be right. But the host doesn't actually get // involved with EVERY command that is run--it is only involved with every // /input/. And that input could be a script, and that script could run some // commands that set up an auto-repeat command (like stepping), and then run // some other commands (like .detach, or anything), leaving the auto-repeat // command set up, but the user definitely does not expect pressing [enter] // to step again (or do whatever the auto-repeat command was). So, for non- // formatting commands, we'll always start out by clearing the auto-repeat // command. (We exclude formatting commands, because then auto-repeat commands // would never work, because formatting commands are always tacked on to the // end of pipelines.) // // Note that this does not completely solve the problem, because cmdlets that // derive from DbgBaseCommand are not the only commands that could be run // after an auto-repeat command. But it at least helps. // // A more complete alternative might be to hook the PreCommandLookupAction and // clear the auto-repeat command there, but it would have to be a lot // trickier, because we'd need to do things like exclude auto-repeat-command- // clearing during execution of the "prompt" command, the PSReadline command, // etc. (in addition to formatting commands). if (!typeof(Formatting.Commands.FormatBaseCommand).IsAssignableFrom(GetType()) && !typeof(Formatting.Commands.GetAltFormatViewDefCommand).IsAssignableFrom(GetType())) { DbgProvider.SetAutoRepeatCommand(null); } if (TrySetDebuggerContext) { // Need to set context based on current path. PathInfo pi = this.CurrentProviderLocation(DbgProvider.ProviderId); string curPath = pi.ProviderPath; // It's okay if it fails (maybe current dir is "Dbg:\")--some commands don't // need a context. Debugger.TrySetContextByPath(curPath); } } // end BeginProcessing()
} // end ProcessRecord() private void _UnassembleInstructions(ulong addr, Func <ulong, bool> keepGoing) { ColorString csBlockId; try { ulong disp; var addrName = Debugger.GetNameByOffset(addr, out disp); csBlockId = DbgProvider.ColorizeSymbol(addrName); if (0 != disp) { csBlockId.Append(Util.Sprintf("+{0:x}", disp)); } } catch (DbgEngException) { // Ignore. We'll just use the address as the block id. If we really can't // get the memory there, we'll fail with a good error message later. csBlockId = new ColorString().Append(DbgProvider.FormatAddress(addr, Debugger.TargetIs32Bit, true)); } csBlockId.Append(":").MakeReadOnly(); bool hasCodeBytes = !Debugger.AssemblyOptions.HasFlag(DbgAssemblyOptions.NoCodeBytes); while (keepGoing(addr)) { ulong tmpAddr = addr; string disasm = Debugger.Disassemble(tmpAddr, out addr).Trim(); WriteObject(_ParseDisassembly(tmpAddr, disasm, csBlockId, hasCodeBytes)); } DbgProvider.SetAutoRepeatCommand(Util.Sprintf("{0} -Address 0x{1} -InstructionCount {2}", MyInvocation.InvocationName, addr.ToString("x"), InstructionCount)); NextAddrToDisassemble = addr; } // end _UnassembleInstructions()
protected override void ProcessRecord() { base.ProcessRecord(); if (0 == Address) { Address = NextAddressToDump; } if (!MyInvocation.BoundParameters.ContainsKey("DefaultDisplayFormat")) { DefaultDisplayFormat = NextDefaultFormat; } if (!MyInvocation.BoundParameters.ContainsKey("LengthInBytes")) { if (DefaultDisplayFormat != NextDefaultFormat) { // We're switching formats; may need to also switch the default size. LengthInBytes = _GetDefaultReadSize(DefaultDisplayFormat); } else { LengthInBytes = NextDefaultLengthInBytes; } } if (0 == LengthInBytes) { LengthInBytes = _GetDefaultReadSize(DefaultDisplayFormat); } // Okay, we've got a length + display format, but one or the other may be // defaulted. In that case, we want to make sure that the LengthInBytes is // compatible with the display format. (And actually, even if they were both // explicitly specified, we /still/ want to make sure they are compatible.) // // For instance, if the LengthInBytes was explicitly specified as 4, and the // display format got defaulted to PointersWithAscii, on a 64-bit target, we // are not going to be able to display anything (because we won't have read // even a single full pointer)--we need to downgrade the display format to fit // the size requested, if possible. if (!_IsDisplayFormatCompatibleWithReadSize(LengthInBytes, DefaultDisplayFormat, Debugger.PointerSize)) { var compatibleDisplayFormat = _GetDefaultDisplayFormatForSize(LengthInBytes, Debugger.PointerSize); if (MyInvocation.BoundParameters.ContainsKey("DefaultDisplayFormat")) { SafeWriteWarning("The specified display format ({0}) is not compatible with the byte length requested ({1}). The {2} format will be used instead.", DefaultDisplayFormat, LengthInBytes, compatibleDisplayFormat); } // else just silently fix it up. DefaultDisplayFormat = compatibleDisplayFormat; } if (!MyInvocation.BoundParameters.ContainsKey("DefaultDisplayColumns")) { DefaultDisplayColumns = NextDefaultNumColumns; } var raw = Debugger.ReadMem(Address, LengthInBytes, false); if (raw.Length != LengthInBytes) { WriteWarning(Util.Sprintf("Actual memory read (0x{0:x} bytes) is less than requested (0x{1:x} bytes).", raw.Length, LengthInBytes)); } var mem = new DbgMemory(Address, raw, Debugger.TargetIs32Bit, false, // TODO: actually determine endianness Debugger); mem.DefaultDisplayFormat = DefaultDisplayFormat; mem.DefaultDisplayColumns = DefaultDisplayColumns; NextAddressToDump = Address + (ulong)raw.Length; NextDefaultFormat = DefaultDisplayFormat; NextDefaultLengthInBytes = LengthInBytes; NextDefaultNumColumns = DefaultDisplayColumns; WriteObject(mem); DbgProvider.SetAutoRepeatCommand(Util.Sprintf("{0}", MyInvocation.InvocationName)); } // end ProcessRecord()
protected override void ProcessRecord() { using (SetDebuggerAndContextAndUpdateCallbacks()) { EnsureTargetCanStep(Debugger); // In the native debugger, if you are in some other frame, your scope // is reset (to the top frame), but not your thread. Debugger.SetCurrentScopeFrame(0); switch (ResumeType) { case Dbg.ResumeType.ReverseStepBranch: case Dbg.ResumeType.ReverseStepInto: case Dbg.ResumeType.ReverseStepOver: case Dbg.ResumeType.StepBranch: case Dbg.ResumeType.StepInto: case Dbg.ResumeType.StepOver: case Dbg.ResumeType.StepOut: case Dbg.ResumeType.StepToCall: m_baselineRegisters = DbgRegisterSetBase.GetRegisterSet(Debugger); break; } if (ResumeType.Passive != ResumeType) { try { Debugger.ResumeExecution(ResumeType); } catch (DbgEngAlreadyRunningException) { SafeWriteWarning("Target has already been resumed."); // // We'll just proceed, then, to wait... // } } bool noException = false; try { Debugger.BreakpointHit += Debugger_BreakpointHit; if (DbgProvider.IsInBreakpointCommand) { Util.Assert(DbgProvider.EntryDepth > 0); // If we are executing as a breakpoint command, and someone is // resuming execution, we have to handle it specially: // // 1. We need to cancel the rest of the pipeline. This is the same // way that dbgeng breakpoint commands work (if your bp command // is "!foo;g;!bar", then !bar will not ever be executed. // // At first glance you might think that you are getting robbed // of some cool power by canceling any remaining commands, but // if you think a little more about it, you realize it would be // very difficult to reason about what should happen. For // example, consider: // // bp myDll!Foo "!stuff ; g ; !otherStuff" // // The first time you hit the breakpoint, "!stuff" is run, then // execution is resumed. The seocnd time the breakpoint is // hit... what should be run first--"!otherStuff", or "!stuff"? // And you could get nested much further. So we'll take the // same easy out as dbgeng, and treat "g" as an "exit" in a // breakpoint command. // // TODO: we could run the script through the parser and warn // about unreachable code after a "g" command. // // 2. When a breakpoint hits and a breakpoint command is run, the // dbgeng thread is blocked, waiting for the breakpoint command // to finish. If we want to resume execution from within a // breakpoint command, we can tell dbgeng to resume, but if we // then turn around and call WaitForEvent (which is what we // normally do), then you're deadlocked--WaitForEvent can never // return, because before dbgeng can let the target resume, // your breakpoint command has to return, but it can't return // because it's waiting for WaitForEvent. // // So in this case, once we've instructed dbgeng to execute, we // need to return from the command and let dbgeng let the // target resume. We will depend on the "host" (whether it be // windbg or DbgShell or something else) to do the // WaitForEvent. Fortunately, "exit" should get us what we want // for this case, too. // // TODO: what if we are nested a few levels deep? i.e. // // bp myDll!foo "!dbgshell -Bp !dbgshell write-host 'so nested!' -fore Yellow , g" // // I'm not sure how we'd get the "exit" to propagate out from // the inner !dbgshell, through the outer one, so that we could // return to the host. // // Also, if we are nested, and multiple levels use -Bp, we're // definitely broken--I'd need to change inBreakpointCommand to // be a count instead of a bool, for one thing. // if (null != m_baselineRegisters) // <-- shortcut for determining if we are stepping { // If someone is trying to step while inside a breakpoint // command, let's let them know what's going on. this.InvokeCommand.InvokeScript("Write-Host 'Resuming execution from within a breakpoint " + "command necessarily returns control to the host, for it " + "to perform the wait.' -Fore Cyan"); this.InvokeCommand.InvokeScript("Write-Host '(it also acts like an 'exit'; terminating the " + "current pipeline and discarding any remaining commands)' " + " -Fore Cyan"); this.InvokeCommand.InvokeScript("Write-Host '(if you want to step in DbgShell instead of " + "the host, just run !dbgshell again from the host and then " + "step)' -Fore Cyan"); // Give them a chance to notice that a message was printed out // before we switch the foreground window: this.InvokeCommand.InvokeScript("Start-Sleep -Milliseconds 500"); } if (!DbgProvider.IsInGuestMode) { // If DbgShell.exe is the host, then we need to let it know // that it shouldn't exit all the way; we're just trying to // terminate the current breakpoint script. DbgProvider.DontExitAllTheWay = true; } // Throwing an ExitException would be much more direct, but there // are no public constructors for it. //throw new ExitException(); this.InvokeCommand.InvokeScript("exit"); } base.ProcessRecord(); // sets up ctrl-c interception, performs wait. noException = true; } finally { Debugger.BreakpointHit -= Debugger_BreakpointHit; _TryRemoveSpecialBp(); // We check NoTarget before setting the auto-repeat command, because // if -skipFinalBreakpoint was used, the target might be completely // gone (leaving you back with a working dir of "Dbg:\"), and it would // be nice if hitting [ENTER] for a newline right there didn't try to // auto-repeat your last "g" command (which would blow up with a "no // target!" error). if (noException && !Debugger.NoTarget) { DbgProvider.SetAutoRepeatCommand(MyInvocation.Line.Trim()); } } if (null != m_bea) { // if( m_bea.Breakpoint.IsValid ) // Console.WriteLine( "Breakpoint hit. The command was: {0}", m_bea.Breakpoint.Command ); // else // Console.WriteLine( "Breakpoint hit. (breakpoint no longer valid)" ); // TODO: Idea: I could accept ScriptBlocks as commands to be executed, and store // them with dbgeng in the form of a string identifier, like "ScriptBlock_NNNN", // or probably better, "!dbgshell ScriptBlock_NNNN". I need to figure out what // happens with the breakpoint command when I'm just a debugger client. } } } // end ProcessRecord()
protected override void ProcessRecord() { base.ProcessRecord(); if (0 == Address) { Address = NextAddressToDump; } if (!MyInvocation.BoundParameters.ContainsKey("DefaultDisplayFormat")) { DefaultDisplayFormat = NextDefaultFormat; } if (!MyInvocation.BoundParameters.ContainsKey("LengthInBytes")) { if (DefaultDisplayFormat != NextDefaultFormat) { // We're switching formats; may need to also switch the default size. LengthInBytes = _GetDefaultReadSize(DefaultDisplayFormat); } else { LengthInBytes = NextDefaultLengthInBytes; } } if (0 == LengthInBytes) { LengthInBytes = _GetDefaultReadSize(DefaultDisplayFormat); } if (!MyInvocation.BoundParameters.ContainsKey("DefaultDisplayColumns")) { DefaultDisplayColumns = NextDefaultNumColumns; } var raw = Debugger.ReadMem(Address, LengthInBytes, false); if (raw.Length != LengthInBytes) { WriteWarning(Util.Sprintf("Actual memory read (0x{0:x} bytes) is less than requested (0x{1:x} bytes).", raw.Length, LengthInBytes)); } var mem = new DbgMemory(Address, raw, Debugger.TargetIs32Bit, false, // TODO: actually determine endianness Debugger); mem.DefaultDisplayFormat = DefaultDisplayFormat; mem.DefaultDisplayColumns = DefaultDisplayColumns; NextAddressToDump = Address + (ulong)raw.Length; NextDefaultFormat = DefaultDisplayFormat; NextDefaultLengthInBytes = LengthInBytes; NextDefaultNumColumns = DefaultDisplayColumns; WriteObject(mem); DbgProvider.SetAutoRepeatCommand(Util.Sprintf("{0}", MyInvocation.InvocationName)); } // end ProcessRecord()