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:
parent
375f442949
commit
d1834df3c5
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user