cmd,service: in non-headless mode use an in-memory connection
Replace the socket connection with an in-memory connection (created by net.Pipe) for non-headless uses of delve. This is faster and more secure. Fixes #1332
This commit is contained in:
parent
910f90c3c8
commit
ca0596724f
@ -317,6 +317,13 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
return 1
|
||||
}
|
||||
|
||||
if Headless {
|
||||
fmt.Fprintf(os.Stderr, "Warning: headless mode not supported with trace\n")
|
||||
}
|
||||
if AcceptMulti {
|
||||
fmt.Fprintf(os.Stderr, "Warning: accept multiclient mode not supported with trace")
|
||||
}
|
||||
|
||||
debugname, err := filepath.Abs(cmd.Flag("output").Value.String())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
@ -343,12 +350,9 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
|
||||
processArgs = append([]string{debugname}, targetArgs...)
|
||||
}
|
||||
// Make a TCP listener
|
||||
listener, err := net.Listen("tcp", Addr)
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't start listener: %s\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
// Make a local in-memory connection that client and server use to communicate
|
||||
listener, clientConn := service.ListenerPipe()
|
||||
defer listener.Close()
|
||||
|
||||
// Create and start a debug server
|
||||
@ -364,7 +368,7 @@ func traceCmd(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
client := rpc2.NewClient(listener.Addr().String())
|
||||
client := rpc2.NewClientFromConn(clientConn)
|
||||
funcs, err := client.ListFunctions(regexp)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
@ -430,7 +434,7 @@ func connectCmd(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprint(os.Stderr, "An empty address was provided. You must provide an address as the first argument.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(connect(addr, conf, executingOther))
|
||||
os.Exit(connect(addr, nil, conf, executingOther))
|
||||
}
|
||||
|
||||
func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
|
||||
@ -440,9 +444,14 @@ func splitArgs(cmd *cobra.Command, args []string) ([]string, []string) {
|
||||
return args, []string{}
|
||||
}
|
||||
|
||||
func connect(addr string, conf *config.Config, kind executeKind) int {
|
||||
func connect(addr string, clientConn net.Conn, conf *config.Config, kind executeKind) int {
|
||||
// Create and start a terminal - attach to running instance
|
||||
client := rpc2.NewClient(addr)
|
||||
var client *rpc2.RPCClient
|
||||
if clientConn != nil {
|
||||
client = rpc2.NewClientFromConn(clientConn)
|
||||
} else {
|
||||
client = rpc2.NewClient(addr)
|
||||
}
|
||||
if client.Recorded() && (kind == executingGeneratedFile || kind == executingGeneratedTest) {
|
||||
// When using the rr backend remove the trace directory if we built the
|
||||
// executable
|
||||
@ -474,14 +483,6 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
return 1
|
||||
}
|
||||
|
||||
// Make a TCP listener
|
||||
listener, err := net.Listen("tcp", Addr)
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't start listener: %s\n", err)
|
||||
return 1
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
if Headless && (InitFile != "") {
|
||||
fmt.Fprint(os.Stderr, "Warning: init file ignored\n")
|
||||
}
|
||||
@ -493,6 +494,22 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
AcceptMulti = false
|
||||
}
|
||||
|
||||
var listener net.Listener
|
||||
var clientConn net.Conn
|
||||
var err error
|
||||
|
||||
// Make a TCP listener
|
||||
if Headless {
|
||||
listener, err = net.Listen("tcp", Addr)
|
||||
} else {
|
||||
listener, clientConn = service.ListenerPipe()
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("couldn't start listener: %s\n", err)
|
||||
return 1
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
var server interface {
|
||||
Run() error
|
||||
Stop() error
|
||||
@ -554,7 +571,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
return status
|
||||
}
|
||||
|
||||
return connect(listener.Addr().String(), conf, kind)
|
||||
return connect(listener.Addr().String(), clientConn, conf, kind)
|
||||
}
|
||||
|
||||
func optflags(args []string) []string {
|
||||
|
60
service/listenerpipe.go
Normal file
60
service/listenerpipe.go
Normal file
@ -0,0 +1,60 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ListenerPipe returns a full-duplex in-memory connection, like net.Pipe.
|
||||
// Unlike net.Pipe one end of the connection is returned as an object
|
||||
// satisfying the net.Listener interface.
|
||||
// The first call to the Accept method of this object will return a net.Conn
|
||||
// connected to the other net.Conn returned by ListenerPipe.
|
||||
// Any subsequent calls to Accept will block until the listener is closed.
|
||||
func ListenerPipe() (net.Listener, net.Conn) {
|
||||
conn0, conn1 := net.Pipe()
|
||||
return &preconnectedListener{conn: conn0, closech: make(chan struct{})}, conn1
|
||||
}
|
||||
|
||||
// preconnectedListener satisfies the net.Listener interface by accepting a
|
||||
// single pre-established connection.
|
||||
// The first call to Accept will return the conn field, any subsequent call
|
||||
// will block until the listener is closed.
|
||||
type preconnectedListener struct {
|
||||
accepted bool
|
||||
conn net.Conn
|
||||
closech chan struct{}
|
||||
closeMu sync.Mutex
|
||||
acceptMu sync.Mutex
|
||||
}
|
||||
|
||||
// Accept returns the pre-established connection the first time it's called,
|
||||
// it blocks until the listener is closed on every subsequent call.
|
||||
func (l *preconnectedListener) Accept() (net.Conn, error) {
|
||||
l.acceptMu.Lock()
|
||||
defer l.acceptMu.Unlock()
|
||||
if !l.accepted {
|
||||
l.accepted = true
|
||||
return l.conn, nil
|
||||
}
|
||||
<-l.closech
|
||||
return nil, errors.New("accept failed: listener closed")
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
func (l *preconnectedListener) Close() error {
|
||||
l.closeMu.Lock()
|
||||
defer l.closeMu.Unlock()
|
||||
if l.closech == nil {
|
||||
return nil
|
||||
}
|
||||
close(l.closech)
|
||||
l.closech = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *preconnectedListener) Addr() net.Addr {
|
||||
return l.conn.LocalAddr()
|
||||
}
|
@ -3,6 +3,7 @@ package rpc2
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"time"
|
||||
@ -13,7 +14,6 @@ import (
|
||||
|
||||
// Client is a RPC service.Client.
|
||||
type RPCClient struct {
|
||||
addr string
|
||||
client *rpc.Client
|
||||
|
||||
retValLoadCfg *api.LoadConfig
|
||||
@ -28,11 +28,20 @@ func NewClient(addr string) *RPCClient {
|
||||
if err != nil {
|
||||
log.Fatal("dialing:", err)
|
||||
}
|
||||
c := &RPCClient{addr: addr, client: client}
|
||||
return newFromRPCClient(client)
|
||||
}
|
||||
|
||||
func newFromRPCClient(client *rpc.Client) *RPCClient {
|
||||
c := &RPCClient{client: client}
|
||||
c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{})
|
||||
return c
|
||||
}
|
||||
|
||||
// NewClientFromConn creates a new RPCClient from the given connection.
|
||||
func NewClientFromConn(conn net.Conn) *RPCClient {
|
||||
return newFromRPCClient(jsonrpc.NewClient(conn))
|
||||
}
|
||||
|
||||
func (c *RPCClient) ProcessPid() int {
|
||||
out := new(ProcessPidOut)
|
||||
c.call("ProcessPid", ProcessPidIn{}, out)
|
||||
|
@ -41,11 +41,7 @@ func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
||||
if testBackend == "rr" {
|
||||
protest.MustHaveRecordingAllowed(t)
|
||||
}
|
||||
listener, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't start listener: %s\n", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
listener, clientConn := service.ListenerPipe()
|
||||
server := rpccommon.NewServer(&service.Config{
|
||||
Listener: listener,
|
||||
ProcessArgs: []string{protest.BuildFixture(name, 0).Path},
|
||||
@ -54,7 +50,7 @@ func withTestClient2(name string, t *testing.T, fn func(c service.Client)) {
|
||||
if err := server.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := rpc2.NewClient(listener.Addr().String())
|
||||
client := rpc2.NewClientFromConn(clientConn)
|
||||
defer func() {
|
||||
dir, _ := client.TraceDirectory()
|
||||
client.Detach(true)
|
||||
|
Loading…
Reference in New Issue
Block a user