Extend the "frame" command to set the current frame. (#1110)
* Extend the "frame" command to set the current frame. Command frame 3 sets up so that subsequent "print", "set", "whatis" command will operate on frame 3. frame 3 print foo continues to work. Added "up", "down". They move the current frame up or down. Implementation note: This changes removes "scopePrefix" mode from the terminal/command.go and instead have the command examine the goroutine/frame value to see if it is invoked in a scoped context. * Rename Command.Frame -> Command.frame.
This commit is contained in:
parent
ec8dc3a10d
commit
82aff3f18a
@ -9,7 +9,10 @@ func agoroutine(started chan<- struct{}, done chan<- struct{}, i int) {
|
|||||||
done <- struct{}{}
|
done <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dummy int
|
||||||
|
|
||||||
func stacktraceme() {
|
func stacktraceme() {
|
||||||
|
dummy++
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,7 +896,7 @@ func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStacktraceGoroutine(t *testing.T) {
|
func TestStacktraceGoroutine(t *testing.T) {
|
||||||
mainStack := []loc{{13, "main.stacktraceme"}, {26, "main.main"}}
|
mainStack := []loc{{14, "main.stacktraceme"}, {29, "main.main"}}
|
||||||
agoroutineStacks := [][]loc{
|
agoroutineStacks := [][]loc{
|
||||||
{{8, "main.agoroutine"}},
|
{{8, "main.agoroutine"}},
|
||||||
{{9, "main.agoroutine"}},
|
{{9, "main.agoroutine"}},
|
||||||
|
@ -32,7 +32,7 @@ func TestGoroutineCreationLocation(t *testing.T) {
|
|||||||
if filepath.Base(createdLocation.File) != "goroutinestackprog.go" {
|
if filepath.Base(createdLocation.File) != "goroutinestackprog.go" {
|
||||||
t.Fatalf("goroutine creation file incorrect: %s", filepath.Base(createdLocation.File))
|
t.Fatalf("goroutine creation file incorrect: %s", filepath.Base(createdLocation.File))
|
||||||
}
|
}
|
||||||
if createdLocation.Line != 20 {
|
if createdLocation.Line != 23 {
|
||||||
t.Fatalf("goroutine creation line incorrect: %v", createdLocation.Line)
|
t.Fatalf("goroutine creation line incorrect: %v", createdLocation.Line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,8 @@ const optimizedFunctionWarning = "Warning: debugging optimized function"
|
|||||||
type cmdPrefix int
|
type cmdPrefix int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
noPrefix = cmdPrefix(0)
|
noPrefix = cmdPrefix(0)
|
||||||
scopePrefix = cmdPrefix(1 << iota)
|
onPrefix = cmdPrefix(1 << iota)
|
||||||
onPrefix
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type callContext struct {
|
type callContext struct {
|
||||||
@ -40,6 +39,18 @@ type callContext struct {
|
|||||||
Breakpoint *api.Breakpoint
|
Breakpoint *api.Breakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *callContext) scoped() bool {
|
||||||
|
return ctx.Scope.GoroutineID >= 0 || ctx.Scope.Frame > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type frameDirection int
|
||||||
|
|
||||||
|
const (
|
||||||
|
frameSet frameDirection = iota
|
||||||
|
frameUp
|
||||||
|
frameDown
|
||||||
|
)
|
||||||
|
|
||||||
type cmdfunc func(t *Term, ctx callContext, args string) error
|
type cmdfunc func(t *Term, ctx callContext, args string) error
|
||||||
|
|
||||||
type command struct {
|
type command struct {
|
||||||
@ -62,9 +73,10 @@ func (c command) match(cmdstr string) bool {
|
|||||||
|
|
||||||
// Commands represents the commands for Delve terminal process.
|
// Commands represents the commands for Delve terminal process.
|
||||||
type Commands struct {
|
type Commands struct {
|
||||||
cmds []command
|
cmds []command
|
||||||
lastCmd cmdfunc
|
lastCmd cmdfunc
|
||||||
client service.Client
|
client service.Client
|
||||||
|
frame int // Current frame as set by frame/up/down commands.
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -111,11 +123,11 @@ See also: "help on", "help cond" and "help clear"`},
|
|||||||
checkpoint. For normal processes restarts the process, optionally changing
|
checkpoint. For normal processes restarts the process, optionally changing
|
||||||
the arguments. With -noargs, the process starts with an empty commandline.
|
the arguments. With -noargs, the process starts with an empty commandline.
|
||||||
`},
|
`},
|
||||||
{aliases: []string{"continue", "c"}, cmdFn: cont, helpMsg: "Run until breakpoint or program termination."},
|
{aliases: []string{"continue", "c"}, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
|
||||||
{aliases: []string{"step", "s"}, allowedPrefixes: scopePrefix, cmdFn: step, helpMsg: "Single step through program."},
|
{aliases: []string{"step", "s"}, cmdFn: c.step, helpMsg: "Single step through program."},
|
||||||
{aliases: []string{"step-instruction", "si"}, allowedPrefixes: scopePrefix, cmdFn: stepInstruction, helpMsg: "Single step a single cpu instruction."},
|
{aliases: []string{"step-instruction", "si"}, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
|
||||||
{aliases: []string{"next", "n"}, allowedPrefixes: scopePrefix, cmdFn: next, helpMsg: "Step over to next source line."},
|
{aliases: []string{"next", "n"}, cmdFn: c.next, helpMsg: "Step over to next source line."},
|
||||||
{aliases: []string{"stepout"}, allowedPrefixes: scopePrefix, cmdFn: stepout, helpMsg: "Step out of the current function."},
|
{aliases: []string{"stepout"}, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
|
||||||
{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
|
{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
|
||||||
{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: `Switch to the specified thread.
|
{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: `Switch to the specified thread.
|
||||||
|
|
||||||
@ -139,7 +151,7 @@ Print out info for every goroutine. The flag controls what information is shown
|
|||||||
-g displays location of go instruction that created the goroutine
|
-g displays location of go instruction that created the goroutine
|
||||||
|
|
||||||
If no flag is specified the default is -u.`},
|
If no flag is specified the default is -u.`},
|
||||||
{aliases: []string{"goroutine"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine
|
{aliases: []string{"goroutine"}, allowedPrefixes: onPrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine
|
||||||
|
|
||||||
goroutine
|
goroutine
|
||||||
goroutine <id>
|
goroutine <id>
|
||||||
@ -149,15 +161,15 @@ Called without arguments it will show information about the current goroutine.
|
|||||||
Called with a single argument it will switch to the specified goroutine.
|
Called with a single argument it will switch to the specified goroutine.
|
||||||
Called with more arguments it will execute a command on the specified goroutine.`},
|
Called with more arguments it will execute a command on the specified goroutine.`},
|
||||||
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
|
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
|
||||||
{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
|
{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] print <expression>
|
[goroutine <n>] [frame <m>] print <expression>
|
||||||
|
|
||||||
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions.`},
|
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions.`},
|
||||||
{aliases: []string{"whatis"}, allowedPrefixes: scopePrefix, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
|
{aliases: []string{"whatis"}, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
|
||||||
|
|
||||||
whatis <expression>.`},
|
whatis <expression>.`},
|
||||||
{aliases: []string{"set"}, allowedPrefixes: scopePrefix, cmdFn: setVar, helpMsg: `Changes the value of a variable.
|
{aliases: []string{"set"}, cmdFn: setVar, helpMsg: `Changes the value of a variable.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] set <variable> = <value>
|
[goroutine <n>] [frame <m>] set <variable> = <value>
|
||||||
|
|
||||||
@ -177,12 +189,12 @@ If regex is specified only the functions matching it will be returned.`},
|
|||||||
types [<regex>]
|
types [<regex>]
|
||||||
|
|
||||||
If regex is specified only the types matching it will be returned.`},
|
If regex is specified only the types matching it will be returned.`},
|
||||||
{aliases: []string{"args"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: args, helpMsg: `Print function arguments.
|
{aliases: []string{"args"}, allowedPrefixes: onPrefix, cmdFn: args, helpMsg: `Print function arguments.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] args [-v] [<regex>]
|
[goroutine <n>] [frame <m>] args [-v] [<regex>]
|
||||||
|
|
||||||
If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.`},
|
If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.`},
|
||||||
{aliases: []string{"locals"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: locals, helpMsg: `Print local variables.
|
{aliases: []string{"locals"}, allowedPrefixes: onPrefix, cmdFn: locals, helpMsg: `Print local variables.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] locals [-v] [<regex>]
|
[goroutine <n>] [frame <m>] locals [-v] [<regex>]
|
||||||
|
|
||||||
@ -200,25 +212,53 @@ If regex is specified only package variables with a name matching it will be ret
|
|||||||
|
|
||||||
Argument -a shows more registers.`},
|
Argument -a shows more registers.`},
|
||||||
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
|
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
|
||||||
{aliases: []string{"list", "ls", "l"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: `Show source code.
|
{aliases: []string{"list", "ls", "l"}, cmdFn: listCommand, helpMsg: `Show source code.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] list [<linespec>]
|
[goroutine <n>] [frame <m>] list [<linespec>]
|
||||||
|
|
||||||
Show source around current point or provided linespec.`},
|
Show source around current point or provided linespec.`},
|
||||||
{aliases: []string{"stack", "bt"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
|
{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-g] [-s] [-offsets]
|
[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-g] [-s] [-offsets]
|
||||||
|
|
||||||
-full every stackframe is decorated with the value of its local variables and arguments.
|
-full every stackframe is decorated with the value of its local variables and arguments.
|
||||||
-offsets prints frame offset of each frame
|
-offsets prints frame offset of each frame
|
||||||
`},
|
`},
|
||||||
{aliases: []string{"frame"}, allowedPrefixes: scopePrefix, cmdFn: c.frame, helpMsg: `Executes command on a different frame.
|
{aliases: []string{"frame"},
|
||||||
|
cmdFn: func(t *Term, ctx callContext, arg string) error {
|
||||||
|
return c.frameCommand(t, ctx, arg, frameSet)
|
||||||
|
},
|
||||||
|
helpMsg: `Set the current frame, or execute command on a different frame.
|
||||||
|
|
||||||
frame <frame index> <command>.`},
|
frame <m>
|
||||||
|
frame <m> <command>
|
||||||
|
|
||||||
|
The first form sets frame used by subsequent commands such as "print" or "set".
|
||||||
|
The second form runs the command on the given frame.`},
|
||||||
|
{aliases: []string{"up"},
|
||||||
|
cmdFn: func(t *Term, ctx callContext, arg string) error {
|
||||||
|
return c.frameCommand(t, ctx, arg, frameUp)
|
||||||
|
},
|
||||||
|
helpMsg: `Move the current frame up.
|
||||||
|
|
||||||
|
up [<m>]
|
||||||
|
up [<m>] <command>
|
||||||
|
|
||||||
|
Move the current frame up by <m>. The second form runs the command on the given frame.`},
|
||||||
|
{aliases: []string{"down"},
|
||||||
|
cmdFn: func(t *Term, ctx callContext, arg string) error {
|
||||||
|
return c.frameCommand(t, ctx, arg, frameDown)
|
||||||
|
},
|
||||||
|
helpMsg: `Move the current frame down.
|
||||||
|
|
||||||
|
down [<m>]
|
||||||
|
down [<m>] <command>
|
||||||
|
|
||||||
|
Move the current frame down by <m>. The second form runs the command on the given frame.`},
|
||||||
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
|
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
|
||||||
|
|
||||||
source <path>`},
|
source <path>`},
|
||||||
{aliases: []string{"disassemble", "disass"}, allowedPrefixes: scopePrefix, cmdFn: disassCommand, helpMsg: `Disassembler.
|
{aliases: []string{"disassemble", "disass"}, cmdFn: disassCommand, helpMsg: `Disassembler.
|
||||||
|
|
||||||
[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]
|
[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]
|
||||||
|
|
||||||
@ -349,7 +389,7 @@ func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) Call(cmdstr string, t *Term) error {
|
func (c *Commands) Call(cmdstr string, t *Term) error {
|
||||||
ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: 0}}
|
ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame}}
|
||||||
return c.CallWithContext(cmdstr, t, ctx)
|
return c.CallWithContext(cmdstr, t, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -544,9 +584,6 @@ func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
if ctx.Prefix == scopePrefix {
|
|
||||||
return errors.New("no command passed to goroutine")
|
|
||||||
}
|
|
||||||
if args[0] == "" {
|
if args[0] == "" {
|
||||||
return printscope(t)
|
return printscope(t)
|
||||||
}
|
}
|
||||||
@ -563,13 +600,12 @@ func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.frame = 0
|
||||||
fmt.Printf("Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
|
fmt.Printf("Switched from %d to %d (thread %d)\n", selectedGID(oldState), gid, newState.CurrentThread.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
ctx.Prefix = scopePrefix
|
|
||||||
ctx.Scope.GoroutineID, err = strconv.Atoi(args[0])
|
ctx.Scope.GoroutineID, err = strconv.Atoi(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -577,21 +613,54 @@ func (c *Commands) goroutine(t *Term, ctx callContext, argstr string) error {
|
|||||||
return c.CallWithContext(args[1], t, ctx)
|
return c.CallWithContext(args[1], t, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Commands) frame(t *Term, ctx callContext, args string) error {
|
// Handle "frame", "up", "down" commands.
|
||||||
v := strings.SplitN(args, " ", 2)
|
func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, direction frameDirection) error {
|
||||||
|
frame := 1
|
||||||
switch len(v) {
|
arg := ""
|
||||||
case 0, 1:
|
if len(argstr) == 0 {
|
||||||
return errors.New("not enough arguments")
|
if direction == frameSet {
|
||||||
|
return errors.New("not enough arguments")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args := strings.SplitN(argstr, " ", 2)
|
||||||
|
var err error
|
||||||
|
if frame, err = strconv.Atoi(args[0]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
arg = args[1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
switch direction {
|
||||||
var err error
|
case frameUp:
|
||||||
ctx.Prefix = scopePrefix
|
frame = c.frame + frame
|
||||||
ctx.Scope.Frame, err = strconv.Atoi(v[0])
|
case frameDown:
|
||||||
|
frame = c.frame - frame
|
||||||
|
}
|
||||||
|
if len(arg) > 0 {
|
||||||
|
ctx.Scope.Frame = frame
|
||||||
|
return c.CallWithContext(arg, t, ctx)
|
||||||
|
}
|
||||||
|
if frame < 0 {
|
||||||
|
return fmt.Errorf("Invalid frame %d", frame)
|
||||||
|
}
|
||||||
|
stack, err := t.client.Stacktrace(ctx.Scope.GoroutineID, frame, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.CallWithContext(v[1], t, ctx)
|
if frame >= len(stack) {
|
||||||
|
return fmt.Errorf("Invalid frame %d", frame)
|
||||||
|
}
|
||||||
|
c.frame = frame
|
||||||
|
state, err := t.client.GetState()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
printcontext(t, state)
|
||||||
|
th := stack[frame]
|
||||||
|
fmt.Printf("Frame %d: %s:%d (PC: %x)\n", frame, ShortenFilePath(th.File), th.Line, th.PC)
|
||||||
|
printfile(t, th.File, th.Line, true)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printscope(t *Term) error {
|
func printscope(t *Term) error {
|
||||||
@ -730,7 +799,8 @@ func printfileNoState(t *Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cont(t *Term, ctx callContext, args string) error {
|
func (c *Commands) cont(t *Term, ctx callContext, args string) error {
|
||||||
|
c.frame = 0
|
||||||
stateChan := t.client.Continue()
|
stateChan := t.client.Continue()
|
||||||
var state *api.DebuggerState
|
var state *api.DebuggerState
|
||||||
for state = range stateChan {
|
for state = range stateChan {
|
||||||
@ -768,12 +838,6 @@ func continueUntilCompleteNext(t *Term, state *api.DebuggerState, op string) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scopePrefixSwitch(t *Term, ctx callContext) error {
|
func scopePrefixSwitch(t *Term, ctx callContext) error {
|
||||||
if ctx.Prefix != scopePrefix {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ctx.Scope.Frame != 0 {
|
|
||||||
return errors.New("frame prefix not accepted")
|
|
||||||
}
|
|
||||||
if ctx.Scope.GoroutineID > 0 {
|
if ctx.Scope.GoroutineID > 0 {
|
||||||
_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
|
_, err := t.client.SwitchGoroutine(ctx.Scope.GoroutineID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -790,10 +854,11 @@ func exitedToError(state *api.DebuggerState, err error) (*api.DebuggerState, err
|
|||||||
return state, err
|
return state, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func step(t *Term, ctx callContext, args string) error {
|
func (c *Commands) step(t *Term, ctx callContext, args string) error {
|
||||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.frame = 0
|
||||||
state, err := exitedToError(t.client.Step())
|
state, err := exitedToError(t.client.Step())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printfileNoState(t)
|
printfileNoState(t)
|
||||||
@ -803,11 +868,12 @@ func step(t *Term, ctx callContext, args string) error {
|
|||||||
return continueUntilCompleteNext(t, state, "step")
|
return continueUntilCompleteNext(t, state, "step")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stepInstruction(t *Term, ctx callContext, args string) error {
|
func (c *Commands) stepInstruction(t *Term, ctx callContext, args string) error {
|
||||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state, err := exitedToError(t.client.StepInstruction())
|
state, err := exitedToError(t.client.StepInstruction())
|
||||||
|
c.frame = 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printfileNoState(t)
|
printfileNoState(t)
|
||||||
return err
|
return err
|
||||||
@ -817,11 +883,12 @@ func stepInstruction(t *Term, ctx callContext, args string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func next(t *Term, ctx callContext, args string) error {
|
func (c *Commands) next(t *Term, ctx callContext, args string) error {
|
||||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state, err := exitedToError(t.client.Next())
|
state, err := exitedToError(t.client.Next())
|
||||||
|
c.frame = 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printfileNoState(t)
|
printfileNoState(t)
|
||||||
return err
|
return err
|
||||||
@ -830,11 +897,12 @@ func next(t *Term, ctx callContext, args string) error {
|
|||||||
return continueUntilCompleteNext(t, state, "next")
|
return continueUntilCompleteNext(t, state, "next")
|
||||||
}
|
}
|
||||||
|
|
||||||
func stepout(t *Term, ctx callContext, args string) error {
|
func (c *Commands) stepout(t *Term, ctx callContext, args string) error {
|
||||||
if err := scopePrefixSwitch(t, ctx); err != nil {
|
if err := scopePrefixSwitch(t, ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
state, err := exitedToError(t.client.StepOut())
|
state, err := exitedToError(t.client.StepOut())
|
||||||
|
c.frame = 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printfileNoState(t)
|
printfileNoState(t)
|
||||||
return err
|
return err
|
||||||
@ -949,7 +1017,7 @@ func breakpoints(t *Term, ctx callContext, args string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setBreakpoint(t *Term, tracepoint bool, argstr string) error {
|
func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) error {
|
||||||
args := strings.SplitN(argstr, " ", 2)
|
args := strings.SplitN(argstr, " ", 2)
|
||||||
|
|
||||||
requestedBp := &api.Breakpoint{}
|
requestedBp := &api.Breakpoint{}
|
||||||
@ -969,7 +1037,7 @@ func setBreakpoint(t *Term, tracepoint bool, argstr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
requestedBp.Tracepoint = tracepoint
|
requestedBp.Tracepoint = tracepoint
|
||||||
locs, err := t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, locspec)
|
locs, err := t.client.FindLocation(ctx.Scope, locspec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if requestedBp.Name == "" {
|
if requestedBp.Name == "" {
|
||||||
return err
|
return err
|
||||||
@ -977,7 +1045,7 @@ func setBreakpoint(t *Term, tracepoint bool, argstr string) error {
|
|||||||
requestedBp.Name = ""
|
requestedBp.Name = ""
|
||||||
locspec = argstr
|
locspec = argstr
|
||||||
var err2 error
|
var err2 error
|
||||||
locs, err2 = t.client.FindLocation(api.EvalScope{GoroutineID: -1, Frame: 0}, locspec)
|
locs, err2 = t.client.FindLocation(ctx.Scope, locspec)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -996,11 +1064,11 @@ func setBreakpoint(t *Term, tracepoint bool, argstr string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func breakpoint(t *Term, ctx callContext, args string) error {
|
func breakpoint(t *Term, ctx callContext, args string) error {
|
||||||
return setBreakpoint(t, false, args)
|
return setBreakpoint(t, ctx, false, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tracepoint(t *Term, ctx callContext, args string) error {
|
func tracepoint(t *Term, ctx callContext, args string) error {
|
||||||
return setBreakpoint(t, true, args)
|
return setBreakpoint(t, ctx, true, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printVar(t *Term, ctx callContext, args string) error {
|
func printVar(t *Term, ctx callContext, args string) error {
|
||||||
@ -1228,7 +1296,7 @@ func parseStackArgs(argstr string) (stackArgs, error) {
|
|||||||
|
|
||||||
func listCommand(t *Term, ctx callContext, args string) error {
|
func listCommand(t *Term, ctx callContext, args string) error {
|
||||||
switch {
|
switch {
|
||||||
case len(args) == 0 && ctx.Prefix != scopePrefix:
|
case len(args) == 0 && !ctx.scoped():
|
||||||
state, err := t.client.GetState()
|
state, err := t.client.GetState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1236,7 +1304,7 @@ func listCommand(t *Term, ctx callContext, args string) error {
|
|||||||
printcontext(t, state)
|
printcontext(t, state)
|
||||||
return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
return printfile(t, state.CurrentThread.File, state.CurrentThread.Line, true)
|
||||||
|
|
||||||
case len(args) == 0 && ctx.Prefix == scopePrefix:
|
case len(args) == 0 && ctx.scoped():
|
||||||
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, nil)
|
locs, err := t.client.Stacktrace(ctx.Scope.GoroutineID, ctx.Scope.Frame, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -343,10 +343,6 @@ func TestScopePrefix(t *testing.T) {
|
|||||||
term.MustExec("c")
|
term.MustExec("c")
|
||||||
|
|
||||||
term.AssertExecError("frame", "not enough arguments")
|
term.AssertExecError("frame", "not enough arguments")
|
||||||
term.AssertExecError("frame 1", "not enough arguments")
|
|
||||||
term.AssertExecError("frame 1 goroutines", "command not available")
|
|
||||||
term.AssertExecError("frame 1 goroutine", "no command passed to goroutine")
|
|
||||||
term.AssertExecError(fmt.Sprintf("frame 1 goroutine %d", curgid), "no command passed to goroutine")
|
|
||||||
term.AssertExecError(fmt.Sprintf("goroutine %d frame 10 locals", curgid), fmt.Sprintf("Frame 10 does not exist in goroutine %d", curgid))
|
term.AssertExecError(fmt.Sprintf("goroutine %d frame 10 locals", curgid), fmt.Sprintf("Frame 10 does not exist in goroutine %d", curgid))
|
||||||
term.AssertExecError("goroutine 9000 locals", "Unknown goroutine 9000")
|
term.AssertExecError("goroutine 9000 locals", "Unknown goroutine 9000")
|
||||||
|
|
||||||
@ -356,6 +352,26 @@ func TestScopePrefix(t *testing.T) {
|
|||||||
term.AssertExec("frame 3 print n", "1\n")
|
term.AssertExec("frame 3 print n", "1\n")
|
||||||
term.AssertExec("frame 4 print n", "0\n")
|
term.AssertExec("frame 4 print n", "0\n")
|
||||||
term.AssertExecError("frame 5 print n", "could not find symbol value for n")
|
term.AssertExecError("frame 5 print n", "could not find symbol value for n")
|
||||||
|
|
||||||
|
term.MustExec("frame 2")
|
||||||
|
term.AssertExec("print n", "2\n")
|
||||||
|
term.MustExec("frame 4")
|
||||||
|
term.AssertExec("print n", "0\n")
|
||||||
|
term.MustExec("down")
|
||||||
|
term.AssertExec("print n", "1\n")
|
||||||
|
term.MustExec("down 2")
|
||||||
|
term.AssertExec("print n", "3\n")
|
||||||
|
term.AssertExecError("down 2", "Invalid frame -1")
|
||||||
|
term.AssertExec("print n", "3\n")
|
||||||
|
term.MustExec("up 2")
|
||||||
|
term.AssertExec("print n", "1\n")
|
||||||
|
term.AssertExecError("up 100", "Invalid frame 103")
|
||||||
|
term.AssertExec("print n", "1\n")
|
||||||
|
|
||||||
|
term.MustExec("step")
|
||||||
|
term.AssertExecError("print n", "could not find symbol value for n")
|
||||||
|
term.MustExec("frame 2")
|
||||||
|
term.AssertExec("print n", "2\n")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user