terminal,service: Add support for rev prefix and step-instruction (#1596)

Support for rev {next,step} is not currently implemented.
This commit is contained in:
dpapastamos 2019-07-09 02:01:00 +01:00 committed by Derek Parker
parent 1758823429
commit c7d1692e92
7 changed files with 60 additions and 2 deletions

@ -32,6 +32,7 @@ Command | Description
[print](#print) | Evaluate an expression.
[regs](#regs) | Print contents of CPU registers.
[restart](#restart) | Restart process from a checkpoint or event.
[rev](#rev) | Reverses the execution of the target program for the command specified.
[rewind](#rewind) | Run backwards until breakpoint or program termination.
[set](#set) | Changes the value of a variable.
[source](#source) | Executes a file containing a list of delve commands
@ -329,6 +330,11 @@ Restart process from a checkpoint or event.
Aliases: r
## rev
Reverses the execution of the target program for the command specified.
Currently, only the rev step-instruction command is supported.
## rewind
Run backwards until breakpoint or program termination.

@ -34,6 +34,7 @@ const (
noPrefix = cmdPrefix(0)
onPrefix = cmdPrefix(1 << iota)
deferredPrefix
revPrefix
)
type callContext struct {
@ -136,7 +137,7 @@ the arguments. With -noargs, the process starts with an empty commandline.
`},
{aliases: []string{"continue", "c"}, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
{aliases: []string{"step", "s"}, cmdFn: c.step, helpMsg: "Single step through program."},
{aliases: []string{"step-instruction", "si"}, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
{aliases: []string{"step-instruction", "si"}, allowedPrefixes: revPrefix, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
{aliases: []string{"next", "n"}, cmdFn: c.next, helpMsg: "Step over to next source line."},
{aliases: []string{"stepout"}, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
{aliases: []string{"call"}, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
@ -379,6 +380,12 @@ The "note" is arbitrary text that can be used to identify the checkpoint, if it
helpMsg: `Deletes checkpoint.
clear-checkpoint <id>`,
})
c.cmds = append(c.cmds, command{
aliases: []string{"rev"},
cmdFn: c.revCmd,
helpMsg: `Reverses the execution of the target program for the command specified.
Currently, only the rev step-instruction command is supported.`,
})
for i := range c.cmds {
v := &c.cmds[i]
@ -988,7 +995,15 @@ func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error
if c.frame != 0 {
return notOnFrameZeroErr
}
state, err := exitedToError(t.client.StepInstruction())
var fn func() (*api.DebuggerState, error)
if ctx.Prefix == revPrefix {
fn = t.client.ReverseStepInstruction
} else {
fn = t.client.StepInstruction
}
state, err := exitedToError(fn())
if err != nil {
printcontextNoState(t)
return err
@ -998,6 +1013,18 @@ func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error
return nil
}
func (c *Commands) revCmd(t *Term, ctx callContext, args string) error {
if len(args) == 0 {
return errors.New("not enough arguments")
}
ctx.Prefix = revPrefix
if err := c.CallWithContext(args, t, ctx); err != nil {
return err
}
return nil
}
func (c *Commands) next(t *Term, ctx callContext, args string) error {
if err := scopePrefixSwitch(t, ctx); err != nil {
return err

@ -356,6 +356,8 @@ const (
StepOut = "stepOut"
// StepInstruction continues for exactly 1 cpu instruction.
StepInstruction = "stepInstruction"
// ReverseStepInstruction reverses execution for exactly 1 cpu instruction.
ReverseStepInstruction = "reverseStepInstruction"
// Next continues to the next source line, not entering function calls.
Next = "next"
// SwitchThread switches the debugger's current thread context.

@ -43,6 +43,8 @@ type Client interface {
// SingleStep will step a single cpu instruction.
StepInstruction() (*api.DebuggerState, error)
// ReverseSingleStep will reverse step a single cpu instruction.
ReverseStepInstruction() (*api.DebuggerState, error)
// SwitchThread switches the current thread context.
SwitchThread(threadID int) (*api.DebuggerState, error)
// SwitchGoroutine switches the current goroutine (and the current thread as well)

@ -624,6 +624,15 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
case api.StepInstruction:
d.log.Debug("single stepping")
err = d.target.StepInstruction()
case api.ReverseStepInstruction:
d.log.Debug("reverse single stepping")
if err := d.target.Direction(proc.Backward); err != nil {
return nil, err
}
defer func() {
d.target.Direction(proc.Forward)
}()
err = d.target.StepInstruction()
case api.StepOut:
d.log.Debug("step out")
err = proc.StepOut(d.target)

@ -125,6 +125,12 @@ func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
return state, err
}
func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) {
state := new(api.DebuggerState)
err := c.call("Command", &api.DebuggerCommand{Name: api.ReverseStepInstruction}, state)
return state, err
}
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
state := new(api.DebuggerState)
cmd := &api.DebuggerCommand{

@ -160,6 +160,12 @@ func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
return &out.State, err
}
func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepInstruction}, &out)
return &out.State, err
}
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
var out CommandOut
cmd := api.DebuggerCommand{