service/dap: misc remote attach improvements (#2778)
Co-authored-by: Polina Sokolova <polinasok@users.noreply.github.com>
This commit is contained in:
parent
b8a9ae26f6
commit
d0898e4de1
@ -750,9 +750,9 @@ func TestRemoteDAPClient(t *testing.T) {
|
|||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeDAPRemoteMultiClient(t *testing.T, c *daptest.Client) {
|
func closeDAPRemoteMultiClient(t *testing.T, c *daptest.Client, expectStatus string) {
|
||||||
c.DisconnectRequest()
|
c.DisconnectRequest()
|
||||||
c.ExpectOutputEventClosingClient(t)
|
c.ExpectOutputEventClosingClient(t, expectStatus)
|
||||||
c.ExpectDisconnectResponse(t)
|
c.ExpectDisconnectResponse(t)
|
||||||
c.ExpectTerminatedEvent(t)
|
c.ExpectTerminatedEvent(t)
|
||||||
c.Close()
|
c.Close()
|
||||||
@ -787,6 +787,11 @@ func TestRemoteDAPClientMulti(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Client 0 connects but with the wrong attach request
|
||||||
|
dapclient0 := daptest.NewClient(listenAddr)
|
||||||
|
dapclient0.AttachRequest(map[string]interface{}{"mode": "local"})
|
||||||
|
dapclient0.ExpectErrorResponse(t)
|
||||||
|
|
||||||
// Client 1 connects and continues to main.main
|
// Client 1 connects and continues to main.main
|
||||||
dapclient := newDAPRemoteClient(t, listenAddr)
|
dapclient := newDAPRemoteClient(t, listenAddr)
|
||||||
dapclient.SetFunctionBreakpointsRequest([]godap.FunctionBreakpoint{{Name: "main.main"}})
|
dapclient.SetFunctionBreakpointsRequest([]godap.FunctionBreakpoint{{Name: "main.main"}})
|
||||||
@ -795,7 +800,7 @@ func TestRemoteDAPClientMulti(t *testing.T) {
|
|||||||
dapclient.ExpectContinueResponse(t)
|
dapclient.ExpectContinueResponse(t)
|
||||||
dapclient.ExpectStoppedEvent(t)
|
dapclient.ExpectStoppedEvent(t)
|
||||||
dapclient.CheckStopLocation(t, 1, "main.main", 5)
|
dapclient.CheckStopLocation(t, 1, "main.main", 5)
|
||||||
closeDAPRemoteMultiClient(t, dapclient)
|
closeDAPRemoteMultiClient(t, dapclient, "halted")
|
||||||
|
|
||||||
// Client 2 reconnects at main.main and continues to process exit
|
// Client 2 reconnects at main.main and continues to process exit
|
||||||
dapclient2 := newDAPRemoteClient(t, listenAddr)
|
dapclient2 := newDAPRemoteClient(t, listenAddr)
|
||||||
@ -803,13 +808,13 @@ func TestRemoteDAPClientMulti(t *testing.T) {
|
|||||||
dapclient2.ContinueRequest(1)
|
dapclient2.ContinueRequest(1)
|
||||||
dapclient2.ExpectContinueResponse(t)
|
dapclient2.ExpectContinueResponse(t)
|
||||||
dapclient2.ExpectTerminatedEvent(t)
|
dapclient2.ExpectTerminatedEvent(t)
|
||||||
closeDAPRemoteMultiClient(t, dapclient2)
|
closeDAPRemoteMultiClient(t, dapclient2, "exited")
|
||||||
|
|
||||||
// Attach to exited processs is an error
|
// Attach to exited processs is an error
|
||||||
dapclient3 := daptest.NewClient(listenAddr)
|
dapclient3 := daptest.NewClient(listenAddr)
|
||||||
dapclient3.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
|
dapclient3.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
|
||||||
dapclient3.ExpectErrorResponseWith(t, dap.FailedToAttach, `Process \d+ has exited with status 0`, true)
|
dapclient3.ExpectErrorResponseWith(t, dap.FailedToAttach, `Process \d+ has exited with status 0`, true)
|
||||||
closeDAPRemoteMultiClient(t, dapclient3)
|
closeDAPRemoteMultiClient(t, dapclient3, "exited")
|
||||||
|
|
||||||
// But rpc clients can still connect and restart
|
// But rpc clients can still connect and restart
|
||||||
rpcclient := rpc2.NewClient(listenAddr)
|
rpcclient := rpc2.NewClient(listenAddr)
|
||||||
|
|||||||
@ -178,9 +178,11 @@ func (c *Client) ExpectOutputEventTerminating(t *testing.T) *dap.OutputEvent {
|
|||||||
return c.ExpectOutputEventRegex(t, `Terminating process [0-9]+\n`)
|
return c.ExpectOutputEventRegex(t, `Terminating process [0-9]+\n`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) ExpectOutputEventClosingClient(t *testing.T) *dap.OutputEvent {
|
const ClosingClient = "Closing client session, but leaving multi-client DAP server at .+:[0-9]+ with debuggee %s\n"
|
||||||
|
|
||||||
|
func (c *Client) ExpectOutputEventClosingClient(t *testing.T, status string) *dap.OutputEvent {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return c.ExpectOutputEventRegex(t, `Closing client session, but leaving multi-client DAP server running at .+:[0-9]+\n`)
|
return c.ExpectOutputEventRegex(t, fmt.Sprintf(ClosingClient, status))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) CheckStopLocation(t *testing.T, thread int, name string, line int) {
|
func (c *Client) CheckStopLocation(t *testing.T, thread int, name string, line int) {
|
||||||
|
|||||||
@ -491,6 +491,16 @@ func (s *Server) RunWithClient(conn net.Conn) {
|
|||||||
go s.runSession(conn)
|
go s.runSession(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) address() string {
|
||||||
|
if s.config.Listener != nil {
|
||||||
|
return s.config.Listener.Addr().String()
|
||||||
|
}
|
||||||
|
if netconn, ok := s.conn.(net.Conn); ok {
|
||||||
|
return netconn.LocalAddr().String()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// ServeDAPCodec reads and decodes requests from the client
|
// ServeDAPCodec reads and decodes requests from the client
|
||||||
// until it encounters an error or EOF, when it sends
|
// until it encounters an error or EOF, when it sends
|
||||||
// a disconnect signal and returns.
|
// a disconnect signal and returns.
|
||||||
@ -934,8 +944,8 @@ func cleanExeName(name string) string {
|
|||||||
func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
func (s *Session) onLaunchRequest(request *dap.LaunchRequest) {
|
||||||
var err error
|
var err error
|
||||||
if s.debugger != nil {
|
if s.debugger != nil {
|
||||||
s.sendShowUserErrorResponse(request.Request, FailedToLaunch,
|
s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch",
|
||||||
"Failed to launch", "debugger already started - use remote attach to connect to a server with an active debug session")
|
fmt.Sprintf("debug session already in progress at %s - use remote attach mode to connect to a server with an active debug session", s.address()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1178,7 +1188,13 @@ func (s *Session) onDisconnectRequest(request *dap.DisconnectRequest) {
|
|||||||
// This is a multi-use server/debugger, so a disconnect request that doesn't
|
// This is a multi-use server/debugger, so a disconnect request that doesn't
|
||||||
// terminate the debuggee should clean up only the client connection and pointer to debugger,
|
// terminate the debuggee should clean up only the client connection and pointer to debugger,
|
||||||
// but not the entire server.
|
// but not the entire server.
|
||||||
s.logToConsole("Closing client session, but leaving multi-client DAP server running at " + s.config.Listener.Addr().String())
|
status := "halted"
|
||||||
|
if s.isRunningCmd() {
|
||||||
|
status = "running"
|
||||||
|
} else if s, err := s.debugger.State(false); processExited(s, err) {
|
||||||
|
status = "exited"
|
||||||
|
}
|
||||||
|
s.logToConsole(fmt.Sprintf("Closing client session, but leaving multi-client DAP server at %s with debuggee %s", s.config.Listener.Addr().String(), status))
|
||||||
s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
|
s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)})
|
||||||
s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")})
|
s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")})
|
||||||
s.conn.Close()
|
s.conn.Close()
|
||||||
@ -1752,7 +1768,8 @@ func (s *Session) onAttachRequest(request *dap.AttachRequest) {
|
|||||||
if s.debugger != nil {
|
if s.debugger != nil {
|
||||||
s.sendShowUserErrorResponse(
|
s.sendShowUserErrorResponse(
|
||||||
request.Request, FailedToAttach,
|
request.Request, FailedToAttach,
|
||||||
"Failed to attach", "debugger already started - use remote mode to connect")
|
"Failed to attach",
|
||||||
|
fmt.Sprintf("debug session already in progress at %s - use remote mode to connect to a server with an active debug session", s.address()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if args.ProcessID == 0 {
|
if args.ProcessID == 0 {
|
||||||
|
|||||||
@ -6358,10 +6358,10 @@ func TestAttachRemoteToRunningTargetContinueOnEntry(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestMultiClient tests that that remote attach doesn't take down
|
// TestAttachRemoteMultiClientDisconnect tests that that remote attach doesn't take down
|
||||||
// the server in multi-client mode unless terminateDebugee is explicitely set.
|
// the server in multi-client mode unless terminateDebugee is explicitely set.
|
||||||
func TestAttachRemoteMultiClient(t *testing.T) {
|
func TestAttachRemoteMultiClientDisconnect(t *testing.T) {
|
||||||
closingClientSessionOnly := "Closing client session, but leaving multi-client DAP server running at"
|
closingClientSessionOnly := fmt.Sprintf(daptest.ClosingClient, "halted")
|
||||||
detachingAndTerminating := "Detaching and terminating target process"
|
detachingAndTerminating := "Detaching and terminating target process"
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -6441,18 +6441,18 @@ func TestLaunchAttachErrorWhenDebugInProgress(t *testing.T) {
|
|||||||
// Both launch and attach requests should go through for additional error checking
|
// Both launch and attach requests should go through for additional error checking
|
||||||
client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 100})
|
client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 100})
|
||||||
er := client.ExpectVisibleErrorResponse(t)
|
er := client.ExpectVisibleErrorResponse(t)
|
||||||
msg := "Failed to attach: debugger already started - use remote mode to connect"
|
msgRe, _ := regexp.Compile("Failed to attach: debug session already in progress at [0-9]+:[0-9]+ - use remote mode to connect to a server with an active debug session")
|
||||||
if er.Body.Error.Id != FailedToAttach || er.Body.Error.Format != msg {
|
if er.Body.Error.Id != FailedToAttach || msgRe.MatchString(er.Body.Error.Format) {
|
||||||
t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToAttach, msg)
|
t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToAttach, msgRe)
|
||||||
}
|
}
|
||||||
tests := []string{"debug", "test", "exec", "replay", "core"}
|
tests := []string{"debug", "test", "exec", "replay", "core"}
|
||||||
for _, mode := range tests {
|
for _, mode := range tests {
|
||||||
t.Run(mode, func(t *testing.T) {
|
t.Run(mode, func(t *testing.T) {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{"mode": mode})
|
client.LaunchRequestWithArgs(map[string]interface{}{"mode": mode})
|
||||||
er := client.ExpectVisibleErrorResponse(t)
|
er := client.ExpectVisibleErrorResponse(t)
|
||||||
msg := "Failed to launch: debugger already started - use remote attach to connect to a server with an active debug session"
|
msgRe, _ := regexp.Compile("Failed to launch: debug session already in progress at [0-9]+:[0-9]+ - use remote attach mode to connect to a server with an active debug session")
|
||||||
if er.Body.Error.Id != FailedToLaunch || er.Body.Error.Format != msg {
|
if er.Body.Error.Id != FailedToLaunch || msgRe.MatchString(er.Body.Error.Format) {
|
||||||
t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToLaunch, msg)
|
t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToLaunch, msgRe)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,6 +195,7 @@ func (m *SubstitutePath) UnmarshalJSON(data []byte) error {
|
|||||||
type AttachConfig struct {
|
type AttachConfig struct {
|
||||||
// Acceptable values are:
|
// Acceptable values are:
|
||||||
// "local": attaches to the local process with the given ProcessID.
|
// "local": attaches to the local process with the given ProcessID.
|
||||||
|
// "remote": expects the debugger to already be running to "attach" to an in-progress debug session.
|
||||||
//
|
//
|
||||||
// Default is "local".
|
// Default is "local".
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user