From ad641afb3495702efc91c9a3dd1e190d7db57e8c Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Tue, 21 Mar 2017 15:34:11 +0100 Subject: [PATCH] Update vendor folder (support for Delve RPC) --- .../derekparker/delve/pkg/version/version.go | 25 ++ .../derekparker/delve/service/rpc1/client.go | 312 +++++++++++++ .../delve/service/rpc1/readme.txtr | 5 + .../derekparker/delve/service/rpc1/server.go | 318 ++++++++++++++ .../delve/service/rpccommon/server.go | 409 ++++++++++++++++++ vendor/vendor.json | 18 + 6 files changed, 1087 insertions(+) create mode 100644 vendor/github.com/derekparker/delve/pkg/version/version.go create mode 100644 vendor/github.com/derekparker/delve/service/rpc1/client.go create mode 100644 vendor/github.com/derekparker/delve/service/rpc1/readme.txtr create mode 100644 vendor/github.com/derekparker/delve/service/rpc1/server.go create mode 100644 vendor/github.com/derekparker/delve/service/rpccommon/server.go diff --git a/vendor/github.com/derekparker/delve/pkg/version/version.go b/vendor/github.com/derekparker/delve/pkg/version/version.go new file mode 100644 index 0000000..f89e9df --- /dev/null +++ b/vendor/github.com/derekparker/delve/pkg/version/version.go @@ -0,0 +1,25 @@ +package version + +import "fmt" + +// Version represents the current version of Delve. +type Version struct { + Major string + Minor string + Patch string + Metadata string + Build string +} + +var ( + // DelveVersion is the current version of Delve. + DelveVersion = Version{Major: "0", Minor: "12", Patch: "1", Metadata: ""} +) + +func (v Version) String() string { + ver := fmt.Sprintf("Version: %s.%s.%s", v.Major, v.Minor, v.Patch) + if v.Metadata != "" { + ver += "-" + v.Metadata + } + return fmt.Sprintf("%s\nBuild: %s", ver, v.Build) +} diff --git a/vendor/github.com/derekparker/delve/service/rpc1/client.go b/vendor/github.com/derekparker/delve/service/rpc1/client.go new file mode 100644 index 0000000..43c76c4 --- /dev/null +++ b/vendor/github.com/derekparker/delve/service/rpc1/client.go @@ -0,0 +1,312 @@ +package rpc1 + +import ( + "errors" + "fmt" + "log" + "net/rpc" + "net/rpc/jsonrpc" + + "sync" + + "github.com/derekparker/delve/service/api" +) + +// Client is a RPC service.Client. +type RPCClient struct { + addr string + processPid int + client *rpc.Client + haltMu sync.Mutex + haltReq bool +} + +var unsupportedApiError = errors.New("unsupported") + +// 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 { + var pid int + c.call("ProcessPid", nil, &pid) + return pid +} + +func (c *RPCClient) Detach(kill bool) error { + return c.call("Detach", kill, nil) +} + +func (c *RPCClient) Restart() error { + return c.call("Restart", nil, nil) +} + +func (c *RPCClient) GetState() (*api.DebuggerState, error) { + state := new(api.DebuggerState) + err := c.call("State", nil, state) + return state, err +} + +func (c *RPCClient) Continue() <-chan *api.DebuggerState { + ch := make(chan *api.DebuggerState) + c.haltMu.Lock() + c.haltReq = false + c.haltMu.Unlock() + go func() { + for { + c.haltMu.Lock() + if c.haltReq { + c.haltMu.Unlock() + close(ch) + return + } + c.haltMu.Unlock() + state := new(api.DebuggerState) + err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state) + if err != nil { + state.Err = err + } + if state.Exited { + // Error types apparently 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) { + state := new(api.DebuggerState) + err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state) + return state, err +} + +func (c *RPCClient) Step() (*api.DebuggerState, error) { + state := new(api.DebuggerState) + err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state) + return state, err +} + +func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) { + state := new(api.DebuggerState) + err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state) + return state, err +} + +func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) { + state := new(api.DebuggerState) + cmd := &api.DebuggerCommand{ + Name: api.SwitchThread, + ThreadID: threadID, + } + err := c.call("Command", cmd, state) + return state, err +} + +func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) { + state := new(api.DebuggerState) + cmd := &api.DebuggerCommand{ + Name: api.SwitchGoroutine, + GoroutineID: goroutineID, + } + err := c.call("Command", cmd, state) + return state, err +} + +func (c *RPCClient) Halt() (*api.DebuggerState, error) { + state := new(api.DebuggerState) + c.haltMu.Lock() + c.haltReq = true + c.haltMu.Unlock() + err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state) + return state, err +} + +func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) { + breakpoint := new(api.Breakpoint) + err := c.call("GetBreakpoint", id, breakpoint) + return breakpoint, err +} + +func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) { + breakpoint := new(api.Breakpoint) + err := c.call("GetBreakpointByName", name, breakpoint) + return breakpoint, err +} + +func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) { + newBreakpoint := new(api.Breakpoint) + err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint) + return newBreakpoint, err +} + +func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) { + var breakpoints []*api.Breakpoint + err := c.call("ListBreakpoints", nil, &breakpoints) + return breakpoints, err +} + +func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) { + bp := new(api.Breakpoint) + err := c.call("ClearBreakpoint", id, bp) + return bp, err +} + +func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) { + bp := new(api.Breakpoint) + err := c.call("ClearBreakpointByName", name, bp) + return bp, err +} + +func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error { + err := c.call("AmendBreakpoint", bp, nil) + return err +} + +func (c *RPCClient) CancelNext() error { + return unsupportedApiError +} + +func (c *RPCClient) ListThreads() ([]*api.Thread, error) { + var threads []*api.Thread + err := c.call("ListThreads", nil, &threads) + return threads, err +} + +func (c *RPCClient) GetThread(id int) (*api.Thread, error) { + thread := new(api.Thread) + err := c.call("GetThread", id, &thread) + return thread, err +} + +func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) { + v := new(api.Variable) + err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v) + return v, err +} + +func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error { + var unused int + return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused) +} + +func (c *RPCClient) ListSources(filter string) ([]string, error) { + var sources []string + err := c.call("ListSources", filter, &sources) + return sources, err +} + +func (c *RPCClient) ListFunctions(filter string) ([]string, error) { + var funcs []string + err := c.call("ListFunctions", filter, &funcs) + return funcs, err +} + +func (c *RPCClient) ListTypes(filter string) ([]string, error) { + var types []string + err := c.call("ListTypes", filter, &types) + return types, err +} + +func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) { + var vars []api.Variable + err := c.call("ListPackageVars", filter, &vars) + return vars, err +} + +func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) { + var vars []api.Variable + err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars) + return vars, err +} + +func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) { + var vars []api.Variable + err := c.call("ListLocalVars", scope, &vars) + return vars, err +} + +func (c *RPCClient) ListRegisters() (string, error) { + var regs string + err := c.call("ListRegisters", nil, ®s) + return regs, err +} + +func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) { + var vars []api.Variable + err := c.call("ListFunctionArgs", scope, &vars) + return vars, err +} + +func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) { + var goroutines []*api.Goroutine + err := c.call("ListGoroutines", nil, &goroutines) + return goroutines, err +} + +func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) { + var locations []api.Stackframe + err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations) + return locations, err +} + +func (c *RPCClient) AttachedToExistingProcess() bool { + var answer bool + c.call("AttachedToExistingProcess", nil, &answer) + return answer +} + +func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) { + var answer []api.Location + err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer) + return answer, err +} + +// Disassemble code between startPC and endPC +func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { + var r api.AsmInstructions + err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r) + return r, err +} + +// Disassemble function containing pc +func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) { + var r api.AsmInstructions + err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r) + return r, 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) +} diff --git a/vendor/github.com/derekparker/delve/service/rpc1/readme.txtr b/vendor/github.com/derekparker/delve/service/rpc1/readme.txtr new file mode 100644 index 0000000..c25192d --- /dev/null +++ b/vendor/github.com/derekparker/delve/service/rpc1/readme.txtr @@ -0,0 +1,5 @@ +This package implements version 1 of Delve's API and is only +kept here for backwards compatibility. Client.go is the old +client code used by Delve's frontend (delve/cmd/dlv), it is +only preserved here for the backwards compatibility tests in +service/test/integration1_test.go. diff --git a/vendor/github.com/derekparker/delve/service/rpc1/server.go b/vendor/github.com/derekparker/delve/service/rpc1/server.go new file mode 100644 index 0000000..02b18e4 --- /dev/null +++ b/vendor/github.com/derekparker/delve/service/rpc1/server.go @@ -0,0 +1,318 @@ +package rpc1 + +import ( + "errors" + "fmt" + + "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/service" + "github.com/derekparker/delve/service/api" + "github.com/derekparker/delve/service/debugger" +) + +var defaultLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1} + +type RPCServer struct { + // config is all the information necessary to start the debugger and server. + config *service.Config + // debugger is a debugger service. + debugger *debugger.Debugger +} + +func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer { + return &RPCServer{config, debugger} +} + +func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error { + *pid = s.debugger.ProcessPid() + return nil +} + +func (s *RPCServer) Detach(kill bool, ret *int) error { + return s.debugger.Detach(kill) +} + +func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error { + if s.config.AttachPid != 0 { + return errors.New("cannot restart process Delve did not create") + } + _, err := s.debugger.Restart() + return err +} + +func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error { + st, err := s.debugger.State() + if err != nil { + return err + } + *state = *st + return nil +} + +func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) { + st, err := s.debugger.Command(command) + cb.Return(st, err) +} + +func (s *RPCServer) GetBreakpoint(id int, breakpoint *api.Breakpoint) error { + bp := s.debugger.FindBreakpoint(id) + if bp == nil { + return fmt.Errorf("no breakpoint with id %d", id) + } + *breakpoint = *bp + return nil +} + +func (s *RPCServer) GetBreakpointByName(name string, breakpoint *api.Breakpoint) error { + bp := s.debugger.FindBreakpointByName(name) + if bp == nil { + return fmt.Errorf("no breakpoint with name %s", name) + } + *breakpoint = *bp + return nil +} + +type StacktraceGoroutineArgs struct { + Id int + Depth int + Full bool +} + +func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error { + var loadcfg *proc.LoadConfig = nil + if args.Full { + loadcfg = &defaultLoadConfig + } + locs, err := s.debugger.Stacktrace(args.Id, args.Depth, loadcfg) + if err != nil { + return err + } + *locations = locs + return nil +} + +func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error { + *breakpoints = s.debugger.Breakpoints() + return nil +} + +func (s *RPCServer) CreateBreakpoint(bp, newBreakpoint *api.Breakpoint) error { + createdbp, err := s.debugger.CreateBreakpoint(bp) + if err != nil { + return err + } + *newBreakpoint = *createdbp + return nil +} + +func (s *RPCServer) ClearBreakpoint(id int, breakpoint *api.Breakpoint) error { + bp := s.debugger.FindBreakpoint(id) + if bp == nil { + return fmt.Errorf("no breakpoint with id %d", id) + } + deleted, err := s.debugger.ClearBreakpoint(bp) + if err != nil { + return err + } + *breakpoint = *deleted + return nil +} + +func (s *RPCServer) ClearBreakpointByName(name string, breakpoint *api.Breakpoint) error { + bp := s.debugger.FindBreakpointByName(name) + if bp == nil { + return fmt.Errorf("no breakpoint with name %s", name) + } + deleted, err := s.debugger.ClearBreakpoint(bp) + if err != nil { + return err + } + *breakpoint = *deleted + return nil +} + +func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error { + *unused = 0 + return s.debugger.AmendBreakpoint(amend) +} + +func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) { + *threads, err = s.debugger.Threads() + return err +} + +func (s *RPCServer) GetThread(id int, thread *api.Thread) error { + t, err := s.debugger.FindThread(id) + if err != nil { + return err + } + if t == nil { + return fmt.Errorf("no thread with id %d", id) + } + *thread = *t + return nil +} + +func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) error { + state, err := s.debugger.State() + if err != nil { + return err + } + + current := state.CurrentThread + if current == nil { + return fmt.Errorf("no current thread") + } + + vars, err := s.debugger.PackageVariables(current.ID, filter, defaultLoadConfig) + if err != nil { + return err + } + *variables = vars + return nil +} + +type ThreadListArgs struct { + Id int + Filter string +} + +func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error { + thread, err := s.debugger.FindThread(args.Id) + if err != nil { + return err + } + if thread == nil { + return fmt.Errorf("no thread with id %d", args.Id) + } + + vars, err := s.debugger.PackageVariables(args.Id, args.Filter, defaultLoadConfig) + if err != nil { + return err + } + *variables = vars + return nil +} + +func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error { + state, err := s.debugger.State() + if err != nil { + return err + } + + regs, err := s.debugger.Registers(state.CurrentThread.ID, false) + if err != nil { + return err + } + *registers = regs.String() + return nil +} + +func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error { + vars, err := s.debugger.LocalVariables(scope, defaultLoadConfig) + if err != nil { + return err + } + *variables = vars + return nil +} + +func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error { + vars, err := s.debugger.FunctionArguments(scope, defaultLoadConfig) + if err != nil { + return err + } + *variables = vars + return nil +} + +type EvalSymbolArgs struct { + Scope api.EvalScope + Symbol string +} + +func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error { + v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol, defaultLoadConfig) + if err != nil { + return err + } + *variable = *v + return nil +} + +type SetSymbolArgs struct { + Scope api.EvalScope + Symbol string + Value string +} + +func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error { + *unused = 0 + return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value) +} + +func (s *RPCServer) ListSources(filter string, sources *[]string) error { + ss, err := s.debugger.Sources(filter) + if err != nil { + return err + } + *sources = ss + return nil +} + +func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error { + fns, err := s.debugger.Functions(filter) + if err != nil { + return err + } + *funcs = fns + return nil +} + +func (s *RPCServer) ListTypes(filter string, types *[]string) error { + tps, err := s.debugger.Types(filter) + if err != nil { + return err + } + *types = tps + return nil +} + +func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error { + gs, err := s.debugger.Goroutines() + if err != nil { + return err + } + *goroutines = gs + return nil +} + +func (c *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error { + if c.config.AttachPid != 0 { + *answer = true + } + return nil +} + +type FindLocationArgs struct { + Scope api.EvalScope + Loc string +} + +func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error { + var err error + *answer, err = c.debugger.FindLocation(args.Scope, args.Loc) + return err +} + +type DisassembleRequest struct { + Scope api.EvalScope + StartPC, EndPC uint64 + Flavour api.AssemblyFlavour +} + +func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error { + var err error + *answer, err = c.debugger.Disassemble(args.Scope, args.StartPC, args.EndPC, args.Flavour) + return err +} diff --git a/vendor/github.com/derekparker/delve/service/rpccommon/server.go b/vendor/github.com/derekparker/delve/service/rpccommon/server.go new file mode 100644 index 0000000..765fd09 --- /dev/null +++ b/vendor/github.com/derekparker/delve/service/rpccommon/server.go @@ -0,0 +1,409 @@ +package rpccommon + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "net" + "net/rpc" + "net/rpc/jsonrpc" + "reflect" + "runtime" + "sync" + "unicode" + "unicode/utf8" + + "github.com/derekparker/delve/pkg/version" + "github.com/derekparker/delve/service" + "github.com/derekparker/delve/service/api" + "github.com/derekparker/delve/service/debugger" + "github.com/derekparker/delve/service/rpc1" + "github.com/derekparker/delve/service/rpc2" +) + +// ServerImpl implements a JSON-RPC server that can switch between two +// versions of the API. +type ServerImpl struct { + // config is all the information necessary to start the debugger and server. + config *service.Config + // listener is used to serve HTTP. + listener net.Listener + // stopChan is used to stop the listener goroutine. + stopChan chan struct{} + // debugger is the debugger service. + debugger *debugger.Debugger + // s1 is APIv1 server. + s1 *rpc1.RPCServer + // s2 is APIv2 server. + s2 *rpc2.RPCServer + // maps of served methods, one for each supported API. + methodMaps []map[string]*methodType +} + +type RPCCallback struct { + s *ServerImpl + sending *sync.Mutex + codec rpc.ServerCodec + req rpc.Request +} + +// RPCServer implements the RPC method calls common to all versions of the API. +type RPCServer struct { + s *ServerImpl +} + +type methodType struct { + method reflect.Method + Rcvr reflect.Value + ArgType reflect.Type + ReplyType reflect.Type + Synchronous bool +} + +// NewServer creates a new RPCServer. +func NewServer(config *service.Config, logEnabled bool) *ServerImpl { + log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) + if !logEnabled { + log.SetOutput(ioutil.Discard) + } + if config.APIVersion < 2 { + log.Printf("Using API v1") + } + return &ServerImpl{ + config: config, + listener: config.Listener, + stopChan: make(chan struct{}), + } +} + +// Stop stops the JSON-RPC server. +func (s *ServerImpl) Stop(kill bool) error { + if s.config.AcceptMulti { + close(s.stopChan) + s.listener.Close() + } + return s.debugger.Detach(kill) +} + +// Restart restarts the debugger. +func (s *ServerImpl) Restart() error { + if s.config.AttachPid != 0 { + return errors.New("cannot restart process Delve did not create") + } + return s.s2.Restart(rpc2.RestartIn{}, nil) +} + +// Run starts a debugger and exposes it with an HTTP server. The debugger +// itself can be stopped with the `detach` API. Run blocks until the HTTP +// server stops. +func (s *ServerImpl) Run() error { + var err error + + if s.config.APIVersion < 2 { + s.config.APIVersion = 1 + } + if s.config.APIVersion > 2 { + return fmt.Errorf("unknown API version") + } + + // Create and start the debugger + if s.debugger, err = debugger.New(&debugger.Config{ + ProcessArgs: s.config.ProcessArgs, + AttachPid: s.config.AttachPid, + WorkingDir: s.config.WorkingDir, + }); err != nil { + return err + } + + s.s1 = rpc1.NewServer(s.config, s.debugger) + s.s2 = rpc2.NewServer(s.config, s.debugger) + + rpcServer := &RPCServer{s} + + s.methodMaps = make([]map[string]*methodType, 2) + + s.methodMaps[0] = map[string]*methodType{} + s.methodMaps[1] = map[string]*methodType{} + suitableMethods(s.s1, s.methodMaps[0]) + suitableMethods(rpcServer, s.methodMaps[0]) + suitableMethods(s.s2, s.methodMaps[1]) + suitableMethods(rpcServer, s.methodMaps[1]) + + go func() { + defer s.listener.Close() + for { + c, err := s.listener.Accept() + if err != nil { + select { + case <-s.stopChan: + // We were supposed to exit, do nothing and return + return + default: + panic(err) + } + } + go s.serveJSONCodec(c) + if !s.config.AcceptMulti { + break + } + } + }() + return nil +} + +// Precompute the reflect type for error. Can't use error directly +// because Typeof takes an empty interface value. This is annoying. +var typeOfError = reflect.TypeOf((*error)(nil)).Elem() + +// Is this an exported - upper case - name? +func isExported(name string) bool { + rune, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(rune) +} + +// Is this type exported or a builtin? +func isExportedOrBuiltinType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + // PkgPath will be non-empty even for an exported type, + // so we need to check the type name as well. + return isExported(t.Name()) || t.PkgPath() == "" +} + +// Fills methods map with the methods of receiver that should be made +// available through the RPC interface. +// These are all the public methods of rcvr that have one of those +// two signatures: +// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error +// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback) +func suitableMethods(rcvr interface{}, methods map[string]*methodType) { + typ := reflect.TypeOf(rcvr) + rcvrv := reflect.ValueOf(rcvr) + sname := reflect.Indirect(rcvrv).Type().Name() + if sname == "" { + log.Printf("rpc.Register: no service name for type %s", typ) + return + } + for m := 0; m < typ.NumMethod(); m++ { + method := typ.Method(m) + mname := method.Name + mtype := method.Type + // method must be exported + if method.PkgPath != "" { + continue + } + // Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback) + if mtype.NumIn() != 3 { + log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) + continue + } + // First arg need not be a pointer. + argType := mtype.In(1) + if !isExportedOrBuiltinType(argType) { + log.Println(mname, "argument type not exported:", argType) + continue + } + + replyType := mtype.In(2) + synchronous := replyType.String() != "service.RPCCallback" + + if synchronous { + // Second arg must be a pointer. + if replyType.Kind() != reflect.Ptr { + log.Println("method", mname, "reply type not a pointer:", replyType) + continue + } + // Reply type must be exported. + if !isExportedOrBuiltinType(replyType) { + log.Println("method", mname, "reply type not exported:", replyType) + continue + } + + // Method needs one out. + if mtype.NumOut() != 1 { + log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) + continue + } + // The return type of the method must be error. + if returnType := mtype.Out(0); returnType != typeOfError { + log.Println("method", mname, "returns", returnType.String(), "not error") + continue + } + } else { + // Method needs zero outs. + if mtype.NumOut() != 0 { + log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) + continue + } + } + methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv} + } + return +} + +func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) { + sending := new(sync.Mutex) + codec := jsonrpc.NewServerCodec(conn) + var req rpc.Request + var resp rpc.Response + for { + req = rpc.Request{} + err := codec.ReadRequestHeader(&req) + if err != nil { + if err != io.EOF { + log.Println("rpc:", err) + } + break + } + + mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod] + if !ok { + log.Printf("rpc: can't find method %s", req.ServiceMethod) + continue + } + + var argv, replyv reflect.Value + + // Decode the argument value. + argIsValue := false // if true, need to indirect before calling. + if mtype.ArgType.Kind() == reflect.Ptr { + argv = reflect.New(mtype.ArgType.Elem()) + } else { + argv = reflect.New(mtype.ArgType) + argIsValue = true + } + // argv guaranteed to be a pointer now. + if err = codec.ReadRequestBody(argv.Interface()); err != nil { + return + } + if argIsValue { + argv = argv.Elem() + } + + if mtype.Synchronous { + replyv = reflect.New(mtype.ReplyType.Elem()) + function := mtype.method.Func + var returnValues []reflect.Value + var errInter interface{} + func() { + defer func() { + if ierr := recover(); ierr != nil { + errInter = newInternalError(ierr, 2) + } + }() + returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv}) + errInter = returnValues[0].Interface() + }() + + errmsg := "" + if errInter != nil { + errmsg = errInter.(error).Error() + } + resp = rpc.Response{} + s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg) + } else { + function := mtype.method.Func + ctl := &RPCCallback{s, sending, codec, req} + go func() { + defer func() { + if ierr := recover(); ierr != nil { + ctl.Return(nil, newInternalError(ierr, 2)) + } + }() + function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)}) + }() + } + } + codec.Close() +} + +// A value sent as a placeholder for the server's response value when the server +// receives an invalid request. It is never decoded by the client since the Response +// contains an error when it is used. +var invalidRequest = struct{}{} + +func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *rpc.Response, reply interface{}, codec rpc.ServerCodec, errmsg string) { + resp.ServiceMethod = req.ServiceMethod + if errmsg != "" { + resp.Error = errmsg + reply = invalidRequest + } + resp.Seq = req.Seq + sending.Lock() + defer sending.Unlock() + err := codec.WriteResponse(resp, reply) + if err != nil { + log.Println("rpc: writing response:", err) + } +} + +func (cb *RPCCallback) Return(out interface{}, err error) { + errmsg := "" + if err != nil { + errmsg = err.Error() + } + var resp rpc.Response + cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg) +} + +// GetVersion returns the version of delve as well as the API version +// currently served. +func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) error { + out.DelveVersion = version.DelveVersion.String() + out.APIVersion = s.s.config.APIVersion + return nil +} + +// Changes version of the API being served. +func (s *RPCServer) SetApiVersion(args api.SetAPIVersionIn, out *api.SetAPIVersionOut) error { + if args.APIVersion < 2 { + args.APIVersion = 1 + } + if args.APIVersion > 2 { + return fmt.Errorf("unknown API version") + } + s.s.config.APIVersion = args.APIVersion + return nil +} + +type internalError struct { + Err interface{} + Stack []internalErrorFrame +} + +type internalErrorFrame struct { + Pc uintptr + Func string + File string + Line int +} + +func newInternalError(ierr interface{}, skip int) *internalError { + r := &internalError{ierr, nil} + for i := skip; ; i++ { + pc, file, line, ok := runtime.Caller(i) + if !ok { + break + } + fname := "" + fn := runtime.FuncForPC(pc) + if fn != nil { + fname = fn.Name() + } + r.Stack = append(r.Stack, internalErrorFrame{pc, fname, file, line}) + } + return r +} + +func (err *internalError) Error() string { + var out bytes.Buffer + fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err) + for _, frame := range err.Stack { + fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line) + } + return out.String() +} diff --git a/vendor/vendor.json b/vendor/vendor.json index dcad732..9f40335 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -68,6 +68,12 @@ "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", "revisionTime": "2017-03-13T17:59:34Z" }, + { + "checksumSHA1": "xRa64kuzCXVktmGscs1vCz9jIHs=", + "path": "github.com/derekparker/delve/pkg/version", + "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", + "revisionTime": "2017-03-13T17:59:34Z" + }, { "checksumSHA1": "BPHre0My8iszpkoNcX4ljtrz1dA=", "path": "github.com/derekparker/delve/service", @@ -86,12 +92,24 @@ "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", "revisionTime": "2017-03-13T17:59:34Z" }, + { + "checksumSHA1": "d73mTsiCuYYpDaVs9QtozhzMDU0=", + "path": "github.com/derekparker/delve/service/rpc1", + "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", + "revisionTime": "2017-03-13T17:59:34Z" + }, { "checksumSHA1": "B+J0KRNuX0anRlif3mWXhBQhnVo=", "path": "github.com/derekparker/delve/service/rpc2", "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", "revisionTime": "2017-03-13T17:59:34Z" }, + { + "checksumSHA1": "PsNfnfv8GS79AG225Q/h57q/6F4=", + "path": "github.com/derekparker/delve/service/rpccommon", + "revision": "ab7367ed2bf15044f7bca97147a802f77b875797", + "revisionTime": "2017-03-13T17:59:34Z" + }, { "checksumSHA1": "hveFTNQ9YEyYRs6SWuXM+XU9qRI=", "path": "github.com/fsnotify/fsnotify",