service,logflags: log all RPC messages
We occasionally receive bug reports from users of VSCode-go and GoLand. GoLand has its own way of capturing the packet exchange between itself and delve but VSCode-go (supposedly) doesn't. So far this hasn't been a problem since all bug reports were obvious bugs on the plugin or easy to reproduce without VSCode-go, but it might be helpful in the future to have a way to log the packet exchange between dlv and a frontend. This commit adds a --log-output option to enable logging of all rpc messages and changes service/rpccommon accordingly.
This commit is contained in:
parent
60c58acb8e
commit
454491ce86
@ -37,6 +37,7 @@ Pass flags to the program you are debugging using `--`, for example:
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -37,6 +37,7 @@ dlv attach pid [executable]
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -32,6 +32,7 @@ dlv connect addr
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -36,6 +36,7 @@ dlv core <executable> <core>
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -43,6 +43,7 @@ dlv debug [package]
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -37,6 +37,7 @@ dlv exec <path/to/binary>
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -36,6 +36,7 @@ dlv replay [trace directory]
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -32,6 +32,7 @@ dlv run
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -43,6 +43,7 @@ dlv test [package]
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -45,6 +45,7 @@ dlv trace [package] regexp
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -32,6 +32,7 @@ dlv version
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.
|
Defaults to "debugger" when logging is enabled with --log.
|
||||||
--wd string Working directory for running the program. (default ".")
|
--wd string Working directory for running the program. (default ".")
|
||||||
```
|
```
|
||||||
|
|||||||
@ -94,6 +94,7 @@ func New(docCall bool) *cobra.Command {
|
|||||||
gdbwire Log connection to gdbserial backend
|
gdbwire Log connection to gdbserial backend
|
||||||
lldbout Copy output from debugserver/lldb to standard output
|
lldbout Copy output from debugserver/lldb to standard output
|
||||||
debuglineerr Log recoverable errors reading .debug_line
|
debuglineerr Log recoverable errors reading .debug_line
|
||||||
|
rpc Log all RPC messages
|
||||||
Defaults to "debugger" when logging is enabled with --log.`)
|
Defaults to "debugger" when logging is enabled with --log.`)
|
||||||
RootCommand.PersistentFlags().BoolVarP(&Headless, "headless", "", false, "Run debug server only, in headless mode.")
|
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().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.")
|
||||||
@ -363,7 +364,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
|||||||
APIVersion: 2,
|
APIVersion: 2,
|
||||||
WorkingDir: WorkingDir,
|
WorkingDir: WorkingDir,
|
||||||
Backend: Backend,
|
Backend: Backend,
|
||||||
}, logflags.Debugger())
|
}, logflags.RPC())
|
||||||
if err := server.Run(); err != nil {
|
if err := server.Run(); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return 1
|
return 1
|
||||||
@ -509,7 +510,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
Foreground: Headless,
|
Foreground: Headless,
|
||||||
|
|
||||||
DisconnectChan: disconnectChan,
|
DisconnectChan: disconnectChan,
|
||||||
}, logflags.Debugger())
|
}, logflags.RPC())
|
||||||
default:
|
default:
|
||||||
fmt.Printf("Unknown API version: %d\n", APIVersion)
|
fmt.Printf("Unknown API version: %d\n", APIVersion)
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@ -2,14 +2,16 @@ package logflags
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var debugger = false
|
var debugger = false
|
||||||
var gdbWire = false
|
var gdbWire = false
|
||||||
var lldbServerOutput = false
|
var lldbServerOutput = false
|
||||||
var suppressedErrors = false
|
|
||||||
var debugLineErrors = false
|
var debugLineErrors = false
|
||||||
|
var rpc = false
|
||||||
|
|
||||||
// GdbWire returns true if the gdbserial package should log all the packets
|
// GdbWire returns true if the gdbserial package should log all the packets
|
||||||
// exchanged with the stub.
|
// exchanged with the stub.
|
||||||
@ -34,11 +36,18 @@ func DebugLineErrors() bool {
|
|||||||
return debugLineErrors
|
return debugLineErrors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RPC returns true if rpc messages should be logged.
|
||||||
|
func RPC() bool {
|
||||||
|
return rpc
|
||||||
|
}
|
||||||
|
|
||||||
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
|
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
|
||||||
|
|
||||||
// Setup sets debugger flags based on the contents of logstr.
|
// Setup sets debugger flags based on the contents of logstr.
|
||||||
func Setup(log bool, logstr string) error {
|
func Setup(logFlag bool, logstr string) error {
|
||||||
if !log {
|
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||||
|
if !logFlag {
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
if logstr != "" {
|
if logstr != "" {
|
||||||
return errLogstrWithoutLog
|
return errLogstrWithoutLog
|
||||||
}
|
}
|
||||||
@ -58,6 +67,8 @@ func Setup(log bool, logstr string) error {
|
|||||||
lldbServerOutput = true
|
lldbServerOutput = true
|
||||||
case "debuglineerr":
|
case "debuglineerr":
|
||||||
debugLineErrors = true
|
debugLineErrors = true
|
||||||
|
case "rpc":
|
||||||
|
rpc = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/pkg/logflags"
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/pkg/proc"
|
||||||
"github.com/derekparker/delve/pkg/proc/core"
|
"github.com/derekparker/delve/pkg/proc/core"
|
||||||
"github.com/derekparker/delve/pkg/proc/gdbserial"
|
"github.com/derekparker/delve/pkg/proc/gdbserial"
|
||||||
@ -70,7 +71,9 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
// Create the process by either attaching or launching.
|
// Create the process by either attaching or launching.
|
||||||
switch {
|
switch {
|
||||||
case d.config.AttachPid > 0:
|
case d.config.AttachPid > 0:
|
||||||
log.Printf("attaching to pid %d", d.config.AttachPid)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("attaching to pid %d", d.config.AttachPid)
|
||||||
|
}
|
||||||
path := ""
|
path := ""
|
||||||
if len(d.processArgs) > 0 {
|
if len(d.processArgs) > 0 {
|
||||||
path = d.processArgs[0]
|
path = d.processArgs[0]
|
||||||
@ -86,10 +89,14 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
var err error
|
var err error
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "rr":
|
case "rr":
|
||||||
log.Printf("opening trace %s", d.config.CoreFile)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("opening trace %s", d.config.CoreFile)
|
||||||
|
}
|
||||||
p, err = gdbserial.Replay(d.config.CoreFile, false)
|
p, err = gdbserial.Replay(d.config.CoreFile, false)
|
||||||
default:
|
default:
|
||||||
log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.processArgs[0])
|
if logflags.Debugger() {
|
||||||
|
log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.processArgs[0])
|
||||||
|
}
|
||||||
p, err = core.OpenCore(d.config.CoreFile, d.processArgs[0])
|
p, err = core.OpenCore(d.config.CoreFile, d.processArgs[0])
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -98,7 +105,9 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
d.target = p
|
d.target = p
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Printf("launching process with args: %v", d.processArgs)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("launching process with args: %v", d.processArgs)
|
||||||
|
}
|
||||||
p, err := d.Launch(d.processArgs, d.config.WorkingDir)
|
p, err := d.Launch(d.processArgs, d.config.WorkingDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != proc.NotExecutableErr && err != proc.UnsupportedLinuxArchErr && err != proc.UnsupportedWindowsArchErr && err != proc.UnsupportedDarwinArchErr {
|
if err != proc.NotExecutableErr && err != proc.UnsupportedLinuxArchErr && err != proc.UnsupportedWindowsArchErr && err != proc.UnsupportedDarwinArchErr {
|
||||||
@ -358,7 +367,9 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
createdBp = api.ConvertBreakpoint(bp)
|
createdBp = api.ConvertBreakpoint(bp)
|
||||||
log.Printf("created breakpoint: %#v", createdBp)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("created breakpoint: %#v", createdBp)
|
||||||
|
}
|
||||||
return createdBp, nil
|
return createdBp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +417,9 @@ func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint
|
|||||||
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
||||||
}
|
}
|
||||||
clearedBp = api.ConvertBreakpoint(bp)
|
clearedBp = api.ConvertBreakpoint(bp)
|
||||||
log.Printf("cleared breakpoint: %#v", clearedBp)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("cleared breakpoint: %#v", clearedBp)
|
||||||
|
}
|
||||||
return clearedBp, err
|
return clearedBp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +517,9 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
if command.Name == api.Halt {
|
if command.Name == api.Halt {
|
||||||
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
||||||
// access the process directly.
|
// access the process directly.
|
||||||
log.Print("halting")
|
if logflags.Debugger() {
|
||||||
|
log.Print("halting")
|
||||||
|
}
|
||||||
err = d.target.RequestManualStop()
|
err = d.target.RequestManualStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,10 +530,14 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
|
|
||||||
switch command.Name {
|
switch command.Name {
|
||||||
case api.Continue:
|
case api.Continue:
|
||||||
log.Print("continuing")
|
if logflags.Debugger() {
|
||||||
|
log.Print("continuing")
|
||||||
|
}
|
||||||
err = proc.Continue(d.target)
|
err = proc.Continue(d.target)
|
||||||
case api.Rewind:
|
case api.Rewind:
|
||||||
log.Print("rewinding")
|
if logflags.Debugger() {
|
||||||
|
log.Print("rewinding")
|
||||||
|
}
|
||||||
if err := d.target.Direction(proc.Backward); err != nil {
|
if err := d.target.Direction(proc.Backward); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -527,23 +546,35 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
}()
|
}()
|
||||||
err = proc.Continue(d.target)
|
err = proc.Continue(d.target)
|
||||||
case api.Next:
|
case api.Next:
|
||||||
log.Print("nexting")
|
if logflags.Debugger() {
|
||||||
|
log.Print("nexting")
|
||||||
|
}
|
||||||
err = proc.Next(d.target)
|
err = proc.Next(d.target)
|
||||||
case api.Step:
|
case api.Step:
|
||||||
log.Print("stepping")
|
if logflags.Debugger() {
|
||||||
|
log.Print("stepping")
|
||||||
|
}
|
||||||
err = proc.Step(d.target)
|
err = proc.Step(d.target)
|
||||||
case api.StepInstruction:
|
case api.StepInstruction:
|
||||||
log.Print("single stepping")
|
if logflags.Debugger() {
|
||||||
|
log.Print("single stepping")
|
||||||
|
}
|
||||||
err = d.target.StepInstruction()
|
err = d.target.StepInstruction()
|
||||||
case api.StepOut:
|
case api.StepOut:
|
||||||
log.Print("step out")
|
if logflags.Debugger() {
|
||||||
|
log.Print("step out")
|
||||||
|
}
|
||||||
err = proc.StepOut(d.target)
|
err = proc.StepOut(d.target)
|
||||||
case api.SwitchThread:
|
case api.SwitchThread:
|
||||||
log.Printf("switching to thread %d", command.ThreadID)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("switching to thread %d", command.ThreadID)
|
||||||
|
}
|
||||||
err = d.target.SwitchThread(command.ThreadID)
|
err = d.target.SwitchThread(command.ThreadID)
|
||||||
withBreakpointInfo = false
|
withBreakpointInfo = false
|
||||||
case api.SwitchGoroutine:
|
case api.SwitchGoroutine:
|
||||||
log.Printf("switching to goroutine %d", command.GoroutineID)
|
if logflags.Debugger() {
|
||||||
|
log.Printf("switching to goroutine %d", command.GoroutineID)
|
||||||
|
}
|
||||||
err = d.target.SwitchGoroutine(command.GoroutineID)
|
err = d.target.SwitchGoroutine(command.GoroutineID)
|
||||||
withBreakpointInfo = false
|
withBreakpointInfo = false
|
||||||
case api.Halt:
|
case api.Halt:
|
||||||
|
|||||||
@ -2,10 +2,10 @@ package rpccommon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/rpc"
|
"net/rpc"
|
||||||
@ -41,6 +41,7 @@ type ServerImpl struct {
|
|||||||
s2 *rpc2.RPCServer
|
s2 *rpc2.RPCServer
|
||||||
// maps of served methods, one for each supported API.
|
// maps of served methods, one for each supported API.
|
||||||
methodMaps []map[string]*methodType
|
methodMaps []map[string]*methodType
|
||||||
|
log bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RPCCallback struct {
|
type RPCCallback struct {
|
||||||
@ -65,17 +66,16 @@ type methodType struct {
|
|||||||
|
|
||||||
// NewServer creates a new RPCServer.
|
// NewServer creates a new RPCServer.
|
||||||
func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
|
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 {
|
if config.APIVersion < 2 {
|
||||||
log.Printf("Using API v1")
|
if logEnabled {
|
||||||
|
log.Printf("Using API v1")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &ServerImpl{
|
return &ServerImpl{
|
||||||
config: config,
|
config: config,
|
||||||
listener: config.Listener,
|
listener: config.Listener,
|
||||||
stopChan: make(chan struct{}),
|
stopChan: make(chan struct{}),
|
||||||
|
log: logEnabled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +130,10 @@ func (s *ServerImpl) Run() error {
|
|||||||
|
|
||||||
s.methodMaps[0] = map[string]*methodType{}
|
s.methodMaps[0] = map[string]*methodType{}
|
||||||
s.methodMaps[1] = map[string]*methodType{}
|
s.methodMaps[1] = map[string]*methodType{}
|
||||||
suitableMethods(s.s1, s.methodMaps[0])
|
suitableMethods(s.s1, s.methodMaps[0], s.log)
|
||||||
suitableMethods(rpcServer, s.methodMaps[0])
|
suitableMethods(rpcServer, s.methodMaps[0], s.log)
|
||||||
suitableMethods(s.s2, s.methodMaps[1])
|
suitableMethods(s.s2, s.methodMaps[1], s.log)
|
||||||
suitableMethods(rpcServer, s.methodMaps[1])
|
suitableMethods(rpcServer, s.methodMaps[1], s.log)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer s.listener.Close()
|
defer s.listener.Close()
|
||||||
@ -183,12 +183,14 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
|
|||||||
// two signatures:
|
// two signatures:
|
||||||
// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error
|
// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error
|
||||||
// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback)
|
// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback)
|
||||||
func suitableMethods(rcvr interface{}, methods map[string]*methodType) {
|
func suitableMethods(rcvr interface{}, methods map[string]*methodType, logEnabled bool) {
|
||||||
typ := reflect.TypeOf(rcvr)
|
typ := reflect.TypeOf(rcvr)
|
||||||
rcvrv := reflect.ValueOf(rcvr)
|
rcvrv := reflect.ValueOf(rcvr)
|
||||||
sname := reflect.Indirect(rcvrv).Type().Name()
|
sname := reflect.Indirect(rcvrv).Type().Name()
|
||||||
if sname == "" {
|
if sname == "" {
|
||||||
log.Printf("rpc.Register: no service name for type %s", typ)
|
if logEnabled {
|
||||||
|
log.Printf("rpc.Register: no service name for type %s", typ)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for m := 0; m < typ.NumMethod(); m++ {
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
@ -201,13 +203,17 @@ func suitableMethods(rcvr interface{}, methods map[string]*methodType) {
|
|||||||
}
|
}
|
||||||
// Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback)
|
// Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback)
|
||||||
if mtype.NumIn() != 3 {
|
if mtype.NumIn() != 3 {
|
||||||
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// First arg need not be a pointer.
|
// First arg need not be a pointer.
|
||||||
argType := mtype.In(1)
|
argType := mtype.In(1)
|
||||||
if !isExportedOrBuiltinType(argType) {
|
if !isExportedOrBuiltinType(argType) {
|
||||||
log.Println(mname, "argument type not exported:", argType)
|
if logEnabled {
|
||||||
|
log.Println(mname, "argument type not exported:", argType)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,29 +223,39 @@ func suitableMethods(rcvr interface{}, methods map[string]*methodType) {
|
|||||||
if synchronous {
|
if synchronous {
|
||||||
// Second arg must be a pointer.
|
// Second arg must be a pointer.
|
||||||
if replyType.Kind() != reflect.Ptr {
|
if replyType.Kind() != reflect.Ptr {
|
||||||
log.Println("method", mname, "reply type not a pointer:", replyType)
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "reply type not a pointer:", replyType)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Reply type must be exported.
|
// Reply type must be exported.
|
||||||
if !isExportedOrBuiltinType(replyType) {
|
if !isExportedOrBuiltinType(replyType) {
|
||||||
log.Println("method", mname, "reply type not exported:", replyType)
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "reply type not exported:", replyType)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method needs one out.
|
// Method needs one out.
|
||||||
if mtype.NumOut() != 1 {
|
if mtype.NumOut() != 1 {
|
||||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// The return type of the method must be error.
|
// The return type of the method must be error.
|
||||||
if returnType := mtype.Out(0); returnType != typeOfError {
|
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||||
log.Println("method", mname, "returns", returnType.String(), "not error")
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "returns", returnType.String(), "not error")
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Method needs zero outs.
|
// Method needs zero outs.
|
||||||
if mtype.NumOut() != 0 {
|
if mtype.NumOut() != 0 {
|
||||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
if logEnabled {
|
||||||
|
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -257,14 +273,18 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
|
|||||||
err := codec.ReadRequestHeader(&req)
|
err := codec.ReadRequestHeader(&req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
log.Println("rpc:", err)
|
if s.log {
|
||||||
|
log.Println("rpc:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod]
|
mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("rpc: can't find method %s", req.ServiceMethod)
|
if s.log {
|
||||||
|
log.Printf("rpc: can't find method %s", req.ServiceMethod)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,6 +307,10 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mtype.Synchronous {
|
if mtype.Synchronous {
|
||||||
|
if s.log {
|
||||||
|
argvbytes, _ := json.Marshal(argv.Interface())
|
||||||
|
log.Printf("-> %s(%T%s)\n", req.ServiceMethod, argv.Interface(), argvbytes)
|
||||||
|
}
|
||||||
replyv = reflect.New(mtype.ReplyType.Elem())
|
replyv = reflect.New(mtype.ReplyType.Elem())
|
||||||
function := mtype.method.Func
|
function := mtype.method.Func
|
||||||
var returnValues []reflect.Value
|
var returnValues []reflect.Value
|
||||||
@ -306,8 +330,16 @@ func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
|
|||||||
errmsg = errInter.(error).Error()
|
errmsg = errInter.(error).Error()
|
||||||
}
|
}
|
||||||
resp = rpc.Response{}
|
resp = rpc.Response{}
|
||||||
|
if s.log {
|
||||||
|
replyvbytes, _ := json.Marshal(replyv.Interface())
|
||||||
|
log.Printf("<- %T%s error: %q\n", replyv.Interface(), replyvbytes, errmsg)
|
||||||
|
}
|
||||||
s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
|
s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
|
||||||
} else {
|
} else {
|
||||||
|
if s.log {
|
||||||
|
argvbytes, _ := json.Marshal(argv.Interface())
|
||||||
|
log.Printf("(async %d) -> %s(%T%s)\n", req.Seq, req.ServiceMethod, argv.Interface(), argvbytes)
|
||||||
|
}
|
||||||
function := mtype.method.Func
|
function := mtype.method.Func
|
||||||
ctl := &RPCCallback{s, sending, codec, req}
|
ctl := &RPCCallback{s, sending, codec, req}
|
||||||
go func() {
|
go func() {
|
||||||
@ -342,7 +374,9 @@ func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *r
|
|||||||
defer sending.Unlock()
|
defer sending.Unlock()
|
||||||
err := codec.WriteResponse(resp, reply)
|
err := codec.WriteResponse(resp, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("rpc: writing response:", err)
|
if s.log {
|
||||||
|
log.Println("rpc: writing response:", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,6 +386,10 @@ func (cb *RPCCallback) Return(out interface{}, err error) {
|
|||||||
errmsg = err.Error()
|
errmsg = err.Error()
|
||||||
}
|
}
|
||||||
var resp rpc.Response
|
var resp rpc.Response
|
||||||
|
if cb.s.log {
|
||||||
|
outbytes, _ := json.Marshal(out)
|
||||||
|
log.Printf("(async %d) <- %T%s error: %q", cb.req.Seq, out, outbytes, errmsg)
|
||||||
|
}
|
||||||
cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
|
cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user