Replaced net/rpc with custom version

This version preserves the order of requests, allows the
client to switch between API versions and introduces a
way to send notifications to the client (see TODO item at:
proc/proc_linux.go:325).

Fixes #523, #571
This commit is contained in:
aarzilli 2016-06-19 08:43:29 +02:00
parent 51c39ed171
commit 80336e57e0
12 changed files with 432 additions and 221 deletions

@ -15,8 +15,8 @@ import (
"github.com/derekparker/delve/config"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc1"
"github.com/derekparker/delve/service/rpc2"
"github.com/derekparker/delve/service/rpccommon"
"github.com/derekparker/delve/terminal"
"github.com/derekparker/delve/version"
"github.com/spf13/cobra"
@ -27,8 +27,8 @@ var (
Log bool
// Headless is whether to run without terminal.
Headless bool
// ApiVersion is the requested API version while running headless
ApiVersion int
// APIVersion is the requested API version while running headless
APIVersion int
// AcceptMulti allows multiple clients to connect to the same server
AcceptMulti bool
// Addr is the debugging server listen address.
@ -81,7 +81,7 @@ func New() *cobra.Command {
RootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.")
RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
RootCommand.PersistentFlags().BoolVarP(&AcceptMulti, "accept-multiclient", "", false, "Allows a headless server to accept multiple client connections. Note that the server API is not reentrant and clients will have to coordinate.")
RootCommand.PersistentFlags().IntVar(&ApiVersion, "api-version", 1, "Selects API version when headless.")
RootCommand.PersistentFlags().IntVar(&APIVersion, "api-version", 1, "Selects API version when headless.")
RootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.")
RootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
@ -268,10 +268,11 @@ func traceCmd(cmd *cobra.Command, args []string) {
defer listener.Close()
// Create and start a debug server
server := rpc2.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: traceAttachPid,
APIVersion: 2,
}, Log)
if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
@ -386,28 +387,18 @@ func execute(attachPid int, processArgs []string, conf *config.Config, kind exec
Stop(bool) error
}
if !Headless {
ApiVersion = 2
}
// Create and start a debugger server
switch ApiVersion {
case 1:
server = rpc1.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
}, Log)
case 2:
server = rpc2.NewServer(&service.Config{
switch APIVersion {
case 1, 2:
server = rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
APIVersion: APIVersion,
}, Log)
default:
fmt.Println("Unknown API version %d", ApiVersion)
fmt.Println("Unknown API version %d", APIVersion)
return 1
}

@ -273,3 +273,18 @@ type AsmInstruction struct {
}
type AsmInstructions []AsmInstruction
type GetVersionIn struct {
}
type GetVersionOut struct {
DelveVersion string
APIVersion int
}
type SetAPIVersionIn struct {
APIVersion int
}
type SetAPIVersionOut struct {
}

@ -16,7 +16,9 @@ type Config struct {
// AttachPid is the PID of an existing process to which the debugger should
// attach.
AttachPid int
// AcceptMulti configures the server to accept multiple connection
// Note that the server API is not reentrant and clients will have to coordinate
// AcceptMulti configures the server to accept multiple connection.
// Note that the server API is not reentrant and clients will have to coordinate.
AcceptMulti bool
// APIVersion selects which version of the API to serve (default: 1).
APIVersion int
}

@ -3,11 +3,6 @@ package rpc1
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
grpc "net/rpc"
"net/rpc/jsonrpc"
"github.com/derekparker/delve/proc"
"github.com/derekparker/delve/service"
@ -15,89 +10,17 @@ import (
"github.com/derekparker/delve/service/debugger"
)
var defaultLoadConfig = proc.LoadConfig{ true, 1, 64, 64, -1 }
type ServerImpl struct {
s *RPCServer
}
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
// listener is used to serve HTTP.
listener net.Listener
// stopChan is used to stop the listener goroutine
stopChan chan struct{}
// debugger is a debugger service.
debugger *debugger.Debugger
}
// 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)
}
log.Printf("Using API v1")
return &ServerImpl{
&RPCServer{
config: config,
listener: config.Listener,
stopChan: make(chan struct{}),
},
}
}
// Stop detaches from the debugger and waits for it to stop.
func (s *ServerImpl) Stop(kill bool) error {
if s.s.config.AcceptMulti {
close(s.s.stopChan)
s.s.listener.Close()
}
err := s.s.debugger.Detach(kill)
if err != nil {
return err
}
return 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
// Create and start the debugger
if s.s.debugger, err = debugger.New(&debugger.Config{
ProcessArgs: s.s.config.ProcessArgs,
AttachPid: s.s.config.AttachPid,
}); err != nil {
return err
}
rpcs := grpc.NewServer()
rpcs.Register(s.s)
go func() {
defer s.s.listener.Close()
for {
c, err := s.s.listener.Accept()
if err != nil {
select {
case <-s.s.stopChan:
// We were supposed to exit, do nothing and return
return
default:
panic(err)
}
}
go rpcs.ServeCodec(jsonrpc.NewServerCodec(c))
if !s.s.config.AcceptMulti {
break
}
}
}()
return nil
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
return &RPCServer{config, debugger}
}
func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error {
@ -109,10 +32,6 @@ func (s *RPCServer) Detach(kill bool, ret *int) error {
return s.debugger.Detach(kill)
}
func (s *ServerImpl) Restart() error {
return s.s.Restart(nil, nil)
}
func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
if s.config.AttachPid != 0 {
return errors.New("cannot restart process Delve did not create")
@ -129,13 +48,9 @@ func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
return nil
}
func (s *RPCServer) Command(command *api.DebuggerCommand, state *api.DebuggerState) error {
func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) {
st, err := s.debugger.Command(command)
if err != nil {
return err
}
*state = *st
return nil
cb.Return(st, err)
}
func (s *RPCServer) GetBreakpoint(id int, breakpoint *api.Breakpoint) error {

@ -26,10 +26,9 @@ func NewClient(addr string) *RPCClient {
if err != nil {
log.Fatal("dialing:", err)
}
return &RPCClient{
addr: addr,
client: client,
}
c := &RPCClient{addr: addr, client: client}
c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{})
return c
}
func (c *RPCClient) ProcessPid() int {

@ -3,101 +3,21 @@ package rpc2
import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
grpc "net/rpc"
"net/rpc/jsonrpc"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/debugger"
)
type ServerImpl struct {
s *RPCServer
}
type RPCServer 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 a debugger service.
debugger *debugger.Debugger
}
// 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)
}
return &ServerImpl{
&RPCServer{
config: config,
listener: config.Listener,
stopChan: make(chan struct{}),
},
}
}
// Stop detaches from the debugger and waits for it to stop.
func (s *ServerImpl) Stop(kill bool) error {
if s.s.config.AcceptMulti {
close(s.s.stopChan)
s.s.listener.Close()
}
err := s.s.debugger.Detach(kill)
if err != nil {
return err
}
return 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
// Create and start the debugger
if s.s.debugger, err = debugger.New(&debugger.Config{
ProcessArgs: s.s.config.ProcessArgs,
AttachPid: s.s.config.AttachPid,
}); err != nil {
return err
}
rpcs := grpc.NewServer()
rpcs.Register(s.s)
go func() {
defer s.s.listener.Close()
for {
c, err := s.s.listener.Accept()
if err != nil {
select {
case <-s.s.stopChan:
// We were supposed to exit, do nothing and return
return
default:
panic(err)
}
}
go rpcs.ServeCodec(jsonrpc.NewServerCodec(c))
if !s.s.config.AcceptMulti {
break
}
}
}()
return nil
}
func (s *ServerImpl) Restart() error {
return s.s.Restart(RestartIn{}, nil)
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
return &RPCServer{config, debugger}
}
type ProcessPidIn struct {
@ -161,13 +81,16 @@ type CommandOut struct {
}
// Command interrupts, continues and steps through the program.
func (s *RPCServer) Command(command api.DebuggerCommand, out *CommandOut) error {
func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) {
st, err := s.debugger.Command(&command)
if err != nil {
return err
cb.Return(nil, err)
return
}
var out CommandOut
out.State = *st
return nil
cb.Return(out, nil)
return
}
type GetBreakpointIn struct {
@ -201,7 +124,7 @@ type StacktraceIn struct {
Id int
Depth int
Full bool
Cfg *api.LoadConfig
Cfg *api.LoadConfig
}
type StacktraceOut struct {
@ -215,7 +138,7 @@ type StacktraceOut struct {
func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
cfg := arg.Cfg
if cfg == nil && arg.Full {
cfg = &api.LoadConfig{ true, 1, 64, 64, -1 }
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
}
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, api.LoadConfigToProc(cfg))
if err != nil {
@ -361,7 +284,7 @@ func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
type ListPackageVarsIn struct {
Filter string
Cfg api.LoadConfig
Cfg api.LoadConfig
}
type ListPackageVarsOut struct {
@ -412,7 +335,7 @@ func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) er
type ListLocalVarsIn struct {
Scope api.EvalScope
Cfg api.LoadConfig
Cfg api.LoadConfig
}
type ListLocalVarsOut struct {
@ -431,7 +354,7 @@ func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) er
type ListFunctionArgsIn struct {
Scope api.EvalScope
Cfg api.LoadConfig
Cfg api.LoadConfig
}
type ListFunctionArgsOut struct {
@ -451,7 +374,7 @@ func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionAr
type EvalIn struct {
Scope api.EvalScope
Expr string
Cfg *api.LoadConfig
Cfg *api.LoadConfig
}
type EvalOut struct {
@ -465,7 +388,7 @@ type EvalOut struct {
func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
cfg := arg.Cfg
if cfg == nil {
cfg = &api.LoadConfig{ true, 1, 64, 64, -1 }
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
}
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr, *api.LoadConfigToProc(cfg))
if err != nil {

6
service/rpccallback.go Normal file

@ -0,0 +1,6 @@
package service
// RPCCallback is used by RPC methods to return their result asynchronously.
type RPCCallback interface {
Return(out interface{}, err error)
}

351
service/rpccommon/server.go Normal file

@ -0,0 +1,351 @@
package rpccommon
import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"reflect"
"sync"
"unicode"
"unicode/utf8"
"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"
"github.com/derekparker/delve/version"
)
// 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,
}); 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
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 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
}

@ -1,4 +1,5 @@
package main
// This program checks the types of the arguments of calls to
// the API in service/rpc2/client.go (done using rpc2.(*Client).call)
// against the declared types of API methods in srvice/rpc2/server.go
@ -13,6 +14,7 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
)
func findRPCDir() string {
@ -180,10 +182,12 @@ func main() {
continue
}
if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) {
log.Printf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
errcount++
continue
if !strings.HasSuffix(params.At(1).Type().String(), "/service.RPCCallback") {
if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) {
log.Printf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
errcount++
continue
}
}
if clit, ok := arg1.(*ast.CompositeLit); ok {

@ -17,6 +17,7 @@ import (
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc1"
"github.com/derekparker/delve/service/rpccommon"
)
func withTestClient1(name string, t *testing.T, fn func(c *rpc1.RPCClient)) {
@ -25,7 +26,7 @@ func withTestClient1(name string, t *testing.T, fn func(c *rpc1.RPCClient)) {
t.Fatalf("couldn't start listener: %s\n", err)
}
defer listener.Close()
server := rpc1.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{protest.BuildFixture(name).Path},
}, false)
@ -46,7 +47,7 @@ func Test1RunWithInvalidPath(t *testing.T) {
t.Fatalf("couldn't start listener: %s\n", err)
}
defer listener.Close()
server := rpc1.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{"invalid_path"},
}, false)
@ -134,7 +135,7 @@ func Test1Restart_duringStop(t *testing.T) {
func Test1Restart_attachPid(t *testing.T) {
// Assert it does not work and returns error.
// We cannot restart a process we did not spawn.
server := rpc1.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: nil,
AttachPid: 999,
}, false)

@ -18,6 +18,7 @@ import (
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpc2"
"github.com/derekparker/delve/service/rpccommon"
)
var normalLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
@ -32,7 +33,7 @@ func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
t.Fatalf("couldn't start listener: %s\n", err)
}
defer listener.Close()
server := rpc2.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{protest.BuildFixture(name).Path},
}, false)
@ -53,9 +54,10 @@ func TestRunWithInvalidPath(t *testing.T) {
t.Fatalf("couldn't start listener: %s\n", err)
}
defer listener.Close()
server := rpc2.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{"invalid_path"},
APIVersion: 2,
}, false)
if err := server.Run(); err == nil {
t.Fatal("Expected Run to return error for invalid program path")
@ -141,9 +143,10 @@ func TestRestart_duringStop(t *testing.T) {
func TestRestart_attachPid(t *testing.T) {
// Assert it does not work and returns error.
// We cannot restart a process we did not spawn.
server := rpc2.NewServer(&service.Config{
Listener: nil,
AttachPid: 999,
server := rpccommon.NewServer(&service.Config{
Listener: nil,
AttachPid: 999,
APIVersion: 2,
}, false)
if err := server.Restart(); err == nil {
t.Fatal("expected error on restart after attaching to pid but got none")

@ -13,6 +13,7 @@ import (
"github.com/derekparker/delve/proc/test"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
"github.com/derekparker/delve/service/rpccommon"
"github.com/derekparker/delve/service/rpc2"
)
@ -77,7 +78,7 @@ func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
t.Fatalf("couldn't start listener: %s\n", err)
}
defer listener.Close()
server := rpc2.NewServer(&service.Config{
server := rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: []string{test.BuildFixture(name).Path},
}, false)