Skip to content

Commit

Permalink
service/dap: Support local attach (go-delve#2260)
Browse files Browse the repository at this point in the history
* Support local attach

* Undo loopprog change

* Remove test check for system-specific error message

* Skip attach tests on freebasd

Co-authored-by: Polina Sokolova <polinasok@users.noreply.github.com>
  • Loading branch information
2 people authored and zhaixiaojuan committed Apr 25, 2021
1 parent 5becead commit 9c77689
Show file tree
Hide file tree
Showing 4 changed files with 367 additions and 58 deletions.
20 changes: 17 additions & 3 deletions service/dap/daptest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ func (c *Client) ExpectLaunchResponse(t *testing.T) *dap.LaunchResponse {
return c.expectReadProtocolMessage(t).(*dap.LaunchResponse)
}

func (c *Client) ExpectAttachResponse(t *testing.T) *dap.AttachResponse {
t.Helper()
return c.expectReadProtocolMessage(t).(*dap.AttachResponse)
}

func (c *Client) ExpectSetExceptionBreakpointsResponse(t *testing.T) *dap.SetExceptionBreakpointsResponse {
t.Helper()
return c.expectReadProtocolMessage(t).(*dap.SetExceptionBreakpointsResponse)
Expand Down Expand Up @@ -332,10 +337,11 @@ func (c *Client) LaunchRequestWithArgs(arguments map[string]interface{}) {
c.send(request)
}

// AttachRequest sends an 'attach' request.
func (c *Client) AttachRequest() {
// AttachRequest sends an 'attach' request with the specified
// arguments.
func (c *Client) AttachRequest(arguments map[string]interface{}) {
request := &dap.AttachRequest{Request: *c.newRequest("attach")}
// TODO(polina): populate meaningful arguments
request.Arguments = arguments
c.send(request)
}

Expand All @@ -345,6 +351,14 @@ func (c *Client) DisconnectRequest() {
c.send(request)
}

// DisconnectRequest sends a 'disconnect' request with an option to specify
// `terminateDebuggee`.
func (c *Client) DisconnectRequestWithKillOption(kill bool) {
request := &dap.DisconnectRequest{Request: *c.newRequest("disconnect")}
request.Arguments.TerminateDebuggee = kill
c.send(request)
}

// SetBreakpointsRequest sends a 'setBreakpoints' request.
func (c *Client) SetBreakpointsRequest(file string, lines []int) {
c.SetConditionalBreakpointsRequest(file, lines, nil)
Expand Down
2 changes: 1 addition & 1 deletion service/dap/error_ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const (
// Where applicable and for consistency only,
// values below are inspired the original vscode-go debug adaptor.
FailedToLaunch = 3000
FailedtoAttach = 3001
FailedToAttach = 3001
UnableToSetBreakpoints = 2002
UnableToDisplayThreads = 2003
UnableToProduceStackTrace = 2004
Expand Down
77 changes: 60 additions & 17 deletions service/dap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ func NewServer(config *service.Config) *Server {
}
}

// If user-specified options are provided via Launch/AttachRequest,
// we override the defaults for optional args.
func (s *Server) setLaunchAttachArgs(request dap.LaunchAttachRequest) {
stop, ok := request.GetArguments()["stopOnEntry"].(bool)
if ok {
s.args.stopOnEntry = stop
}
depth, ok := request.GetArguments()["stackTraceDepth"].(float64)
if ok && depth > 0 {
s.args.stackTraceDepth = int(depth)
}
globals, ok := request.GetArguments()["showGlobalVariables"].(bool)
if ok {
s.args.showGlobalVariables = globals
}
}

// Stop stops the DAP debugger service, closes the listener and the client
// connection. It shuts down the underlying debugger and kills the target
// process if it was launched by it. This method mustn't be called more than
Expand Down Expand Up @@ -233,7 +250,6 @@ func (s *Server) handleRequest(request dap.Message) {
s.onLaunchRequest(request)
case *dap.AttachRequest:
// Required
// TODO: implement this request in V0
s.onAttachRequest(request)
case *dap.DisconnectRequest:
// Required
Expand Down Expand Up @@ -384,6 +400,7 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
response.Body.SupportsConfigurationDoneRequest = true
response.Body.SupportsConditionalBreakpoints = true
response.Body.SupportsDelayedStackTraceLoading = true
response.Body.SupportTerminateDebuggee = true
// TODO(polina): support this to match vscode-go functionality
response.Body.SupportsSetVariable = false
// TODO(polina): support these requests in addition to vscode-go feature parity
Expand Down Expand Up @@ -465,19 +482,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
return
}

// If user-specified, overwrite the defaults for optional args.
stop, ok := request.Arguments["stopOnEntry"].(bool)
if ok {
s.args.stopOnEntry = stop
}
depth, ok := request.Arguments["stackTraceDepth"].(float64)
if ok && depth > 0 {
s.args.stackTraceDepth = int(depth)
}
globals, ok := request.Arguments["showGlobalVariables"].(bool)
if ok {
s.args.showGlobalVariables = globals
}
s.setLaunchAttachArgs(request)

var targetArgs []string
args, ok := request.Arguments["args"]
Expand Down Expand Up @@ -528,7 +533,14 @@ func (s *Server) onDisconnectRequest(request *dap.DisconnectRequest) {
if err != nil {
s.log.Error(err)
}
// We always kill launched programs
kill := s.config.Debugger.AttachPid == 0
// In case of attach, we leave the program
// running but default, which can be
// overridden by an explicit request to terminate.
if request.Arguments.TerminateDebuggee {
kill = true
}
err = s.debugger.Detach(kill)
if err != nil {
s.log.Error(err)
Expand Down Expand Up @@ -685,10 +697,41 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
s.send(response)
}

// onAttachRequest sends a not-yet-implemented error response.
// onAttachRequest handles 'attach' request.
// This is a mandatory request to support.
func (s *Server) onAttachRequest(request *dap.AttachRequest) { // TODO V0
s.sendNotYetImplementedErrorResponse(request.Request)
func (s *Server) onAttachRequest(request *dap.AttachRequest) {
mode, ok := request.Arguments["mode"]
if !ok || mode == "" {
mode = "local"
}
if mode == "local" {
pid, ok := request.Arguments["processId"].(float64)
if !ok || pid == 0 {
s.sendErrorResponse(request.Request,
FailedToAttach, "Failed to attach",
"The 'processId' attribute is missing in debug configuration")
return
}
s.config.Debugger.AttachPid = int(pid)
s.setLaunchAttachArgs(request)
var err error
if s.debugger, err = debugger.New(&s.config.Debugger, nil); err != nil {
s.sendErrorResponse(request.Request,
FailedToAttach, "Failed to attach", err.Error())
return
}
} else {
// TODO(polina): support 'remote' mode with 'host' and 'port'
s.sendErrorResponse(request.Request,
FailedToAttach, "Failed to attach",
fmt.Sprintf("Unsupported 'mode' value %q in debug configuration", mode))
return
}
// Notify the client that the debugger is ready to start accepting
// configuration requests for setting breakpoints, etc. The client
// will end the configuration sequence with 'configurationDone'.
s.send(&dap.InitializedEvent{Event: *newEvent("initialized")})
s.send(&dap.AttachResponse{Response: *newResponse(request.Request)})
}

// onNextRequest handles 'next' request.
Expand Down
Loading

0 comments on commit 9c77689

Please sign in to comment.