cmd/dlv: add flag to make headless server accept multiple clients

This commit is contained in:
aarzilli
2016-02-01 11:05:26 +01:00
parent 3be65a4c1f
commit c277b27157
3 changed files with 39 additions and 10 deletions

View File

@ -32,6 +32,8 @@ var (
Log bool Log bool
// Headless is whether to run without terminal. // Headless is whether to run without terminal.
Headless bool Headless bool
// Allows multiple clients to connect to the same server
AcceptMulti bool
// Addr is the debugging server listen address. // Addr is the debugging server listen address.
Addr string Addr string
// InitFile is the path to initialization file. // InitFile is the path to initialization file.
@ -74,6 +76,7 @@ func init() {
rootCommand.PersistentFlags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.") rootCommand.PersistentFlags().StringVarP(&Addr, "listen", "l", "localhost:0", "Debugging server listen address.")
rootCommand.PersistentFlags().BoolVarP(&Log, "log", "", false, "Enable debugging server logging.") 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(&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 connection. Note that the server API is not reentrant and clients will have to coordinate")
rootCommand.PersistentFlags().StringVar(&InitFile, "init", "", "Init file, executed by the terminal client.") 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.") rootCommand.PersistentFlags().StringVar(&BuildFlags, "build-flags", buildFlagsDefault, "Build flags, to be passed to the compiler.")
@ -351,6 +354,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config) int {
Listener: listener, Listener: listener,
ProcessArgs: processArgs, ProcessArgs: processArgs,
AttachPid: attachPid, AttachPid: attachPid,
AcceptMulti: AcceptMulti,
}, Log) }, Log)
if err := server.Run(); err != nil { if err := server.Run(); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View File

@ -16,4 +16,7 @@ type Config struct {
// AttachPid is the PID of an existing process to which the debugger should // AttachPid is the PID of an existing process to which the debugger should
// attach. // attach.
AttachPid int 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 bool
} }

View File

@ -23,6 +23,8 @@ type RPCServer struct {
config *service.Config config *service.Config
// listener is used to serve HTTP. // listener is used to serve HTTP.
listener net.Listener listener net.Listener
// stopChan is used to stop the listener goroutine
stopChan chan struct{}
// debugger is a debugger service. // debugger is a debugger service.
debugger *debugger.Debugger debugger *debugger.Debugger
} }
@ -38,13 +40,22 @@ func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
&RPCServer{ &RPCServer{
config: config, config: config,
listener: config.Listener, listener: config.Listener,
stopChan: make(chan struct{}),
}, },
} }
} }
// Stop detaches from the debugger and waits for it to stop. // Stop detaches from the debugger and waits for it to stop.
func (s *ServerImpl) Stop(kill bool) error { func (s *ServerImpl) Stop(kill bool) error {
return s.s.debugger.Detach(kill) 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 // Run starts a debugger and exposes it with an HTTP server. The debugger
@ -60,16 +71,27 @@ func (s *ServerImpl) Run() error {
return err return err
} }
go func() { rpcs := grpc.NewServer()
c, err := s.s.listener.Accept() rpcs.Register(s.s)
if err != nil {
panic(err)
}
defer s.s.listener.Close()
rpcs := grpc.NewServer() go func() {
rpcs.Register(s.s) defer s.s.listener.Close()
rpcs.ServeCodec(jsonrpc.NewServerCodec(c)) 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 return nil
} }