mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	 c4e01da5ca
			
		
	
	c4e01da5ca
	
	
	
		
			
			* proc: bugfix: StepInto can not function when temp bps exist * terminal,service: auto-continue during next and step Make dlv call continue automatically when a breakpoint is hit on a different goroutine during a next or step operation. Added API hooks to implement the other solution to this problem (cancel the next/step operation if a different breakpoint is hit). Fixes #387
		
			
				
	
	
		
			296 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rpc2
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"net/rpc"
 | |
| 	"net/rpc/jsonrpc"
 | |
| 
 | |
| 	"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)
 | |
| 	}
 | |
| 	return &RPCClient{
 | |
| 		addr:   addr,
 | |
| 		client: client,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *RPCClient) ProcessPid() int {
 | |
| 	out := new(ProcessPidOut)
 | |
| 	c.call("ProcessPid", ProcessPidIn{}, out)
 | |
| 	return out.Pid
 | |
| }
 | |
| 
 | |
| func (c *RPCClient) Detach(kill bool) error {
 | |
| 	out := new(DetachOut)
 | |
| 	return c.call("Detach", DetachIn{kill}, out)
 | |
| }
 | |
| 
 | |
| func (c *RPCClient) Restart() error {
 | |
| 	out := new(RestartOut)
 | |
| 	return c.call("Restart", RestartIn{}, out)
 | |
| }
 | |
| 
 | |
| 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) 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() (string, error) {
 | |
| 	out := new(ListRegistersOut)
 | |
| 	err := c.call("ListRegisters", ListRegistersIn{}, out)
 | |
| 	return out.Registers, 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)
 | |
| }
 |