service/debugger,terminal: API and user interface for follow exec mode (#3286)
Updates #2551
This commit is contained in:
parent
47481fe0ab
commit
a61ccea65a
@ -91,6 +91,7 @@ Command | Description
|
|||||||
[list](#list) | Show source code.
|
[list](#list) | Show source code.
|
||||||
[source](#source) | Executes a file containing a list of delve commands
|
[source](#source) | Executes a file containing a list of delve commands
|
||||||
[sources](#sources) | Print list of source files.
|
[sources](#sources) | Print list of source files.
|
||||||
|
[target](#target) | Manages child process debugging.
|
||||||
[transcript](#transcript) | Appends command output to a file.
|
[transcript](#transcript) | Appends command output to a file.
|
||||||
[types](#types) | Print list of types
|
[types](#types) | Print list of types
|
||||||
|
|
||||||
@ -623,6 +624,22 @@ Step out of the current function.
|
|||||||
|
|
||||||
Aliases: so
|
Aliases: so
|
||||||
|
|
||||||
|
## target
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
## thread
|
## thread
|
||||||
Switch to the specified thread.
|
Switch to the specified thread.
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ dump_wait(Wait) | Equivalent to API call [DumpWait](https://godoc.org/github.com
|
|||||||
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
|
eval(Scope, Expr, Cfg) | Equivalent to API call [Eval](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.Eval)
|
||||||
examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory)
|
examine_memory(Address, Length) | Equivalent to API call [ExamineMemory](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ExamineMemory)
|
||||||
find_location(Scope, Loc, IncludeNonExecutableLines, SubstitutePathRules) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
|
find_location(Scope, Loc, IncludeNonExecutableLines, SubstitutePathRules) | Equivalent to API call [FindLocation](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FindLocation)
|
||||||
|
follow_exec(Enable, Regex) | Equivalent to API call [FollowExec](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FollowExec)
|
||||||
|
follow_exec_enabled() | Equivalent to API call [FollowExecEnabled](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FollowExecEnabled)
|
||||||
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
|
function_return_locations(FnName) | Equivalent to API call [FunctionReturnLocations](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.FunctionReturnLocations)
|
||||||
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
|
get_breakpoint(Id, Name) | Equivalent to API call [GetBreakpoint](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBreakpoint)
|
||||||
get_buffered_tracepoints() | Equivalent to API call [GetBufferedTracepoints](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBufferedTracepoints)
|
get_buffered_tracepoints() | Equivalent to API call [GetBufferedTracepoints](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.GetBufferedTracepoints)
|
||||||
@ -54,6 +56,7 @@ package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://god
|
|||||||
packages_build_info(IncludeFiles) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
|
packages_build_info(IncludeFiles) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
|
||||||
registers(ThreadID, IncludeFp, Scope) | Equivalent to API call [ListRegisters](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
|
registers(ThreadID, IncludeFp, Scope) | Equivalent to API call [ListRegisters](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
|
||||||
sources(Filter) | Equivalent to API call [ListSources](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
|
sources(Filter) | Equivalent to API call [ListSources](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
|
||||||
|
targets() | Equivalent to API call [ListTargets](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTargets)
|
||||||
threads() | Equivalent to API call [ListThreads](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListThreads)
|
threads() | Equivalent to API call [ListThreads](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListThreads)
|
||||||
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
types(Filter) | Equivalent to API call [ListTypes](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTypes)
|
||||||
process_pid() | Equivalent to API call [ProcessPid](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ProcessPid)
|
process_pid() | Equivalent to API call [ProcessPid](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ProcessPid)
|
||||||
|
@ -354,6 +354,11 @@ func (grp *TargetGroup) FollowExec(v bool, regex string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FollowExecEnabled returns true if follow exec is enabled
|
||||||
|
func (grp *TargetGroup) FollowExecEnabled() bool {
|
||||||
|
return grp.followExecEnabled
|
||||||
|
}
|
||||||
|
|
||||||
// ValidTargets iterates through all valid targets in Group.
|
// ValidTargets iterates through all valid targets in Group.
|
||||||
type ValidTargets struct {
|
type ValidTargets struct {
|
||||||
*Target
|
*Target
|
||||||
|
@ -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.
|
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.`},
|
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
|
addrecorded := client == nil
|
||||||
@ -1212,6 +1226,7 @@ func parseOptionalCount(arg string) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func restartLive(t *Term, ctx callContext, args string) error {
|
func restartLive(t *Term, ctx callContext, args string) error {
|
||||||
|
t.oldPid = 0
|
||||||
resetArgs, newArgv, newRedirects, err := parseNewArgv(args)
|
resetArgs, newArgv, newRedirects, err := parseNewArgv(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -2522,6 +2537,12 @@ func printcontext(t *Term, state *api.DebuggerState) {
|
|||||||
return
|
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 {
|
for i := range state.Threads {
|
||||||
if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) {
|
if (state.CurrentThread != nil) && (state.Threads[i].ID == state.CurrentThread.ID) {
|
||||||
continue
|
continue
|
||||||
@ -3177,6 +3198,71 @@ func transcript(t *Term, ctx callContext, args string) error {
|
|||||||
return nil
|
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 {
|
func formatBreakpointName(bp *api.Breakpoint, upcase bool) string {
|
||||||
thing := "breakpoint"
|
thing := "breakpoint"
|
||||||
if bp.Tracepoint {
|
if bp.Tracepoint {
|
||||||
@ -3226,5 +3312,5 @@ func (t *Term) formatBreakpointLocation(bp *api.Breakpoint) string {
|
|||||||
func shouldAskToSuspendBreakpoint(t *Term) bool {
|
func shouldAskToSuspendBreakpoint(t *Term) bool {
|
||||||
fns, _ := t.client.ListFunctions(`^plugin\.Open$`)
|
fns, _ := t.client.ListFunctions(`^plugin\.Open$`)
|
||||||
_, err := t.client.GetState()
|
_, err := t.client.GetState()
|
||||||
return len(fns) > 0 || isErrProcessExited(err)
|
return len(fns) > 0 || isErrProcessExited(err) || t.client.FollowExecEnabled()
|
||||||
}
|
}
|
||||||
|
@ -730,6 +730,56 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
}
|
}
|
||||||
return env.interfaceToStarlarkValue(rpcRet), nil
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
})
|
})
|
||||||
|
r["follow_exec"] = starlark.NewBuiltin("follow_exec", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
|
if err := isCancelled(thread); err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
var rpcArgs rpc2.FollowExecIn
|
||||||
|
var rpcRet rpc2.FollowExecOut
|
||||||
|
if len(args) > 0 && args[0] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[0], &rpcArgs.Enable, "Enable")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) > 1 && args[1] != starlark.None {
|
||||||
|
err := unmarshalStarlarkValue(args[1], &rpcArgs.Regex, "Regex")
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, kv := range kwargs {
|
||||||
|
var err error
|
||||||
|
switch kv[0].(starlark.String) {
|
||||||
|
case "Enable":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Enable, "Enable")
|
||||||
|
case "Regex":
|
||||||
|
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Regex, "Regex")
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown argument %q", kv[0])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := env.ctx.Client().CallAPI("FollowExec", &rpcArgs, &rpcRet)
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, err
|
||||||
|
}
|
||||||
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
|
})
|
||||||
|
r["follow_exec_enabled"] = starlark.NewBuiltin("follow_exec_enabled", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
|
if err := isCancelled(thread); err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
var rpcArgs rpc2.FollowExecEnabledIn
|
||||||
|
var rpcRet rpc2.FollowExecEnabledOut
|
||||||
|
err := env.ctx.Client().CallAPI("FollowExecEnabled", &rpcArgs, &rpcRet)
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, err
|
||||||
|
}
|
||||||
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
|
})
|
||||||
r["function_return_locations"] = starlark.NewBuiltin("function_return_locations", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
r["function_return_locations"] = starlark.NewBuiltin("function_return_locations", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
if err := isCancelled(thread); err != nil {
|
if err := isCancelled(thread); err != nil {
|
||||||
return starlark.None, decorateError(thread, err)
|
return starlark.None, decorateError(thread, err)
|
||||||
@ -1235,6 +1285,18 @@ func (env *Env) starlarkPredeclare() starlark.StringDict {
|
|||||||
}
|
}
|
||||||
return env.interfaceToStarlarkValue(rpcRet), nil
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
})
|
})
|
||||||
|
r["targets"] = starlark.NewBuiltin("targets", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
|
if err := isCancelled(thread); err != nil {
|
||||||
|
return starlark.None, decorateError(thread, err)
|
||||||
|
}
|
||||||
|
var rpcArgs rpc2.ListTargetsIn
|
||||||
|
var rpcRet rpc2.ListTargetsOut
|
||||||
|
err := env.ctx.Client().CallAPI("ListTargets", &rpcArgs, &rpcRet)
|
||||||
|
if err != nil {
|
||||||
|
return starlark.None, err
|
||||||
|
}
|
||||||
|
return env.interfaceToStarlarkValue(rpcRet), nil
|
||||||
|
})
|
||||||
r["threads"] = starlark.NewBuiltin("threads", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
r["threads"] = starlark.NewBuiltin("threads", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
|
||||||
if err := isCancelled(thread); err != nil {
|
if err := isCancelled(thread); err != nil {
|
||||||
return starlark.None, decorateError(thread, err)
|
return starlark.None, decorateError(thread, err)
|
||||||
|
@ -58,6 +58,7 @@ type Term struct {
|
|||||||
stdout *transcriptWriter
|
stdout *transcriptWriter
|
||||||
InitFile string
|
InitFile string
|
||||||
displays []displayEntry
|
displays []displayEntry
|
||||||
|
oldPid int
|
||||||
|
|
||||||
historyFile *os.File
|
historyFile *os.File
|
||||||
|
|
||||||
@ -115,6 +116,9 @@ func New(client service.Client, conf *config.Config) *Term {
|
|||||||
if client != nil {
|
if client != nil {
|
||||||
lcfg := t.loadConfig()
|
lcfg := t.loadConfig()
|
||||||
client.SetReturnValuesLoadConfig(&lcfg)
|
client.SetReturnValuesLoadConfig(&lcfg)
|
||||||
|
if state, err := client.GetState(); err == nil {
|
||||||
|
t.oldPid = state.Pid
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.starlarkEnv = starbind.New(starlarkContext{t}, t.stdout)
|
t.starlarkEnv = starbind.New(starlarkContext{t}, t.stdout)
|
||||||
|
@ -429,10 +429,12 @@ func ConvertRegisters(in *op.DwarfRegisters, dwarfRegisterToString func(int, *op
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConvertImage convers proc.Image to api.Image.
|
||||||
func ConvertImage(image *proc.Image) Image {
|
func ConvertImage(image *proc.Image) Image {
|
||||||
return Image{Path: image.Path, Address: image.StaticBase}
|
return Image{Path: image.Path, Address: image.StaticBase}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConvertDumpState converts proc.DumpState to api.DumpState.
|
||||||
func ConvertDumpState(dumpState *proc.DumpState) *DumpState {
|
func ConvertDumpState(dumpState *proc.DumpState) *DumpState {
|
||||||
dumpState.Mutex.Lock()
|
dumpState.Mutex.Lock()
|
||||||
defer dumpState.Mutex.Unlock()
|
defer dumpState.Mutex.Unlock()
|
||||||
@ -449,3 +451,12 @@ func ConvertDumpState(dumpState *proc.DumpState) *DumpState {
|
|||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConvertTarget converts a proc.Target into a api.Target.
|
||||||
|
func ConvertTarget(tgt *proc.Target, convertThreadBreakpoint func(proc.Thread) *Breakpoint) *Target {
|
||||||
|
//TODO(aarzilli): copy command line here
|
||||||
|
return &Target{
|
||||||
|
Pid: tgt.Pid(),
|
||||||
|
CurrentThread: ConvertThread(tgt.CurrentThread(), convertThreadBreakpoint(tgt.CurrentThread())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -655,3 +655,10 @@ type GoroutineGroupingOptions struct {
|
|||||||
MaxGroupMembers int
|
MaxGroupMembers int
|
||||||
MaxGroups int
|
MaxGroups int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Target represents a debugging target.
|
||||||
|
type Target struct {
|
||||||
|
Pid int
|
||||||
|
CmdLine string
|
||||||
|
CurrentThread *Thread
|
||||||
|
}
|
||||||
|
@ -185,6 +185,14 @@ type Client interface {
|
|||||||
// CoreDumpCancel cancels a core dump in progress
|
// CoreDumpCancel cancels a core dump in progress
|
||||||
CoreDumpCancel() error
|
CoreDumpCancel() error
|
||||||
|
|
||||||
|
// ListTargets returns the list of connected targets
|
||||||
|
ListTargets() ([]api.Target, error)
|
||||||
|
// FollowExec enables or disables the follow exec mode. In follow exec mode
|
||||||
|
// Delve will automatically debug child processes launched by the target
|
||||||
|
// process
|
||||||
|
FollowExec(bool, string) error
|
||||||
|
FollowExecEnabled() bool
|
||||||
|
|
||||||
// Disconnect closes the connection to the server without sending a Detach request first.
|
// Disconnect closes the connection to the server without sending a Detach request first.
|
||||||
// If cont is true a continue command will be sent instead.
|
// If cont is true a continue command will be sent instead.
|
||||||
Disconnect(cont bool) error
|
Disconnect(cont bool) error
|
||||||
|
@ -1264,6 +1264,13 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
|
|||||||
err = d.target.StepOut()
|
err = d.target.StepOut()
|
||||||
case api.SwitchThread:
|
case api.SwitchThread:
|
||||||
d.log.Debugf("switching to thread %d", command.ThreadID)
|
d.log.Debugf("switching to thread %d", command.ThreadID)
|
||||||
|
t := proc.ValidTargets{Group: d.target}
|
||||||
|
for t.Next() {
|
||||||
|
if _, ok := t.FindThread(command.ThreadID); ok {
|
||||||
|
d.target.Selected = t.Target
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
err = d.target.Selected.SwitchThread(command.ThreadID)
|
err = d.target.Selected.SwitchThread(command.ThreadID)
|
||||||
withBreakpointInfo = false
|
withBreakpointInfo = false
|
||||||
case api.SwitchGoroutine:
|
case api.SwitchGoroutine:
|
||||||
@ -2252,6 +2259,20 @@ func (d *Debugger) GetBufferedTracepoints() []api.TracepointResult {
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FollowExec enabled or disables follow exec mode.
|
||||||
|
func (d *Debugger) FollowExec(enabled bool, regex string) error {
|
||||||
|
d.targetMutex.Lock()
|
||||||
|
defer d.targetMutex.Unlock()
|
||||||
|
return d.target.FollowExec(enabled, regex)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExecEnabled returns true if follow exec mode is enabled.
|
||||||
|
func (d *Debugger) FollowExecEnabled() bool {
|
||||||
|
d.targetMutex.Lock()
|
||||||
|
defer d.targetMutex.Unlock()
|
||||||
|
return d.target.FollowExecEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
func go11DecodeErrorCheck(err error) error {
|
func go11DecodeErrorCheck(err error) error {
|
||||||
if _, isdecodeerr := err.(dwarf.DecodeError); !isdecodeerr {
|
if _, isdecodeerr := err.(dwarf.DecodeError); !isdecodeerr {
|
||||||
return err
|
return err
|
||||||
|
@ -530,6 +530,30 @@ func (c *RPCClient) CoreDumpCancel() error {
|
|||||||
return c.call("DumpCancel", DumpCancelIn{}, out)
|
return c.call("DumpCancel", DumpCancelIn{}, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListTargets returns the current list of debug targets.
|
||||||
|
func (c *RPCClient) ListTargets() ([]api.Target, error) {
|
||||||
|
out := &ListTargetsOut{}
|
||||||
|
err := c.call("ListTargets", ListTargetsIn{}, out)
|
||||||
|
return out.Targets, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExec enabled or disabled follow exec mode. When follow exec is
|
||||||
|
// enabled Delve will automatically attach to new subprocesses with a
|
||||||
|
// command line matched by regex, if regex is nil all new subprocesses are
|
||||||
|
// automatically debugged.
|
||||||
|
func (c *RPCClient) FollowExec(v bool, regex string) error {
|
||||||
|
out := &FollowExecOut{}
|
||||||
|
err := c.call("FollowExec", FollowExecIn{Enable: v, Regex: regex}, out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExecEnabled returns true if follow exex mode is enabled.
|
||||||
|
func (c *RPCClient) FollowExecEnabled() bool {
|
||||||
|
out := &FollowExecEnabledOut{}
|
||||||
|
_ = c.call("FollowExecEnabled", FollowExecEnabledIn{}, out)
|
||||||
|
return out.Enabled
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
||||||
return c.client.Call("RPCServer."+method, args, reply)
|
return c.client.Call("RPCServer."+method, args, reply)
|
||||||
}
|
}
|
||||||
|
@ -1033,3 +1033,49 @@ func (s *RPCServer) BuildID(arg BuildIDIn, out *BuildIDOut) error {
|
|||||||
out.BuildID = s.debugger.BuildID()
|
out.BuildID = s.debugger.BuildID()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListTargetsIn struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListTargetsOut struct {
|
||||||
|
Targets []api.Target
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTargets returns the list of targets we are currently attached to.
|
||||||
|
func (s *RPCServer) ListTargets(arg ListTargetsIn, out *ListTargetsOut) error {
|
||||||
|
s.debugger.LockTarget()
|
||||||
|
defer s.debugger.UnlockTarget()
|
||||||
|
out.Targets = []api.Target{}
|
||||||
|
for _, tgt := range s.debugger.TargetGroup().Targets() {
|
||||||
|
if _, err := tgt.Valid(); err == nil {
|
||||||
|
out.Targets = append(out.Targets, *api.ConvertTarget(tgt, s.debugger.ConvertThreadBreakpoint))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type FollowExecIn struct {
|
||||||
|
Enable bool
|
||||||
|
Regex string
|
||||||
|
}
|
||||||
|
|
||||||
|
type FollowExecOut struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExec enables or disables follow exec mode.
|
||||||
|
func (s *RPCServer) FollowExec(arg FollowExecIn, out *FollowExecOut) error {
|
||||||
|
return s.debugger.FollowExec(arg.Enable, arg.Regex)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FollowExecEnabledIn struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type FollowExecEnabledOut struct {
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExecEnabled returns true if follow exec mode is enabled.
|
||||||
|
func (s *RPCServer) FollowExecEnabled(arg FollowExecEnabledIn, out *FollowExecEnabledOut) error {
|
||||||
|
out.Enabled = s.debugger.FollowExecEnabled()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user