package rpc2 import ( "fmt" "log" "net/rpc" "net/rpc/jsonrpc" "time" "github.com/derekparker/delve/service" "github.com/derekparker/delve/service/api" ) // Client is a RPC service.Client. type RPCClient struct { addr string processPid int client *rpc.Client } // 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) } c := &RPCClient{addr: addr, client: client} c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{}) return c } func (c *RPCClient) ProcessPid() int { out := new(ProcessPidOut) c.call("ProcessPid", ProcessPidIn{}, out) return out.Pid } func (c *RPCClient) LastModified() time.Time { out := new(LastModifiedOut) c.call("LastModified", LastModifiedIn{}, out) return out.Time } func (c *RPCClient) Detach(kill bool) error { out := new(DetachOut) return c.call("Detach", DetachIn{kill}, out) } func (c *RPCClient) Restart() ([]api.DiscardedBreakpoint, error) { out := new(RestartOut) err := c.call("Restart", RestartIn{}, out) return out.DiscardedBreakpoints, err } func (c *RPCClient) GetState() (*api.DebuggerState, error) { var out StateOut err := c.call("State", StateIn{}, &out) return out.State, err } func (c *RPCClient) Continue() <-chan *api.DebuggerState { ch := make(chan *api.DebuggerState) go func() { for { out := new(CommandOut) err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, &out) state := out.State if err != nil { state.Err = err } if state.Exited { // Error types apparantly cannot be marshalled by Go correctly. Must reset error here. 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 } } if !isbreakpoint || !istracepoint { close(ch) return } } }() return ch } func (c *RPCClient) Next() (*api.DebuggerState, error) { var out CommandOut err := c.call("Command", api.DebuggerCommand{Name: api.Next}, &out) return &out.State, err } func (c *RPCClient) Step() (*api.DebuggerState, error) { var out CommandOut err := c.call("Command", api.DebuggerCommand{Name: api.Step}, &out) return &out.State, err } func (c *RPCClient) StepOut() (*api.DebuggerState, error) { var out CommandOut err := c.call("Command", &api.DebuggerCommand{Name: api.StepOut}, &out) return &out.State, err } func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) { var out CommandOut err := c.call("Command", api.DebuggerCommand{Name: api.StepInstruction}, &out) return &out.State, err } func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) { var out CommandOut cmd := api.DebuggerCommand{ Name: api.SwitchThread, ThreadID: threadID, } err := c.call("Command", cmd, &out) return &out.State, err } func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) { var out CommandOut cmd := api.DebuggerCommand{ Name: api.SwitchGoroutine, GoroutineID: goroutineID, } err := c.call("Command", cmd, &out) return &out.State, err } func (c *RPCClient) Halt() (*api.DebuggerState, error) { var out CommandOut err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out) return &out.State, err } func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) { var out GetBreakpointOut err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out) return &out.Breakpoint, err } func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) { var out GetBreakpointOut err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out) return &out.Breakpoint, err } func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) { var out CreateBreakpointOut err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint}, &out) return &out.Breakpoint, err } func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) { var out ListBreakpointsOut err := c.call("ListBreakpoints", ListBreakpointsIn{}, &out) return out.Breakpoints, err } func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) { var out ClearBreakpointOut err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out) return out.Breakpoint, err } 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) 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) } func (c *RPCClient) ListThreads() ([]*api.Thread, error) { var out ListThreadsOut err := c.call("ListThreads", ListThreadsIn{}, &out) return out.Threads, err } func (c *RPCClient) GetThread(id int) (*api.Thread, error) { var out GetThreadOut err := c.call("GetThread", GetThreadIn{id}, &out) return out.Thread, err } 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 } func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { out := new(SetOut) return c.call("Set", SetIn{scope, symbol, value}, out) } func (c *RPCClient) ListSources(filter string) ([]string, error) { sources := new(ListSourcesOut) err := c.call("ListSources", ListSourcesIn{filter}, sources) return sources.Sources, err } func (c *RPCClient) ListFunctions(filter string) ([]string, error) { funcs := new(ListFunctionsOut) err := c.call("ListFunctions", ListFunctionsIn{filter}, funcs) return funcs.Funcs, err } 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 } 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 } func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) { out := new(ListRegistersOut) err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp}, out) return out.Regs, err } 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 } func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) { var out ListGoroutinesOut err := c.call("ListGoroutines", ListGoroutinesIn{}, &out) return out.Goroutines, err } func (c *RPCClient) Stacktrace(goroutineId, depth int, cfg *api.LoadConfig) ([]api.Stackframe, error) { var out StacktraceOut err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, cfg}, &out) return out.Locations, err } func (c *RPCClient) AttachedToExistingProcess() bool { out := new(AttachedToExistingProcessOut) c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out) return out.Answer } func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) { var out FindLocationOut err := c.call("FindLocation", FindLocationIn{scope, loc}, &out) return out.Locations, err } // Disassemble code between startPC and endPC 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 } // Disassemble function containing pc 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 } func (c *RPCClient) url(path string) string { return fmt.Sprintf("http://%s%s", c.addr, path) } func (c *RPCClient) call(method string, args, reply interface{}) error { return c.client.Call("RPCServer."+method, args, reply) }