/// <summary> /// See <see cref="StepIn"/>. /// If there's a call as next instruction, it'll be skipped. /// </summary> public void StepOver(DebugThread th) { var code = APIIntermediate.ReadArray <byte>(th.OwnerProcess.Handle, th.CurrentInstruction, DisAsm86.MaximumInstructionLength); int instructionLength = 0; var instrType = DisAsm86.GetInstructionType(code, false, out instructionLength); /* * If there's a call, set a breakpoint right after the call to skip the called subroutine */ if (instrType == InstructionType.Call) { var bpAddr = IntPtr.Add(th.CurrentInstruction, instructionLength); var tempBreakPoint = Breakpoints.ByAddress(bpAddr); bool keepBpAfterStepComplete = false; if (keepBpAfterStepComplete = tempBreakPoint == null) { tempBreakPoint = Breakpoints.CreateBreakpoint(bpAddr); } th.ContinueDebugging(); Debuggee.WaitForDebugEvent(); if (!keepBpAfterStepComplete) { Breakpoints.Remove(tempBreakPoint); } } else { StepIn(th); } }
/// <summary> /// Executes the next single code instruction. /// Returns false if the single step could be executed but wasn't completed due to a breakpoint or an other exception/debug event. /// </summary> bool StepToNextInstruction(DebugThread th) { if (!Debuggee.IsAlive) { return(false); } if (lastUnhandledBreakpoint != null) { SkipAndRestoreLastBreakpoint(false); } bool lastStepState = th.Context.TrapFlagSet; if (!lastStepState) { th.Context.TrapFlagSet = true; } th.ContinueDebugging(); expectsSingleStep = true; Debuggee.WaitForDebugEvent(); //TODO: What if there's a non-ss exception? // Answer: return false if (!lastStepState) { th.Context.TrapFlagSet = false; } return(!expectsSingleStep); }
void SkipAndRestoreLastBreakpoint(bool resetStepFlag = true) { if (lastUnhandledBreakpoint == null) { throw new Exception("lastUnhandledBreakpoint must not be null"); } if (!lastUnhandledBreakpoint.temporarilyDisabled) { throw new Exception("breakpoint must be marked as temporarily disabled!"); } if (lastUnhandledBreakpointThread == null) { throw new Exception("lastUnhandledBreakpointThread must not be null"); } if (postBreakpointResetStepCompleted) { throw new Exception("why is postBreakpointResetStepCompleted set to true?"); } // Immediately after the breakpoint was hit, the bp code was taken out of the code // and the instruction pointer was decreased, so the original instruction will be executed then afterwards. // 1) Enable single-stepping (SS flag) // 2) Step one forward (execute original instruction) // 3) Re-enable breakpoint // 4) (optionally) disable single-stepping again // 1) lastUnhandledBreakpointThread.Context.TrapFlagSet = true; // 2) lastUnhandledBreakpointThread.ContinueDebugging(); postBreakpointResetStepCompleted = false; Debuggee.WaitForDebugEvent(); if (!postBreakpointResetStepCompleted) { return; } // 3) lastUnhandledBreakpoint.Enable(); // 4) if (resetStepFlag) { lastUnhandledBreakpointThread.Context.TrapFlagSet = false; } lastUnhandledBreakpointThread = null; lastUnhandledBreakpoint = null; postBreakpointResetStepCompleted = false; }
/// <summary> /// Executes until the currently executed method's point of return has been reached. /// </summary> public void StepOut(DebugThread th) { var returnPtr = APIIntermediate.Read <IntPtr>(th.OwnerProcess.Handle, new IntPtr(th.Context.lastReadCtxt.ebp + 4)); var tempBreakPoint = Breakpoints.ByAddress(returnPtr); bool keepBpAfterStepComplete = false; if (keepBpAfterStepComplete = tempBreakPoint == null) { tempBreakPoint = Breakpoints.CreateBreakpoint(returnPtr); } th.ContinueDebugging(); Debuggee.WaitForDebugEvent(); if (!keepBpAfterStepComplete) { Breakpoints.Remove(tempBreakPoint); } }