github.com/neilgarb/delve@v1.9.2-nobreaks/service/rpc2/client.go (about)

     1  package rpc2
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"net"
     7  	"net/rpc"
     8  	"net/rpc/jsonrpc"
     9  	"time"
    10  
    11  	"github.com/go-delve/delve/service"
    12  	"github.com/go-delve/delve/service/api"
    13  )
    14  
    15  // RPCClient is a RPC service.Client.
    16  type RPCClient struct {
    17  	client *rpc.Client
    18  
    19  	retValLoadCfg *api.LoadConfig
    20  }
    21  
    22  // Ensure the implementation satisfies the interface.
    23  var _ service.Client = &RPCClient{}
    24  
    25  // NewClient creates a new RPCClient.
    26  func NewClient(addr string) *RPCClient {
    27  	client, err := jsonrpc.Dial("tcp", addr)
    28  	if err != nil {
    29  		log.Fatal("dialing:", err)
    30  	}
    31  	return newFromRPCClient(client)
    32  }
    33  
    34  func newFromRPCClient(client *rpc.Client) *RPCClient {
    35  	c := &RPCClient{client: client}
    36  	c.call("SetApiVersion", api.SetAPIVersionIn{APIVersion: 2}, &api.SetAPIVersionOut{})
    37  	return c
    38  }
    39  
    40  // NewClientFromConn creates a new RPCClient from the given connection.
    41  func NewClientFromConn(conn net.Conn) *RPCClient {
    42  	return newFromRPCClient(jsonrpc.NewClient(conn))
    43  }
    44  
    45  func (c *RPCClient) ProcessPid() int {
    46  	out := new(ProcessPidOut)
    47  	c.call("ProcessPid", ProcessPidIn{}, out)
    48  	return out.Pid
    49  }
    50  
    51  func (c *RPCClient) BuildID() string {
    52  	out := new(BuildIDOut)
    53  	c.call("BuildID", BuildIDIn{}, out)
    54  	return out.BuildID
    55  }
    56  
    57  func (c *RPCClient) LastModified() time.Time {
    58  	out := new(LastModifiedOut)
    59  	c.call("LastModified", LastModifiedIn{}, out)
    60  	return out.Time
    61  }
    62  
    63  func (c *RPCClient) Detach(kill bool) error {
    64  	defer c.client.Close()
    65  	out := new(DetachOut)
    66  	return c.call("Detach", DetachIn{kill}, out)
    67  }
    68  
    69  func (c *RPCClient) Restart(rebuild bool) ([]api.DiscardedBreakpoint, error) {
    70  	out := new(RestartOut)
    71  	err := c.call("Restart", RestartIn{"", false, nil, false, rebuild, [3]string{}}, out)
    72  	return out.DiscardedBreakpoints, err
    73  }
    74  
    75  func (c *RPCClient) RestartFrom(rerecord bool, pos string, resetArgs bool, newArgs []string, newRedirects [3]string, rebuild bool) ([]api.DiscardedBreakpoint, error) {
    76  	out := new(RestartOut)
    77  	err := c.call("Restart", RestartIn{pos, resetArgs, newArgs, rerecord, rebuild, newRedirects}, out)
    78  	return out.DiscardedBreakpoints, err
    79  }
    80  
    81  func (c *RPCClient) GetState() (*api.DebuggerState, error) {
    82  	var out StateOut
    83  	err := c.call("State", StateIn{NonBlocking: false}, &out)
    84  	return out.State, err
    85  }
    86  
    87  func (c *RPCClient) GetStateNonBlocking() (*api.DebuggerState, error) {
    88  	var out StateOut
    89  	err := c.call("State", StateIn{NonBlocking: true}, &out)
    90  	return out.State, err
    91  }
    92  
    93  func (c *RPCClient) Continue() <-chan *api.DebuggerState {
    94  	return c.continueDir(api.Continue)
    95  }
    96  
    97  func (c *RPCClient) Rewind() <-chan *api.DebuggerState {
    98  	return c.continueDir(api.Rewind)
    99  }
   100  
   101  func (c *RPCClient) DirectionCongruentContinue() <-chan *api.DebuggerState {
   102  	return c.continueDir(api.DirectionCongruentContinue)
   103  }
   104  
   105  func (c *RPCClient) continueDir(cmd string) <-chan *api.DebuggerState {
   106  	ch := make(chan *api.DebuggerState)
   107  	go func() {
   108  		for {
   109  			out := new(CommandOut)
   110  			err := c.call("Command", &api.DebuggerCommand{Name: cmd, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   111  			state := out.State
   112  			if err != nil {
   113  				state.Err = err
   114  			}
   115  			if state.Exited {
   116  				// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
   117  				//lint:ignore ST1005 backwards compatibility
   118  				state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
   119  			}
   120  			ch <- &state
   121  			if err != nil || state.Exited {
   122  				close(ch)
   123  				return
   124  			}
   125  
   126  			isbreakpoint := false
   127  			istracepoint := true
   128  			for i := range state.Threads {
   129  				if state.Threads[i].Breakpoint != nil {
   130  					isbreakpoint = true
   131  					istracepoint = istracepoint && (state.Threads[i].Breakpoint.Tracepoint || state.Threads[i].Breakpoint.TraceReturn)
   132  				}
   133  			}
   134  
   135  			if !isbreakpoint || !istracepoint {
   136  				close(ch)
   137  				return
   138  			}
   139  		}
   140  	}()
   141  	return ch
   142  }
   143  
   144  func (c *RPCClient) Next() (*api.DebuggerState, error) {
   145  	var out CommandOut
   146  	err := c.call("Command", api.DebuggerCommand{Name: api.Next, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   147  	return &out.State, err
   148  }
   149  
   150  func (c *RPCClient) ReverseNext() (*api.DebuggerState, error) {
   151  	var out CommandOut
   152  	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseNext, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   153  	return &out.State, err
   154  }
   155  
   156  func (c *RPCClient) Step() (*api.DebuggerState, error) {
   157  	var out CommandOut
   158  	err := c.call("Command", api.DebuggerCommand{Name: api.Step, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   159  	return &out.State, err
   160  }
   161  
   162  func (c *RPCClient) ReverseStep() (*api.DebuggerState, error) {
   163  	var out CommandOut
   164  	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStep, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   165  	return &out.State, err
   166  }
   167  
   168  func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
   169  	var out CommandOut
   170  	err := c.call("Command", api.DebuggerCommand{Name: api.StepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   171  	return &out.State, err
   172  }
   173  
   174  func (c *RPCClient) ReverseStepOut() (*api.DebuggerState, error) {
   175  	var out CommandOut
   176  	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepOut, ReturnInfoLoadConfig: c.retValLoadCfg}, &out)
   177  	return &out.State, err
   178  }
   179  
   180  func (c *RPCClient) Call(goroutineID int, expr string, unsafe bool) (*api.DebuggerState, error) {
   181  	var out CommandOut
   182  	err := c.call("Command", api.DebuggerCommand{Name: api.Call, ReturnInfoLoadConfig: c.retValLoadCfg, Expr: expr, UnsafeCall: unsafe, GoroutineID: goroutineID}, &out)
   183  	return &out.State, err
   184  }
   185  
   186  func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
   187  	var out CommandOut
   188  	err := c.call("Command", api.DebuggerCommand{Name: api.StepInstruction}, &out)
   189  	return &out.State, err
   190  }
   191  
   192  func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) {
   193  	var out CommandOut
   194  	err := c.call("Command", api.DebuggerCommand{Name: api.ReverseStepInstruction}, &out)
   195  	return &out.State, err
   196  }
   197  
   198  func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
   199  	var out CommandOut
   200  	cmd := api.DebuggerCommand{
   201  		Name:     api.SwitchThread,
   202  		ThreadID: threadID,
   203  	}
   204  	err := c.call("Command", cmd, &out)
   205  	return &out.State, err
   206  }
   207  
   208  func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
   209  	var out CommandOut
   210  	cmd := api.DebuggerCommand{
   211  		Name:        api.SwitchGoroutine,
   212  		GoroutineID: goroutineID,
   213  	}
   214  	err := c.call("Command", cmd, &out)
   215  	return &out.State, err
   216  }
   217  
   218  func (c *RPCClient) Halt() (*api.DebuggerState, error) {
   219  	var out CommandOut
   220  	err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out)
   221  	return &out.State, err
   222  }
   223  
   224  func (c *RPCClient) GetBufferedTracepoints() ([]api.TracepointResult, error) {
   225  	var out GetBufferedTracepointsOut
   226  	err := c.call("GetBufferedTracepoints", GetBufferedTracepointsIn{}, &out)
   227  	return out.TracepointResults, err
   228  }
   229  
   230  func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
   231  	var out GetBreakpointOut
   232  	err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out)
   233  	return &out.Breakpoint, err
   234  }
   235  
   236  func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
   237  	var out GetBreakpointOut
   238  	err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out)
   239  	return &out.Breakpoint, err
   240  }
   241  
   242  // CreateBreakpoint will send a request to the RPC server to create a breakpoint.
   243  // Please refer to the documentation for `Debugger.CreateBreakpoint` for a description of how
   244  // the requested breakpoint parameters are interpreted and used:
   245  // https://pkg.go.dev/github.com/go-delve/delve/service/debugger#Debugger.CreateBreakpoint
   246  func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
   247  	var out CreateBreakpointOut
   248  	err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint}, &out)
   249  	return &out.Breakpoint, err
   250  }
   251  
   252  func (c *RPCClient) CreateEBPFTracepoint(fnName string) error {
   253  	var out CreateEBPFTracepointOut
   254  	return c.call("CreateEBPFTracepoint", CreateEBPFTracepointIn{FunctionName: fnName}, &out)
   255  }
   256  
   257  func (c *RPCClient) CreateWatchpoint(scope api.EvalScope, expr string, wtype api.WatchType) (*api.Breakpoint, error) {
   258  	var out CreateWatchpointOut
   259  	err := c.call("CreateWatchpoint", CreateWatchpointIn{scope, expr, wtype}, &out)
   260  	return out.Breakpoint, err
   261  }
   262  
   263  func (c *RPCClient) ListBreakpoints(all bool) ([]*api.Breakpoint, error) {
   264  	var out ListBreakpointsOut
   265  	err := c.call("ListBreakpoints", ListBreakpointsIn{all}, &out)
   266  	return out.Breakpoints, err
   267  }
   268  
   269  func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
   270  	var out ClearBreakpointOut
   271  	err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out)
   272  	return out.Breakpoint, err
   273  }
   274  
   275  func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
   276  	var out ClearBreakpointOut
   277  	err := c.call("ClearBreakpoint", ClearBreakpointIn{0, name}, &out)
   278  	return out.Breakpoint, err
   279  }
   280  
   281  func (c *RPCClient) ToggleBreakpoint(id int) (*api.Breakpoint, error) {
   282  	var out ToggleBreakpointOut
   283  	err := c.call("ToggleBreakpoint", ToggleBreakpointIn{id, ""}, &out)
   284  	return out.Breakpoint, err
   285  }
   286  
   287  func (c *RPCClient) ToggleBreakpointByName(name string) (*api.Breakpoint, error) {
   288  	var out ToggleBreakpointOut
   289  	err := c.call("ToggleBreakpoint", ToggleBreakpointIn{0, name}, &out)
   290  	return out.Breakpoint, err
   291  }
   292  
   293  func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
   294  	out := new(AmendBreakpointOut)
   295  	err := c.call("AmendBreakpoint", AmendBreakpointIn{*bp}, out)
   296  	return err
   297  }
   298  
   299  func (c *RPCClient) CancelNext() error {
   300  	var out CancelNextOut
   301  	return c.call("CancelNext", CancelNextIn{}, &out)
   302  }
   303  
   304  func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
   305  	var out ListThreadsOut
   306  	err := c.call("ListThreads", ListThreadsIn{}, &out)
   307  	return out.Threads, err
   308  }
   309  
   310  func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
   311  	var out GetThreadOut
   312  	err := c.call("GetThread", GetThreadIn{id}, &out)
   313  	return out.Thread, err
   314  }
   315  
   316  func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) {
   317  	var out EvalOut
   318  	err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out)
   319  	return out.Variable, err
   320  }
   321  
   322  func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
   323  	out := new(SetOut)
   324  	return c.call("Set", SetIn{scope, symbol, value}, out)
   325  }
   326  
   327  func (c *RPCClient) ListSources(filter string) ([]string, error) {
   328  	sources := new(ListSourcesOut)
   329  	err := c.call("ListSources", ListSourcesIn{filter}, sources)
   330  	return sources.Sources, err
   331  }
   332  
   333  func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
   334  	funcs := new(ListFunctionsOut)
   335  	err := c.call("ListFunctions", ListFunctionsIn{filter}, funcs)
   336  	return funcs.Funcs, err
   337  }
   338  
   339  func (c *RPCClient) ListTypes(filter string) ([]string, error) {
   340  	types := new(ListTypesOut)
   341  	err := c.call("ListTypes", ListTypesIn{filter}, types)
   342  	return types.Types, err
   343  }
   344  
   345  func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) {
   346  	var out ListPackageVarsOut
   347  	err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out)
   348  	return out.Variables, err
   349  }
   350  
   351  func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
   352  	var out ListLocalVarsOut
   353  	err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)
   354  	return out.Variables, err
   355  }
   356  
   357  func (c *RPCClient) ListThreadRegisters(threadID int, includeFp bool) (api.Registers, error) {
   358  	out := new(ListRegistersOut)
   359  	err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp, Scope: nil}, out)
   360  	return out.Regs, err
   361  }
   362  
   363  func (c *RPCClient) ListScopeRegisters(scope api.EvalScope, includeFp bool) (api.Registers, error) {
   364  	out := new(ListRegistersOut)
   365  	err := c.call("ListRegisters", ListRegistersIn{ThreadID: 0, IncludeFp: includeFp, Scope: &scope}, out)
   366  	return out.Regs, err
   367  }
   368  
   369  func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
   370  	var out ListFunctionArgsOut
   371  	err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out)
   372  	return out.Args, err
   373  }
   374  
   375  func (c *RPCClient) ListGoroutines(start, count int) ([]*api.Goroutine, int, error) {
   376  	var out ListGoroutinesOut
   377  	err := c.call("ListGoroutines", ListGoroutinesIn{start, count, nil, api.GoroutineGroupingOptions{}}, &out)
   378  	return out.Goroutines, out.Nextg, err
   379  }
   380  
   381  func (c *RPCClient) ListGoroutinesWithFilter(start, count int, filters []api.ListGoroutinesFilter, group *api.GoroutineGroupingOptions) ([]*api.Goroutine, []api.GoroutineGroup, int, bool, error) {
   382  	if group == nil {
   383  		group = &api.GoroutineGroupingOptions{}
   384  	}
   385  	var out ListGoroutinesOut
   386  	err := c.call("ListGoroutines", ListGoroutinesIn{start, count, filters, *group}, &out)
   387  	return out.Goroutines, out.Groups, out.Nextg, out.TooManyGroups, err
   388  }
   389  
   390  func (c *RPCClient) Stacktrace(goroutineId, depth int, opts api.StacktraceOptions, cfg *api.LoadConfig) ([]api.Stackframe, error) {
   391  	var out StacktraceOut
   392  	err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, false, opts, cfg}, &out)
   393  	return out.Locations, err
   394  }
   395  
   396  func (c *RPCClient) Ancestors(goroutineID int, numAncestors int, depth int) ([]api.Ancestor, error) {
   397  	var out AncestorsOut
   398  	err := c.call("Ancestors", AncestorsIn{goroutineID, numAncestors, depth}, &out)
   399  	return out.Ancestors, err
   400  }
   401  
   402  func (c *RPCClient) AttachedToExistingProcess() bool {
   403  	out := new(AttachedToExistingProcessOut)
   404  	c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out)
   405  	return out.Answer
   406  }
   407  
   408  func (c *RPCClient) FindLocation(scope api.EvalScope, loc string, findInstructions bool, substitutePathRules [][2]string) ([]api.Location, error) {
   409  	var out FindLocationOut
   410  	err := c.call("FindLocation", FindLocationIn{scope, loc, !findInstructions, substitutePathRules}, &out)
   411  	return out.Locations, err
   412  }
   413  
   414  // DisassembleRange disassembles code between startPC and endPC
   415  func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
   416  	var out DisassembleOut
   417  	err := c.call("Disassemble", DisassembleIn{scope, startPC, endPC, flavour}, &out)
   418  	return out.Disassemble, err
   419  }
   420  
   421  // DisassemblePC disassembles function containing pc
   422  func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
   423  	var out DisassembleOut
   424  	err := c.call("Disassemble", DisassembleIn{scope, pc, 0, flavour}, &out)
   425  	return out.Disassemble, err
   426  }
   427  
   428  // Recorded returns true if the debugger target is a recording.
   429  func (c *RPCClient) Recorded() bool {
   430  	out := new(RecordedOut)
   431  	c.call("Recorded", RecordedIn{}, out)
   432  	return out.Recorded
   433  }
   434  
   435  // TraceDirectory returns the path to the trace directory for a recording.
   436  func (c *RPCClient) TraceDirectory() (string, error) {
   437  	var out RecordedOut
   438  	err := c.call("Recorded", RecordedIn{}, &out)
   439  	return out.TraceDirectory, err
   440  }
   441  
   442  // Checkpoint sets a checkpoint at the current position.
   443  func (c *RPCClient) Checkpoint(where string) (checkpointID int, err error) {
   444  	var out CheckpointOut
   445  	err = c.call("Checkpoint", CheckpointIn{where}, &out)
   446  	return out.ID, err
   447  }
   448  
   449  // ListCheckpoints gets all checkpoints.
   450  func (c *RPCClient) ListCheckpoints() ([]api.Checkpoint, error) {
   451  	var out ListCheckpointsOut
   452  	err := c.call("ListCheckpoints", ListCheckpointsIn{}, &out)
   453  	return out.Checkpoints, err
   454  }
   455  
   456  // ClearCheckpoint removes a checkpoint
   457  func (c *RPCClient) ClearCheckpoint(id int) error {
   458  	var out ClearCheckpointOut
   459  	err := c.call("ClearCheckpoint", ClearCheckpointIn{id}, &out)
   460  	return err
   461  }
   462  
   463  func (c *RPCClient) SetReturnValuesLoadConfig(cfg *api.LoadConfig) {
   464  	c.retValLoadCfg = cfg
   465  }
   466  
   467  func (c *RPCClient) FunctionReturnLocations(fnName string) ([]uint64, error) {
   468  	var out FunctionReturnLocationsOut
   469  	err := c.call("FunctionReturnLocations", FunctionReturnLocationsIn{fnName}, &out)
   470  	return out.Addrs, err
   471  }
   472  
   473  func (c *RPCClient) IsMulticlient() bool {
   474  	var out IsMulticlientOut
   475  	c.call("IsMulticlient", IsMulticlientIn{}, &out)
   476  	return out.IsMulticlient
   477  }
   478  
   479  func (c *RPCClient) Disconnect(cont bool) error {
   480  	if cont {
   481  		out := new(CommandOut)
   482  		c.client.Go("RPCServer.Command", &api.DebuggerCommand{Name: api.Continue, ReturnInfoLoadConfig: c.retValLoadCfg}, &out, nil)
   483  	}
   484  	return c.client.Close()
   485  }
   486  
   487  func (c *RPCClient) ListDynamicLibraries() ([]api.Image, error) {
   488  	var out ListDynamicLibrariesOut
   489  	c.call("ListDynamicLibraries", ListDynamicLibrariesIn{}, &out)
   490  	return out.List, nil
   491  }
   492  
   493  func (c *RPCClient) ExamineMemory(address uint64, count int) ([]byte, bool, error) {
   494  	out := &ExaminedMemoryOut{}
   495  
   496  	err := c.call("ExamineMemory", ExamineMemoryIn{Length: count, Address: address}, out)
   497  	if err != nil {
   498  		return nil, false, err
   499  	}
   500  	return out.Mem, out.IsLittleEndian, nil
   501  }
   502  
   503  func (c *RPCClient) StopRecording() error {
   504  	return c.call("StopRecording", StopRecordingIn{}, &StopRecordingOut{})
   505  }
   506  
   507  func (c *RPCClient) CoreDumpStart(dest string) (api.DumpState, error) {
   508  	out := &DumpStartOut{}
   509  	err := c.call("DumpStart", DumpStartIn{Destination: dest}, out)
   510  	return out.State, err
   511  }
   512  
   513  func (c *RPCClient) CoreDumpWait(msec int) api.DumpState {
   514  	out := &DumpWaitOut{}
   515  	_ = c.call("DumpWait", DumpWaitIn{Wait: msec}, out)
   516  	return out.State
   517  }
   518  
   519  func (c *RPCClient) CoreDumpCancel() error {
   520  	out := &DumpCancelOut{}
   521  	return c.call("DumpCancel", DumpCancelIn{}, out)
   522  }
   523  
   524  func (c *RPCClient) call(method string, args, reply interface{}) error {
   525  	return c.client.Call("RPCServer."+method, args, reply)
   526  }
   527  
   528  func (c *RPCClient) CallAPI(method string, args, reply interface{}) error {
   529  	return c.call(method, args, reply)
   530  }