service/debugger,terminal: API and user interface for follow exec mode (#3286)

Updates #2551
This commit is contained in:
Alessandro Arzilli
2023-04-24 23:37:31 +02:00
committed by GitHub
parent 47481fe0ab
commit a61ccea65a
12 changed files with 295 additions and 1 deletions

View File

@ -559,6 +559,20 @@ The core dump is always written in ELF, even on systems (windows, macOS) where t
Output of Delve's command is appended to the specified output file. If '-t' is specified and the output file exists it is truncated. If '-x' is specified output to stdout is suppressed instead.
Using the -off option disables the transcript.`},
{aliases: []string{"target"}, cmdFn: target, helpMsg: `Manages child process debugging.
target follow-exec [-on [regex]] [-off]
Enables or disables follow exec mode. When follow exec mode Delve will automatically attach to new child processes executed by the target process. An optional regular expression can be passed to 'target follow-exec', only child processes with a command line matching the regular expression will be followed.
target list
List currently attached processes.
target switch [pid]
Switches to the specified process.`},
}
addrecorded := client == nil
@ -1212,6 +1226,7 @@ func parseOptionalCount(arg string) (int64, error) {
}
func restartLive(t *Term, ctx callContext, args string) error {
t.oldPid = 0
resetArgs, newArgv, newRedirects, err := parseNewArgv(args)
if err != nil {
return err
@ -2522,6 +2537,12 @@ func printcontext(t *Term, state *api.DebuggerState) {
return
}
if state.Pid != t.oldPid {
if t.oldPid != 0 {
fmt.Fprintf(t.stdout, "Switch target process from %d to %d\n", t.oldPid, state.Pid)
}
t.oldPid = state.Pid
}
for i := range state.Threads {
if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) {
continue
@ -3177,6 +3198,71 @@ func transcript(t *Term, ctx callContext, args string) error {
return nil
}
func target(t *Term, ctx callContext, args string) error {
argv := config.Split2PartsBySpace(args)
switch argv[0] {
case "list":
tgts, err := t.client.ListTargets()
if err != nil {
return err
}
w := new(tabwriter.Writer)
w.Init(t.stdout, 4, 4, 2, ' ', 0)
for _, tgt := range tgts {
selected := ""
if tgt.Pid == t.oldPid {
selected = "*"
}
fmt.Fprintf(w, "%s\t%d\t%s\n", selected, tgt.Pid, tgt.CmdLine)
}
w.Flush()
return nil
case "follow-exec":
if len(argv) == 1 {
return errors.New("not enough arguments")
}
argv = config.Split2PartsBySpace(argv[1])
switch argv[0] {
case "-on":
var regex string
if len(argv) == 2 {
regex = argv[1]
}
t.client.FollowExec(true, regex)
case "-off":
if len(argv) > 1 {
return errors.New("too many arguments")
}
t.client.FollowExec(false, "")
default:
return fmt.Errorf("unknown argument %q to 'target follow-exec'", argv[0])
}
return nil
case "switch":
tgts, err := t.client.ListTargets()
if err != nil {
return err
}
pid, err := strconv.Atoi(argv[1])
if err != nil {
return err
}
found := false
for _, tgt := range tgts {
if tgt.Pid == pid {
found = true
t.client.SwitchThread(tgt.CurrentThread.ID)
}
}
if !found {
return fmt.Errorf("could not find target %d", pid)
}
return nil
default:
return fmt.Errorf("unknown command 'target %s'", argv[0])
}
}
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
thing := "breakpoint"
if bp.Tracepoint {
@ -3226,5 +3312,5 @@ func (t *Term) formatBreakpointLocation(bp *api.Breakpoint) string {
func shouldAskToSuspendBreakpoint(t *Term) bool {
fns, _ := t.client.ListFunctions(`^plugin\.Open$`)
_, err := t.client.GetState()
return len(fns) > 0 || isErrProcessExited(err)
return len(fns) > 0 || isErrProcessExited(err) || t.client.FollowExecEnabled()
}