delve/service/rpc2/client.go

673 lines
21 KiB
Go
Raw Normal View History

package rpc2
2015-06-21 03:47:44 +00:00
import (
"bytes"
"encoding/json"
2015-06-21 03:47:44 +00:00
"fmt"
"log"
"net"
2015-06-21 03:47:44 +00:00
"net/rpc"
"net/rpc/jsonrpc"
"os/exec"
"path/filepath"
"strings"
"time"
2015-06-21 03:47:44 +00:00
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
2015-06-21 03:47:44 +00:00
)
// RPCClient is a RPC service.Client.
2015-06-21 03:47:44 +00:00
type RPCClient struct {
client *rpc.Client
retValLoadCfg *api.LoadConfig
2015-06-21 03:47:44 +00:00
}
// Ensure the implementation satisfies the interface.
var _ service.Client = &RPCClient{}
// NewClient creates a new RPCClient.
func NewClient(addr string) *RPCClient {
client, err := jsonrpc.Dial("tcp", addr)
if err != nil {
log.Fatal("dialing:", err)
}
return newFromRPCClient(client)
}
func newFromRPCClient(client *rpc.Client) *RPCClient {
c := &RPCClient{client: client}
c.call("SetApiVersion", api.SetAPIVersionIn{APIVersion: 2}, &api.SetAPIVersionOut{})
return c
2015-06-21 03:47:44 +00:00
}
// NewClientFromConn creates a new RPCClient from the given connection.
func NewClientFromConn(conn net.Conn) *RPCClient {
return newFromRPCClient(jsonrpc.NewClient(conn))
}
func (c *RPCClient) ProcessPid() int {
out := new(ProcessPidOut)
c.call("ProcessPid", ProcessPidIn{}, out)
return out.Pid
}
func (c *RPCClient) BuildID() string {
out := new(BuildIDOut)
c.call("BuildID", BuildIDIn{}, out)
return out.BuildID
}
func (c *RPCClient) LastModified() time.Time {
out := new(LastModifiedOut)
c.call("LastModified", LastModifiedIn{}, out)
return out.Time
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) Detach(kill bool) error {
defer c.client.Close()
out := new(DetachOut)
return c.call("Detach", DetachIn{kill}, out)
2015-06-21 03:47:44 +00:00
}
terminal/command: Add 'reload' command (#1971) * terminal/command: Add 'reload' command These changes add the 'reload' command, which allows us to rebuild the project and start the debugging session again. Currently, if the project's code is updated while debugging it, Delve shows the new source code, but it's still running the old one. With 'reload', the whole binary is rebuilt, and the process starts again. Fixes #1551 * Remove unnecessary print Changes to be committed: modified: pkg/terminal/command.go * Add tests and refactor the code Changes to be committed: modified: cmd/dlv/cmds/commands.go modified: go.mod modified: pkg/terminal/command.go modified: service/config.go modified: service/debugger/debugger.go modified: service/test/integration2_test.go * Fix typo in the comment Changes to be committed: modified: service/debugger/debugger.go * Fix typo in the name of the variables The variables are local therefore the capitalization is not needed Changes to be committed: modified: cmd/dlv/cmds/commands.go * Call GoTestBuild Also, remove the := to avoid redeclaration * Change the Kind in the tests Change from debugger.ExecutingGeneratedTest to debugger.ExecutingGeneratedFile for consistency. We are generating a real binary instead of a test one so ExecutingGeneratedFile makes more sense here. Changes to be committed: modified: service/test/integration2_test.go * Avoid breakpoints based on addresses Changes to be committed: modified: service/debugger/debugger.go * Update the rebuild behaviour There are a few cases where we can't rebuild the binary because we don't know how it was build. Changes to be committed: modified: service/debugger/debugger.go * Fix typos and update documentation Changes to be committed: modified: Documentation/cli/README.md modified: pkg/terminal/command.go modified: service/config.go modified: service/debugger/debugger.go * Fix typo * Remove variables They were added to the debugger.Config * Rename variable Rename Kind to ExecuteKind to make it more accurate Changes to be committed: modified: cmd/dlv/cmds/commands.go modified: service/debugger/debugger.go modified: service/test/integration2_test.go
2020-06-05 18:03:09 +00:00
func (c *RPCClient) Restart(rebuild bool) ([]api.DiscardedBreakpoint, error) {
out := new(RestartOut)
err := c.call("Restart", RestartIn{"", false, nil, false, rebuild, [3]string{}}, out)
return out.DiscardedBreakpoints, err
}
func (c *RPCClient) RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
out := new(RestartOut)
err := c.call("Restart", RestartIn{pos, resetArgs, newArgs, rerecord, rebuild, newRedirects}, out)
return out.DiscardedBreakpoints, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
var out StateOut
err := c.call("State", StateIn{NonBlocking: false}, &out)
return out.State, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) GetStateNonBlocking() (*api.DebuggerState, error) {
var out StateOut
err := c.call("State", StateIn{NonBlocking: true}, &out)
return out.State, err
}
2015-07-03 19:18:03 +00:00
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
return c.continueDir(api.Continue)
}
func (c *RPCClient) Rewind() <-chan *api.DebuggerState {
return c.continueDir(api.Rewind)
}
func (c *RPCClient) DirectionCongruentContinue() <-chan *api.DebuggerState {
return c.continueDir(api.DirectionCongruentContinue)
}
func (c *RPCClient) continueDir(cmd string) <-chan *api.DebuggerState {
2015-06-28 15:00:56 +00:00
ch := make(chan *api.DebuggerState)
go func() {
for {
out := new(CommandOut)
err := c.call("Command", &api.DebuggerCommand{Name: cmd, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
state := out.State
2015-07-03 19:18:03 +00:00
if err != nil {
state.Err = err
}
if state.Exited {
2018-03-20 10:05:35 +00:00
// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
//lint:ignore ST1005 backwards compatibility
2015-07-03 19:18:03 +00:00
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
}
ch <- &state
if err != nil || state.Exited {
close(ch)
return
}
isbreakpoint := false
istracepoint := true
for i := range state.Threads {
if state.Threads[i].Breakpoint != nil {
isbreakpoint = true
istracepoint = istracepoint && (state.Threads[i].Breakpoint.Tracepoint || state.Threads[i].Breakpoint.TraceReturn)
}
}
if !isbreakpoint || !istracepoint {
2015-06-28 15:00:56 +00:00
close(ch)
return
}
}
}()
return ch
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) Next() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Next, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ReverseNext() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.ReverseNext, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) Step() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Step, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ReverseStep() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStep, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.StepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) ReverseStepOut() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
return &out.State, err
}
func (c *RPCClient) Call(goroutineID int64, expr string, unsafe bool) (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Call, ReturnInfoLoadConfig: c.retValLoadCfg, Expr: expr, UnsafeCall: unsafe, GoroutineID: goroutineID}, &out)
return &out.State, err
}
func (c *RPCClient) StepInstruction(skipCalls bool) (*api.DebuggerState, error) {
var out CommandOut
name := api.StepInstruction
if skipCalls {
name = api.NextInstruction
}
err := c.call("Command", api.DebuggerCommand{Name: name}, &out)
return &out.State, err
}
func (c *RPCClient) ReverseStepInstruction(skipCalls bool) (*api.DebuggerState, error) {
var out CommandOut
name := api.ReverseStepInstruction
if skipCalls {
name = api.ReverseNextInstruction
}
err := c.call("Command", api.DebuggerCommand{Name: name}, &out)
return &out.State, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
var out CommandOut
cmd := api.DebuggerCommand{
2015-06-21 03:47:44 +00:00
Name: api.SwitchThread,
ThreadID: threadID,
}
err := c.call("Command", cmd, &out)
return &out.State, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) SwitchGoroutine(goroutineID int64) (*api.DebuggerState, error) {
var out CommandOut
cmd := api.DebuggerCommand{
Name: api.SwitchGoroutine,
GoroutineID: goroutineID,
}
err := c.call("Command", cmd, &out)
return &out.State, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) Halt() (*api.DebuggerState, error) {
var out CommandOut
err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out)
return &out.State, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) GetBufferedTracepoints() ([]api.TracepointResult, error) {
var out GetBufferedTracepointsOut
err := c.call("GetBufferedTracepoints", GetBufferedTracepointsIn{}, &out)
return out.TracepointResults, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
var out GetBreakpointOut
err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out)
return &out.Breakpoint, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
var out GetBreakpointOut
err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out)
return &out.Breakpoint, err
}
// CreateBreakpoint will send a request to the RPC server to create a breakpoint.
// Please refer to the documentation for `Debugger.CreateBreakpoint` for a description of how
// the requested breakpoint parameters are interpreted and used:
// https://pkg.go.dev/github.com/go-delve/delve/service/debugger#Debugger.CreateBreakpoint
2015-06-21 03:47:44 +00:00
func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
var out CreateBreakpointOut
err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint, "", nil, false}, &out)
return &out.Breakpoint, err
}
// CreateBreakpointWithExpr is like CreateBreakpoint but will also set a
// location expression to be used to restore the breakpoint after it is
// disabled.
func (c *RPCClient) CreateBreakpointWithExpr(breakPoint *api.Breakpoint, locExpr string, substitutePathRules [][2]string, suspended bool) (*api.Breakpoint, error) {
var out CreateBreakpointOut
err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint, locExpr, substitutePathRules, suspended}, &out)
return &out.Breakpoint, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) CreateEBPFTracepoint(fnName string) error {
var out CreateEBPFTracepointOut
return c.call("CreateEBPFTracepoint", CreateEBPFTracepointIn{FunctionName: fnName}, &out)
}
func (c *RPCClient) CreateWatchpoint(scope api.EvalScope, expr string, wtype api.WatchType) (*api.Breakpoint, error) {
var out CreateWatchpointOut
err := c.call("CreateWatchpoint", CreateWatchpointIn{scope, expr, wtype}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) ListBreakpoints(all bool) ([]*api.Breakpoint, error) {
var out ListBreakpointsOut
err := c.call("ListBreakpoints", ListBreakpointsIn{all}, &out)
return out.Breakpoints, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
var out ClearBreakpointOut
err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out)
return out.Breakpoint, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
var out ClearBreakpointOut
err := c.call("ClearBreakpoint", ClearBreakpointIn{0, name}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) ToggleBreakpoint(id int) (*api.Breakpoint, error) {
var out ToggleBreakpointOut
err := c.call("ToggleBreakpoint", ToggleBreakpointIn{id, ""}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) ToggleBreakpointByName(name string) (*api.Breakpoint, error) {
var out ToggleBreakpointOut
err := c.call("ToggleBreakpoint", ToggleBreakpointIn{0, name}, &out)
return out.Breakpoint, err
}
func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
out := new(AmendBreakpointOut)
err := c.call("AmendBreakpoint", AmendBreakpointIn{*bp}, out)
return err
}
func (c *RPCClient) CancelNext() error {
var out CancelNextOut
return c.call("CancelNext", CancelNextIn{}, &out)
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
var out ListThreadsOut
err := c.call("ListThreads", ListThreadsIn{}, &out)
return out.Threads, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
var out GetThreadOut
err := c.call("GetThread", GetThreadIn{id}, &out)
return out.Thread, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) {
var out EvalOut
err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out)
return out.Variable, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
out := new(SetOut)
return c.call("Set", SetIn{scope, symbol, value}, out)
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) ListSources(filter string) ([]string, error) {
sources := new(ListSourcesOut)
err := c.call("ListSources", ListSourcesIn{filter}, sources)
return sources.Sources, err
2015-06-21 03:47:44 +00:00
}
pkg/terminal,service/debugger: Support to add a new suboption --follow-calls to trace subcommand (#3594) * rebasing on master to implement --followcalls * in progress changes to enable --followcalls * rebase to master: modified function to add children to funcs array * modify main traversal loop * added tests to check different scenarios * added tests to check different scenarios * added tests to check different scenarios * add test to check for overlapping regular expression * modified type of strings array as a return only * changed depth to a simple integer instead of a global map * avoid calling traverse on recursive calls * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * made auxillary changes for build to go through for new option follow-calls * Add support to print depth of the function calls as well * Added two sample output files for checking * Bypass morestack_noctxt in output for verification testing * Corrected newline error by adding newlines only if the line does not match morestack_noctxt * Added more tests * Cleanup * Updated documentation * fixed error message in fmt.Errorf * Fixed result of Errorf not used error * Addressing review comments to fix depth reporting and other issues * dont invoke stacktrace if tracefollowcalls is enabled, compute depth from main regex root symbol than main.main * Addressing a part of review comments * Added changes to allow deferred functions to be picked up for tracing * Fix issue to avoid printing stack for a simple trace option * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Adding panic-defer test case * Moved rest of the tests to integration2_test.go * addressing review comments: folding Functions and FunctionsDeep, reducing branches by using depth prefix, wrap using %w and other comments * Optimize traversal and parts of printing trace point function and modify trace output layout and adjust tests accordingly * Resolved error occurring due to staticcheck * Implemented traversal algorithm using breadth first search * Addressing review comments on the breadth first search implementation and other comments * Inline filterRuntimeFuncs and remove duplicate initialization
2024-06-12 19:35:48 +00:00
func (c *RPCClient) ListFunctions(filter string, TraceFollow int) ([]string, error) {
funcs := new(ListFunctionsOut)
pkg/terminal,service/debugger: Support to add a new suboption --follow-calls to trace subcommand (#3594) * rebasing on master to implement --followcalls * in progress changes to enable --followcalls * rebase to master: modified function to add children to funcs array * modify main traversal loop * added tests to check different scenarios * added tests to check different scenarios * added tests to check different scenarios * add test to check for overlapping regular expression * modified type of strings array as a return only * changed depth to a simple integer instead of a global map * avoid calling traverse on recursive calls * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * Added tests for various call graphs to test trace followfuncs * made auxillary changes for build to go through for new option follow-calls * Add support to print depth of the function calls as well * Added two sample output files for checking * Bypass morestack_noctxt in output for verification testing * Corrected newline error by adding newlines only if the line does not match morestack_noctxt * Added more tests * Cleanup * Updated documentation * fixed error message in fmt.Errorf * Fixed result of Errorf not used error * Addressing review comments to fix depth reporting and other issues * dont invoke stacktrace if tracefollowcalls is enabled, compute depth from main regex root symbol than main.main * Addressing a part of review comments * Added changes to allow deferred functions to be picked up for tracing * Fix issue to avoid printing stack for a simple trace option * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Moving most tests to integration2_test.go and keeping only one in dlv_test.go * Adding panic-defer test case * Moved rest of the tests to integration2_test.go * addressing review comments: folding Functions and FunctionsDeep, reducing branches by using depth prefix, wrap using %w and other comments * Optimize traversal and parts of printing trace point function and modify trace output layout and adjust tests accordingly * Resolved error occurring due to staticcheck * Implemented traversal algorithm using breadth first search * Addressing review comments on the breadth first search implementation and other comments * Inline filterRuntimeFuncs and remove duplicate initialization
2024-06-12 19:35:48 +00:00
err := c.call("ListFunctions", ListFunctionsIn{filter, TraceFollow}, funcs)
return funcs.Funcs, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
types := new(ListTypesOut)
err := c.call("ListTypes", ListTypesIn{filter}, types)
return types.Types, err
}
func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListPackageVarsOut
err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out)
return out.Variables, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListPackagesBuildInfo(filter string, includeFiles bool) ([]api.PackageBuildInfo, error) {
var out ListPackagesBuildInfoOut
err := c.call("ListPackagesBuildInfo", ListPackagesBuildInfoIn{Filter: filter, IncludeFiles: includeFiles}, &out)
return out.List, err
}
func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListLocalVarsOut
err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)
return out.Variables, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListThreadRegisters(threadID int, includeFp bool) (api.Registers, error) {
out := new(ListRegistersOut)
err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp, Scope: nil}, out)
return out.Regs, err
}
func (c *RPCClient) ListScopeRegisters(scope api.EvalScope, includeFp bool) (api.Registers, error) {
out := new(ListRegistersOut)
err := c.call("ListRegisters", ListRegistersIn{ThreadID: 0, IncludeFp: includeFp, Scope: &scope}, out)
return out.Regs, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListFunctionArgsOut
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out)
return out.Args, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListGoroutines(start, count int) ([]*api.Goroutine, int, error) {
var out ListGoroutinesOut
err := c.call("ListGoroutines", ListGoroutinesIn{start, count, nil, api.GoroutineGroupingOptions{}, nil}, &out)
return out.Goroutines, out.Nextg, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) ListGoroutinesWithFilter(start, count int, filters []api.ListGoroutinesFilter, group *api.GoroutineGroupingOptions, scope *api.EvalScope) ([]*api.Goroutine, []api.GoroutineGroup, int, bool, error) {
if group == nil {
group = &api.GoroutineGroupingOptions{}
}
var out ListGoroutinesOut
err := c.call("ListGoroutines", ListGoroutinesIn{start, count, filters, *group, scope}, &out)
return out.Goroutines, out.Groups, out.Nextg, out.TooManyGroups, err
}
func (c *RPCClient) Stacktrace(goroutineId int64, depth int, opts api.StacktraceOptions, cfg *api.LoadConfig) ([]api.Stackframe, error) {
var out StacktraceOut
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, false, opts, cfg}, &out)
return out.Locations, err
2015-06-21 03:47:44 +00:00
}
func (c *RPCClient) Ancestors(goroutineID int64, numAncestors int, depth int) ([]api.Ancestor, error) {
var out AncestorsOut
err := c.call("Ancestors", AncestorsIn{goroutineID, numAncestors, depth}, &out)
return out.Ancestors, err
}
2015-07-07 14:04:03 +00:00
func (c *RPCClient) AttachedToExistingProcess() bool {
out := new(AttachedToExistingProcessOut)
c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out)
return out.Answer
2015-07-07 14:04:03 +00:00
}
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, string, error) {
var out FindLocationOut
err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out)
return out.Locations, out.SubstituteLocExpr, err
}
// DisassembleRange disassembles code between startPC and endPC
2016-02-06 06:00:48 +00:00
func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var out DisassembleOut
err := c.call("Disassemble", DisassembleIn{scope, startPC, endPC, flavour}, &out)
return out.Disassemble, err
2016-02-06 06:00:48 +00:00
}
// DisassemblePC disassembles function containing pc
2016-02-06 06:00:48 +00:00
func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
var out DisassembleOut
err := c.call("Disassemble", DisassembleIn{scope, pc, 0, flavour}, &out)
return out.Disassemble, err
2016-02-06 06:00:48 +00:00
}
// Recorded returns true if the debugger target is a recording.
func (c *RPCClient) Recorded() bool {
out := new(RecordedOut)
c.call("Recorded", RecordedIn{}, out)
return out.Recorded
}
// TraceDirectory returns the path to the trace directory for a recording.
func (c *RPCClient) TraceDirectory() (string, error) {
var out RecordedOut
err := c.call("Recorded", RecordedIn{}, &out)
return out.TraceDirectory, err
}
// Checkpoint sets a checkpoint at the current position.
func (c *RPCClient) Checkpoint(where string) (checkpointID int, err error) {
var out CheckpointOut
err = c.call("Checkpoint", CheckpointIn{where}, &out)
return out.ID, err
}
// ListCheckpoints gets all checkpoints.
func (c *RPCClient) ListCheckpoints() ([]api.Checkpoint, error) {
var out ListCheckpointsOut
err := c.call("ListCheckpoints", ListCheckpointsIn{}, &out)
return out.Checkpoints, err
}
// ClearCheckpoint removes a checkpoint
func (c *RPCClient) ClearCheckpoint(id int) error {
var out ClearCheckpointOut
err := c.call("ClearCheckpoint", ClearCheckpointIn{id}, &out)
return err
}
func (c *RPCClient) SetReturnValuesLoadConfig(cfg *api.LoadConfig) {
c.retValLoadCfg = cfg
}
func (c *RPCClient) FunctionReturnLocations(fnName string) ([]uint64, error) {
var out FunctionReturnLocationsOut
err := c.call("FunctionReturnLocations", FunctionReturnLocationsIn{fnName}, &out)
return out.Addrs, err
}
func (c *RPCClient) IsMulticlient() bool {
var out IsMulticlientOut
c.call("IsMulticlient", IsMulticlientIn{}, &out)
return out.IsMulticlient
}
func (c *RPCClient) Disconnect(cont bool) error {
if cont {
out := new(CommandOut)
c.client.Go("RPCServer.Command", &api.DebuggerCommand{Name: api.Continue, ReturnInfoLoadConfig: c.retValLoadCfg}, &out, nil)
}
return c.client.Close()
}
func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
var out ListDynamicLibrariesOut
c.call("ListDynamicLibraries", ListDynamicLibrariesIn{}, &out)
return out.List, nil
}
func (c *RPCClient) ExamineMemory(address uint64, count int) ([]byte, bool, error) {
out := &ExaminedMemoryOut{}
err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
if err != nil {
return nil, false, err
}
return out.Mem, out.IsLittleEndian, nil
}
proc/gdbserial,debugger: allow clients to stop a recording (#1890) Allows Delve clients to stop a recording midway by sending a Command('halt') request. This is implemented by changing debugger.New to start recording the process on a separate goroutine while holding the processMutex locked. By locking the processMutex we ensure that almost all RPC requests will block until the recording is done, since we can not respond correctly to any of them. API calls that do not require manipulating or examining the target process, such as "IsMulticlient", "SetApiVersion" and "GetState(nowait=true)" will work while we are recording the process. Two other internal changes are made to the API: both GetState and Restart become asynchronous requests, like Command. Restart because this way it can be interrupted by a StopRecording request if the rerecord option is passed. GetState because clients need a call that will block until the recording is compelted and can also be interrupted with a StopRecording. Clients that are uninterested in allowing the user to stop a recording can ignore this change, since eventually they will make a request to Delve that will block until the recording is completed. Clients that wish to support this feature must: 1. call GetState(nowait=false) after connecting to Delve, before any call that would need to manipulate the target process 2. allow the user to send a StopRecording request during the initial GetState call 3. allow the user to send a StopRecording request during any subsequent Restart(rerecord=true) request (if supported). Implements #1747
2020-03-24 16:09:28 +00:00
func (c *RPCClient) StopRecording() error {
return c.call("StopRecording", StopRecordingIn{}, &StopRecordingOut{})
}
func (c *RPCClient) CoreDumpStart(dest string) (api.DumpState, error) {
out := &DumpStartOut{}
err := c.call("DumpStart", DumpStartIn{Destination: dest}, out)
return out.State, err
}
func (c *RPCClient) CoreDumpWait(msec int) api.DumpState {
out := &DumpWaitOut{}
_ = c.call("DumpWait", DumpWaitIn{Wait: msec}, out)
return out.State
}
func (c *RPCClient) CoreDumpCancel() error {
out := &DumpCancelOut{}
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
}
2023-04-27 20:39:33 +00:00
// FollowExecEnabled returns true if follow exec mode is enabled.
func (c *RPCClient) FollowExecEnabled() bool {
out := &FollowExecEnabledOut{}
_ = c.call("FollowExecEnabled", FollowExecEnabledIn{}, out)
return out.Enabled
}
func (c *RPCClient) SetDebugInfoDirectories(v []string) error {
return c.call("DebugInfoDirectories", DebugInfoDirectoriesIn{Set: true, List: v}, &DebugInfoDirectoriesOut{})
}
func (c *RPCClient) GetDebugInfoDirectories() ([]string, error) {
out := &DebugInfoDirectoriesOut{}
err := c.call("DebugInfoDirectories", DebugInfoDirectoriesIn{Set: false, List: nil}, out)
return out.List, err
}
type goListEntry struct {
Dir string
ImportPath string
Name string
Module *goListModule
}
type goListModule struct {
Path string
}
// MakeGuessSusbtitutePathIn returns a mapping from modules to client
// directories using "go list".
func MakeGuessSusbtitutePathIn() (*api.GuessSubstitutePathIn, error) {
cmd := exec.Command("go", "list", "--json", "all")
buf, err := cmd.Output()
if err != nil {
return nil, err
}
importPathOfMainPackage := ""
importPathOfMainPackageOk := true
mod2dir := make(map[string]string)
d := json.NewDecoder(bytes.NewReader(buf))
for d.More() {
var e goListEntry
err := d.Decode(&e)
if err != nil {
return nil, err
}
if e.Module == nil {
continue
}
if !strings.HasPrefix(e.ImportPath, e.Module.Path) {
continue
}
pkgWithoutModule := e.ImportPath[len(e.Module.Path):]
elems := 0
for _, c := range pkgWithoutModule {
if c == '/' {
elems++
}
}
dir := e.Dir
for i := 0; i < elems; i++ {
dir = filepath.Dir(dir)
}
if mod2dir[e.Module.Path] != "" && mod2dir[e.Module.Path] != dir {
return nil, fmt.Errorf("could not determine path for module %s (got %q and %q)", e.Module.Path, mod2dir[e.Module.Path], dir)
}
mod2dir[e.Module.Path] = dir
if e.Name == "main" {
if importPathOfMainPackage != "" && importPathOfMainPackage != e.ImportPath {
importPathOfMainPackageOk = false
}
importPathOfMainPackage = e.ImportPath
}
}
buf, err = exec.Command("go", "env", "GOROOT").Output()
if err != nil {
return nil, err
}
clientGoroot := strings.TrimSpace(string(buf))
if !importPathOfMainPackageOk {
// There were multiple main packages
importPathOfMainPackage = ""
}
return &api.GuessSubstitutePathIn{ClientGOROOT: clientGoroot, ImportPathOfMainPackage: importPathOfMainPackage, ClientModuleDirectories: mod2dir}, nil
}
func (c *RPCClient) GuessSubstitutePath() ([][2]string, error) {
in, err := MakeGuessSusbtitutePathIn()
if err != nil {
return nil, err
}
out := &GuessSubstitutePathOut{}
err = c.call("GuessSubstitutePath", GuessSubstitutePathIn{*in}, out)
return out.List, err
}
2015-06-21 03:47:44 +00:00
func (c *RPCClient) call(method string, args, reply interface{}) error {
return c.client.Call("RPCServer."+method, args, reply)
}
func (c *RPCClient) CallAPI(method string, args, reply interface{}) error {
return c.call(method, args, reply)
}