Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

service/dap: support waitfor option for 'dap attach' only #3656

Merged
merged 1 commit into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions service/dap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1892,17 +1892,33 @@ func (s *Session) onAttachRequest(request *dap.AttachRequest) {
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
}
if args.ProcessID == 0 {
s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach",
"The 'processId' attribute is missing in debug configuration")
if args.AttachWaitFor != "" && args.ProcessID != 0 {
s.sendShowUserErrorResponse(
request.Request,
FailedToAttach,
"Failed to attach",
"'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time")
return
} else if args.AttachWaitFor != "" {
s.config.Debugger.AttachWaitFor = args.AttachWaitFor
// Keep the default value the same as waitfor-interval.
s.config.Debugger.AttachWaitForInterval = 1
s.config.log.Debugf("Waiting for a process with a name beginning with this prefix: %s", args.AttachWaitFor)
} else if args.ProcessID != 0 {
s.config.Debugger.AttachPid = args.ProcessID
s.config.log.Debugf("Attaching to pid %d", args.ProcessID)
} else {
s.sendShowUserErrorResponse(
request.Request,
FailedToAttach,
"Failed to attach",
"The 'processId' or 'waitFor' attribute is missing in debug configuration")
return
}
s.config.Debugger.AttachPid = args.ProcessID
if args.Backend == "" {
args.Backend = "default"
}
s.config.Debugger.Backend = args.Backend
s.config.log.Debugf("attaching to pid %d", args.ProcessID)
var err error
func() {
s.mu.Lock()
Expand Down
59 changes: 54 additions & 5 deletions service/dap/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5901,6 +5901,51 @@ func TestAttachRequest(t *testing.T) {
})
}

func TestAttachWaitForRequest(t *testing.T) {
if runtime.GOOS == "freebsd" {
// The value of /proc/[pid]/cmdline might be wrong on FreeBSD: zero or the same as the parent process.
t.Skip("test skipped on freebsd")
}
runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
cmd := exec.Command(fixture.Path)
stdout, err := cmd.StdoutPipe()
if err != nil {
t.Fatal(err)
}
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
// Wait for output.
// This will give the target process time to initialize the runtime before we attach,
// so we can rely on having goroutines when they are requested on attach.
scanOut := bufio.NewScanner(stdout)
scanOut.Scan()
if scanOut.Text() != "past main" {
t.Errorf("expected loopprog.go to output \"past main\"")
}

t.Logf("The process id of program %q is %d", cmd.Path, cmd.Process.Pid)

client.InitializeRequest()
client.ExpectInitializeResponseAndCapabilities(t)
client.AttachRequest(map[string]interface{}{
"mode": "local",
"waitFor": fixture.Path,
"stopOnEntry": true,
})
client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
client.ExpectInitializedEvent(t)
client.ExpectAttachResponse(t)
client.DisconnectRequestWithKillOption(true)
client.ExpectOutputEvent(t)
client.ExpectDisconnectResponse(t)
client.ExpectTerminatedEvent(t)

runtime.KeepAlive(stdout)
})
}

// Since we are in async mode while running, we might receive thee messages after pause request
// in either order.
func expectPauseResponseAndStoppedEvent(t *testing.T, client *daptest.Client) {
Expand Down Expand Up @@ -6621,24 +6666,28 @@ func TestBadAttachRequest(t *testing.T) {

client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error)
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: The 'processId' attribute is missing in debug configuration")
"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")

client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error)
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: The 'processId' attribute is missing in debug configuration")
"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")

// Bad "processId"
client.AttachRequest(map[string]interface{}{"mode": "local"})
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: The 'processId' attribute is missing in debug configuration")
"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")

client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil})
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: The 'processId' attribute is missing in debug configuration")
"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")

client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0})
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: The 'processId' attribute is missing in debug configuration")
"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")

client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "waitFor": "loopprog"})
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
"Failed to attach: 'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time")

client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"})
checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
Expand Down
6 changes: 5 additions & 1 deletion service/dap/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func (m *SubstitutePath) UnmarshalJSON(data []byte) error {
}

// AttachConfig is the collection of attach request attributes recognized by DAP implementation.
// 'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time.
type AttachConfig struct {
// Acceptable values are:
// "local": attaches to the local process with the given ProcessID.
Expand All @@ -245,9 +246,12 @@ type AttachConfig struct {
// Default is "local".
Mode string `json:"mode"`

// The numeric ID of the process to be debugged. Required and must not be 0.
// The numeric ID of the process to be debugged.
ProcessID int `json:"processId,omitempty"`

// Wait for a process with a name beginning with this prefix.
AttachWaitFor string `json:"waitFor,omitempty"`

LaunchAttachCommonConfig
}

Expand Down