gitlab.com/Raven-IO/raven-delve@v1.22.4/service/rpc1/client.go (about)

     1  package rpc1
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"net/rpc"
     8  	"net/rpc/jsonrpc"
     9  
    10  	"sync"
    11  
    12  	"gitlab.com/Raven-IO/raven-delve/service/api"
    13  )
    14  
    15  // RPCClient is a RPC service.Client.
    16  type RPCClient struct {
    17  	addr    string
    18  	client  *rpc.Client
    19  	haltMu  sync.Mutex
    20  	haltReq bool
    21  }
    22  
    23  var errAPIUnsupported = errors.New("unsupported")
    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 &RPCClient{
    32  		addr:   addr,
    33  		client: client,
    34  	}
    35  }
    36  
    37  func (c *RPCClient) ProcessPid() int {
    38  	var pid int
    39  	c.call("ProcessPid", nil, &pid)
    40  	return pid
    41  }
    42  
    43  func (c *RPCClient) Detach(kill bool) error {
    44  	return c.call("Detach", kill, nil)
    45  }
    46  
    47  func (c *RPCClient) Restart() error {
    48  	return c.call("Restart", nil, nil)
    49  }
    50  
    51  func (c *RPCClient) GetState() (*api.DebuggerState, error) {
    52  	state := new(api.DebuggerState)
    53  	err := c.call("State", nil, state)
    54  	return state, err
    55  }
    56  
    57  func (c *RPCClient) Continue() <-chan *api.DebuggerState {
    58  	ch := make(chan *api.DebuggerState)
    59  	c.haltMu.Lock()
    60  	c.haltReq = false
    61  	c.haltMu.Unlock()
    62  	go func() {
    63  		for {
    64  			c.haltMu.Lock()
    65  			if c.haltReq {
    66  				c.haltMu.Unlock()
    67  				close(ch)
    68  				return
    69  			}
    70  			c.haltMu.Unlock()
    71  			state := new(api.DebuggerState)
    72  			err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state)
    73  			if err != nil {
    74  				state.Err = err
    75  			}
    76  			if state.Exited {
    77  				// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
    78  				//lint:ignore ST1005 backwards compatibility
    79  				state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
    80  			}
    81  			ch <- state
    82  			if err != nil || state.Exited {
    83  				close(ch)
    84  				return
    85  			}
    86  
    87  			isbreakpoint := false
    88  			istracepoint := true
    89  			for i := range state.Threads {
    90  				if state.Threads[i].Breakpoint != nil {
    91  					isbreakpoint = true
    92  					istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint
    93  				}
    94  			}
    95  
    96  			if !isbreakpoint || !istracepoint {
    97  				close(ch)
    98  				return
    99  			}
   100  		}
   101  	}()
   102  	return ch
   103  }
   104  
   105  func (c *RPCClient) Next() (*api.DebuggerState, error) {
   106  	state := new(api.DebuggerState)
   107  	err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state)
   108  	return state, err
   109  }
   110  
   111  func (c *RPCClient) Step() (*api.DebuggerState, error) {
   112  	state := new(api.DebuggerState)
   113  	err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state)
   114  	return state, err
   115  }
   116  
   117  func (c *RPCClient) Call(expr string) (*api.DebuggerState, error) {
   118  	state := new(api.DebuggerState)
   119  	err := c.call("Command", &api.DebuggerCommand{Name: api.Call, Expr: expr}, state)
   120  	return state, err
   121  }
   122  
   123  func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
   124  	state := new(api.DebuggerState)
   125  	err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state)
   126  	return state, err
   127  }
   128  
   129  func (c *RPCClient) ReverseStepInstruction() (*api.DebuggerState, error) {
   130  	state := new(api.DebuggerState)
   131  	err := c.call("Command", &api.DebuggerCommand{Name: api.ReverseStepInstruction}, state)
   132  	return state, err
   133  }
   134  
   135  func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
   136  	state := new(api.DebuggerState)
   137  	cmd := &api.DebuggerCommand{
   138  		Name:     api.SwitchThread,
   139  		ThreadID: threadID,
   140  	}
   141  	err := c.call("Command", cmd, state)
   142  	return state, err
   143  }
   144  
   145  func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
   146  	state := new(api.DebuggerState)
   147  	cmd := &api.DebuggerCommand{
   148  		Name:        api.SwitchGoroutine,
   149  		GoroutineID: int64(goroutineID),
   150  	}
   151  	err := c.call("Command", cmd, state)
   152  	return state, err
   153  }
   154  
   155  func (c *RPCClient) Halt() (*api.DebuggerState, error) {
   156  	state := new(api.DebuggerState)
   157  	c.haltMu.Lock()
   158  	c.haltReq = true
   159  	c.haltMu.Unlock()
   160  	err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state)
   161  	return state, err
   162  }
   163  
   164  func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
   165  	breakpoint := new(api.Breakpoint)
   166  	err := c.call("GetBreakpoint", id, breakpoint)
   167  	return breakpoint, err
   168  }
   169  
   170  func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
   171  	breakpoint := new(api.Breakpoint)
   172  	err := c.call("GetBreakpointByName", name, breakpoint)
   173  	return breakpoint, err
   174  }
   175  
   176  func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
   177  	newBreakpoint := new(api.Breakpoint)
   178  	err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint)
   179  	return newBreakpoint, err
   180  }
   181  
   182  func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) {
   183  	var breakpoints []*api.Breakpoint
   184  	err := c.call("ListBreakpoints", nil, &breakpoints)
   185  	return breakpoints, err
   186  }
   187  
   188  func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
   189  	bp := new(api.Breakpoint)
   190  	err := c.call("ClearBreakpoint", id, bp)
   191  	return bp, err
   192  }
   193  
   194  func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
   195  	bp := new(api.Breakpoint)
   196  	err := c.call("ClearBreakpointByName", name, bp)
   197  	return bp, err
   198  }
   199  
   200  func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
   201  	err := c.call("AmendBreakpoint", bp, nil)
   202  	return err
   203  }
   204  
   205  func (c *RPCClient) CancelNext() error {
   206  	return errAPIUnsupported
   207  }
   208  
   209  func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
   210  	var threads []*api.Thread
   211  	err := c.call("ListThreads", nil, &threads)
   212  	return threads, err
   213  }
   214  
   215  func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
   216  	thread := new(api.Thread)
   217  	err := c.call("GetThread", id, &thread)
   218  	return thread, err
   219  }
   220  
   221  func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) {
   222  	v := new(api.Variable)
   223  	err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v)
   224  	return v, err
   225  }
   226  
   227  func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
   228  	var unused int
   229  	return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused)
   230  }
   231  
   232  func (c *RPCClient) ListSources(filter string) ([]string, error) {
   233  	var sources []string
   234  	err := c.call("ListSources", filter, &sources)
   235  	return sources, err
   236  }
   237  
   238  func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
   239  	var funcs []string
   240  	err := c.call("ListFunctions", filter, &funcs)
   241  	return funcs, err
   242  }
   243  
   244  func (c *RPCClient) ListTypes(filter string) ([]string, error) {
   245  	var types []string
   246  	err := c.call("ListTypes", filter, &types)
   247  	return types, err
   248  }
   249  
   250  func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
   251  	var vars []api.Variable
   252  	err := c.call("ListPackageVars", filter, &vars)
   253  	return vars, err
   254  }
   255  
   256  func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) {
   257  	var vars []api.Variable
   258  	err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars)
   259  	return vars, err
   260  }
   261  
   262  func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) {
   263  	var vars []api.Variable
   264  	err := c.call("ListLocalVars", scope, &vars)
   265  	return vars, err
   266  }
   267  
   268  func (c *RPCClient) ListRegisters() (string, error) {
   269  	var regs string
   270  	err := c.call("ListRegisters", nil, &regs)
   271  	return regs, err
   272  }
   273  
   274  func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) {
   275  	var vars []api.Variable
   276  	err := c.call("ListFunctionArgs", scope, &vars)
   277  	return vars, err
   278  }
   279  
   280  func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
   281  	var goroutines []*api.Goroutine
   282  	err := c.call("ListGoroutines", nil, &goroutines)
   283  	return goroutines, err
   284  }
   285  
   286  func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) {
   287  	var locations []api.Stackframe
   288  	err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations)
   289  	return locations, err
   290  }
   291  
   292  func (c *RPCClient) AttachedToExistingProcess() bool {
   293  	var answer bool
   294  	c.call("AttachedToExistingProcess", nil, &answer)
   295  	return answer
   296  }
   297  
   298  func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) {
   299  	var answer []api.Location
   300  	err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer)
   301  	return answer, err
   302  }
   303  
   304  // DisassembleRange disassembles code between startPC and endPC
   305  func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
   306  	var r api.AsmInstructions
   307  	err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r)
   308  	return r, err
   309  }
   310  
   311  // DisassemblePC disassembles function containing pc
   312  func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
   313  	var r api.AsmInstructions
   314  	err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r)
   315  	return r, err
   316  }
   317  
   318  func (c *RPCClient) call(method string, args, reply interface{}) error {
   319  	return c.client.Call("RPCServer."+method, args, reply)
   320  }