terminal: Add [linespec] argument to 'continue' command (#2376)

This change allows specifying an optional linespec after the 'continue'
command which sets a temporary breakpoint.

Fixes #2373
This commit is contained in:
Ilia Choly 2021-03-11 16:27:29 -05:00 committed by GitHub
parent 375f442949
commit d1834df3c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 58 additions and 14 deletions

@ -206,6 +206,16 @@ Defines <alias> as an alias to <command> or removes an alias.
## continue ## continue
Run until breakpoint or program termination. Run until breakpoint or program termination.
continue [<linespec>]
Optional linespec argument allows you to continue until a specific location is reached. The program will halt if a breakpoint is hit before reaching the specified location.
For example:
continue main.main
continue encoding/json.Marshal
Aliases: c Aliases: c
## deferred ## deferred
@ -380,7 +390,7 @@ If regex is specified only local variables with a name matching it will be retur
## next ## next
Step over to next source line. Step over to next source line.
next [count] next [count]
Optional [count] argument allows you to skip multiple lines. Optional [count] argument allows you to skip multiple lines.

@ -152,12 +152,22 @@ A list of file redirections can be specified after the new argument list to over
2>error.txt redirects the standard error of the target process to error.txt 2>error.txt redirects the standard error of the target process to error.txt
`}, `},
{aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."}, {aliases: []string{"rebuild"}, group: runCmds, cmdFn: c.rebuild, allowedPrefixes: revPrefix, helpMsg: "Rebuild the target executable and restarts it. It does not work if the executable was not built by delve."},
{aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: "Run until breakpoint or program termination."}, {aliases: []string{"continue", "c"}, group: runCmds, cmdFn: c.cont, allowedPrefixes: revPrefix, helpMsg: `Run until breakpoint or program termination.
continue [<linespec>]
Optional linespec argument allows you to continue until a specific location is reached. The program will halt if a breakpoint is hit before reaching the specified location.
For example:
continue main.main
continue encoding/json.Marshal
`},
{aliases: []string{"step", "s"}, group: runCmds, cmdFn: c.step, allowedPrefixes: revPrefix, helpMsg: "Single step through program."}, {aliases: []string{"step", "s"}, group: runCmds, cmdFn: c.step, allowedPrefixes: revPrefix, helpMsg: "Single step through program."},
{aliases: []string{"step-instruction", "si"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."}, {aliases: []string{"step-instruction", "si"}, group: runCmds, allowedPrefixes: revPrefix, cmdFn: c.stepInstruction, helpMsg: "Single step a single cpu instruction."},
{aliases: []string{"next", "n"}, group: runCmds, cmdFn: c.next, allowedPrefixes: revPrefix, helpMsg: `Step over to next source line. {aliases: []string{"next", "n"}, group: runCmds, cmdFn: c.next, allowedPrefixes: revPrefix, helpMsg: `Step over to next source line.
next [count] next [count]
Optional [count] argument allows you to skip multiple lines. Optional [count] argument allows you to skip multiple lines.
`}, `},
@ -1191,6 +1201,19 @@ func (c *Commands) rebuild(t *Term, ctx callContext, args string) error {
} }
func (c *Commands) cont(t *Term, ctx callContext, args string) error { func (c *Commands) cont(t *Term, ctx callContext, args string) error {
if args != "" {
tmp, err := setBreakpoint(t, ctx, false, args)
if err != nil {
return err
}
defer func() {
for _, bp := range tmp {
if _, err := t.client.ClearBreakpoint(bp.ID); err != nil {
fmt.Printf("failed to clear temporary breakpoint: %d", bp.ID)
}
}
}()
}
if ctx.Prefix == revPrefix { if ctx.Prefix == revPrefix {
return c.rewind(t, ctx, args) return c.rewind(t, ctx, args)
} }
@ -1497,7 +1520,7 @@ func breakpoints(t *Term, ctx callContext, args string) error {
return nil return nil
} }
func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) error { func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) ([]*api.Breakpoint, error) {
args := split2PartsBySpace(argstr) args := split2PartsBySpace(argstr)
requestedBp := &api.Breakpoint{} requestedBp := &api.Breakpoint{}
@ -1513,23 +1536,24 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
spec = argstr spec = argstr
} }
default: default:
return fmt.Errorf("address required") return nil, fmt.Errorf("address required")
} }
requestedBp.Tracepoint = tracepoint requestedBp.Tracepoint = tracepoint
locs, err := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules()) locs, err := t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if err != nil { if err != nil {
if requestedBp.Name == "" { if requestedBp.Name == "" {
return err return nil, err
} }
requestedBp.Name = "" requestedBp.Name = ""
spec = argstr spec = argstr
var err2 error var err2 error
locs, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules()) locs, err2 = t.client.FindLocation(ctx.Scope, spec, true, t.substitutePathRules())
if err2 != nil { if err2 != nil {
return err return nil, err
} }
} }
created := []*api.Breakpoint{}
for _, loc := range locs { for _, loc := range locs {
requestedBp.Addr = loc.PC requestedBp.Addr = loc.PC
requestedBp.Addrs = loc.PCs requestedBp.Addrs = loc.PCs
@ -1539,8 +1563,9 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
bp, err := t.client.CreateBreakpoint(requestedBp) bp, err := t.client.CreateBreakpoint(requestedBp)
if err != nil { if err != nil {
return err return nil, err
} }
created = append(created, bp)
fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp)) fmt.Printf("%s set at %s\n", formatBreakpointName(bp, true), t.formatBreakpointLocation(bp))
} }
@ -1548,7 +1573,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
var shouldSetReturnBreakpoints bool var shouldSetReturnBreakpoints bool
loc, err := locspec.Parse(spec) loc, err := locspec.Parse(spec)
if err != nil { if err != nil {
return err return nil, err
} }
switch t := loc.(type) { switch t := loc.(type) {
case *locspec.NormalLocationSpec: case *locspec.NormalLocationSpec:
@ -1563,7 +1588,7 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
} }
addrs, err := t.client.(*rpc2.RPCClient).FunctionReturnLocations(locs[0].Function.Name()) addrs, err := t.client.(*rpc2.RPCClient).FunctionReturnLocations(locs[0].Function.Name())
if err != nil { if err != nil {
return err return nil, err
} }
for j := range addrs { for j := range addrs {
_, err = t.client.CreateBreakpoint(&api.Breakpoint{ _, err = t.client.CreateBreakpoint(&api.Breakpoint{
@ -1573,20 +1598,22 @@ func setBreakpoint(t *Term, ctx callContext, tracepoint bool, argstr string) err
LoadArgs: &ShortLoadConfig, LoadArgs: &ShortLoadConfig,
}) })
if err != nil { if err != nil {
return err return nil, err
} }
} }
} }
} }
return nil return created, nil
} }
func breakpoint(t *Term, ctx callContext, args string) error { func breakpoint(t *Term, ctx callContext, args string) error {
return setBreakpoint(t, ctx, false, args) _, err := setBreakpoint(t, ctx, false, args)
return err
} }
func tracepoint(t *Term, ctx callContext, args string) error { func tracepoint(t *Term, ctx callContext, args string) error {
return setBreakpoint(t, ctx, true, args) _, err := setBreakpoint(t, ctx, true, args)
return err
} }
func edit(t *Term, ctx callContext, args string) error { func edit(t *Term, ctx callContext, args string) error {

@ -1140,3 +1140,10 @@ func TestParseNewArgv(t *testing.T) {
} }
} }
} }
func TestContinueUntil(t *testing.T) {
withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
listIsAt(t, term, "continue main.main", 16, -1, -1)
listIsAt(t, term, "continue main.sayhi", 12, -1, -1)
})
}