service/dap: clarify handling of relative program path (#2653)

* service/dap: add test verifying handling of relative program path

* Add exec test, log build dir and document in --help

Co-authored-by: Polina Sokolova <polinasok@users.noreply.github.com>
This commit is contained in:
polinasok
2021-08-23 11:27:49 -07:00
committed by GitHub
parent d76181bba5
commit 7cdf48605b
4 changed files with 63 additions and 24 deletions

View File

@ -7,13 +7,15 @@
[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
The server is always headless and requires a DAP client like vscode to connect and request a binary
to be launched or process to be attached to. The following modes are supported:
to be launched or process to be attached to. The following modes can be specified via client's launch config:
- launch + exec (executes precompiled binary, like 'dlv exec')
- launch + debug (builds and launches, like 'dlv debug')
- launch + test (builds and tests, like 'dlv test')
- launch + replay (replays an rr trace, like 'dlv replay')
- launch + core (replays a core dump file, like 'dlv core')
- attach + local (attaches to a running process, like 'dlv attach')
Program and output binary paths will be interpreted relative to dlv's working directory.
The server does not yet accept multiple client connections (--accept-multiclient).
While --continue is not supported, stopOnEntry launch/attach attribute can be used to control if
execution is resumed at the start of the debug session.

View File

@ -180,13 +180,15 @@ option to let the process continue or kill it.
Long: `[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
The server is always headless and requires a DAP client like vscode to connect and request a binary
to be launched or process to be attached to. The following modes are supported:
to be launched or process to be attached to. The following modes can be specified via client's launch config:
- launch + exec (executes precompiled binary, like 'dlv exec')
- launch + debug (builds and launches, like 'dlv debug')
- launch + test (builds and tests, like 'dlv test')
- launch + replay (replays an rr trace, like 'dlv replay')
- launch + core (replays a core dump file, like 'dlv core')
- attach + local (attaches to a running process, like 'dlv attach')
Program and output binary paths will be interpreted relative to dlv's working directory.
The server does not yet accept multiple client connections (--accept-multiclient).
While --continue is not supported, stopOnEntry launch/attach attribute can be used to control if
execution is resumed at the start of the debug session.`,

View File

@ -864,8 +864,8 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
var cmd string
var out []byte
// Log debug binary build
s.log.Debugf("building binary '%s' from '%s' with flags '%v'", debugbinary, program, buildFlags)
wd, _ := os.Getwd()
s.log.Debugf("building program '%s' in '%s' with flags '%v'", program, wd, buildFlags)
switch mode {
case "debug":
cmd, out, err = gobuild.GoBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
@ -936,7 +936,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
s.config.Debugger.WorkingDir = wdParsed
}
s.log.Debugf("running program in %s\n", s.config.Debugger.WorkingDir)
s.log.Debugf("running binary '%s' in '%s'", program, s.config.Debugger.WorkingDir)
if noDebug, ok := request.Arguments["noDebug"].(bool); ok && noDebug {
s.mu.Lock()
cmd, err := s.newNoDebugProcess(program, targetArgs, s.config.Debugger.WorkingDir)

View File

@ -3978,8 +3978,9 @@ type onBreakpoint struct {
// so the test author has full control of its arguments.
// Note that he rest of the test sequence assumes that
// stopOnEntry is false.
// source - source file path, needed to set breakpoints, "" if none to be set.
// breakpoints - list of lines, where breakpoints are to be set
// onBreakpoints - list of test sequences to execute at each of the set breakpoints.
// onBPs - list of test sequences to execute at each of the set breakpoints.
func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) {
client.InitializeRequest()
client.ExpectInitializeResponseAndCapabilities(t)
@ -3994,8 +3995,10 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
panic("expected launch or attach command")
}
client.SetBreakpointsRequest(source, breakpoints)
client.ExpectSetBreakpointsResponse(t)
if source != "" {
client.SetBreakpointsRequest(source, breakpoints)
client.ExpectSetBreakpointsResponse(t)
}
// Skip no-op setExceptionBreakpoints
@ -4045,8 +4048,8 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
// runDebugSession is a helper for executing the standard init and shutdown
// sequences for a program that does not stop on entry
// while specifying unique launch criteria via parameters.
func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string) {
runDebugSessionWithBPs(t, client, cmd, cmdRequest, source, nil, nil)
func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func()) {
runDebugSessionWithBPs(t, client, cmd, cmdRequest, "", nil, nil)
}
func TestLaunchDebugRequest(t *testing.T) {
@ -4061,7 +4064,7 @@ func TestLaunchDebugRequest(t *testing.T) {
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": fixture.Source, "output": tmpBin})
}, fixture.Source)
})
})
// Wait for the test to finish to capture all stderr
time.Sleep(100 * time.Millisecond)
@ -4098,13 +4101,13 @@ func TestLaunchRequestDefaults(t *testing.T) {
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin"})
}, fixture.Source)
})
})
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin"})
}, fixture.Source)
})
})
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSession(t, client, "launch", func() {
@ -4112,7 +4115,7 @@ func TestLaunchRequestDefaults(t *testing.T) {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": fixture.Source})
// writes to default output dir __debug_bin
}, fixture.Source)
})
})
// if noDebug is not a bool, behave as if it is the default value (false).
@ -4120,7 +4123,7 @@ func TestLaunchRequestDefaults(t *testing.T) {
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": fixture.Source, "noDebug": "true"})
}, fixture.Source)
})
})
}
@ -4219,16 +4222,48 @@ func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) {
})
}
func TestLaunchTestRequest(t *testing.T) {
func TestLaunchRequestWithRelativeBuildPath(t *testing.T) {
client := startDapServer(t)
defer client.Close() // will trigger Stop()
fixdir := protest.FindFixturesDir()
if filepath.IsAbs(fixdir) {
t.Fatal("this test requires relative program path")
}
program := filepath.Join(protest.FindFixturesDir(), "buildtest")
// Use different working dir for target than dlv.
// Program path will be interpreted relative to dlv's.
dlvwd, _ := os.Getwd()
runDebugSession(t, client, "launch", func() {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd)})
})
}
func TestLaunchRequestWithRelativeExecPath(t *testing.T) {
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
symlink := "./__thisexe"
err := os.Symlink(fixture.Path, symlink)
defer os.Remove(symlink)
if err != nil {
t.Fatal("unable to create relative symlink:", err)
}
runDebugSession(t, client, "launch", func() {
// We reuse the harness that builds, but ignore the built binary,
// only relying on the source to be built in response to LaunchRequest.
fixtures := protest.FindFixturesDir()
testdir, _ := filepath.Abs(filepath.Join(fixtures, "buildtest"))
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "test", "program": testdir, "output": "__mytestdir"})
}, fixture.Source)
"mode": "exec", "program": symlink})
})
})
}
func TestLaunchTestRequest(t *testing.T) {
client := startDapServer(t)
defer client.Close() // will trigger Stop()
runDebugSession(t, client, "launch", func() {
fixtures := protest.FindFixturesDir()
testdir, _ := filepath.Abs(filepath.Join(fixtures, "buildtest"))
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "test", "program": testdir, "output": "__mytestdir"})
})
}
@ -4242,7 +4277,7 @@ func TestLaunchRequestWithArgs(t *testing.T) {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "exec", "program": fixture.Path,
"args": []string{"test", "pass flag"}})
}, fixture.Source)
})
})
}
@ -4258,7 +4293,7 @@ func TestLaunchRequestWithBuildFlags(t *testing.T) {
client.LaunchRequestWithArgs(map[string]interface{}{
"mode": "debug", "program": fixture.Source, "output": "__mybin",
"buildFlags": "-ldflags '-X main.Hello=World'"})
}, fixture.Source)
})
})
}