mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 17:56:45 +08:00
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:
@ -7,13 +7,15 @@
|
|||||||
[EXPERIMENTAL] Starts a headless TCP server communicating via Debug Adaptor Protocol (DAP).
|
[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
|
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 + exec (executes precompiled binary, like 'dlv exec')
|
||||||
- launch + debug (builds and launches, like 'dlv debug')
|
- launch + debug (builds and launches, like 'dlv debug')
|
||||||
- launch + test (builds and tests, like 'dlv test')
|
- launch + test (builds and tests, like 'dlv test')
|
||||||
- launch + replay (replays an rr trace, like 'dlv replay')
|
- launch + replay (replays an rr trace, like 'dlv replay')
|
||||||
- launch + core (replays a core dump file, like 'dlv core')
|
- launch + core (replays a core dump file, like 'dlv core')
|
||||||
- attach + local (attaches to a running process, like 'dlv attach')
|
- 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).
|
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
|
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.
|
execution is resumed at the start of the debug session.
|
||||||
|
|||||||
@ -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).
|
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
|
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 + exec (executes precompiled binary, like 'dlv exec')
|
||||||
- launch + debug (builds and launches, like 'dlv debug')
|
- launch + debug (builds and launches, like 'dlv debug')
|
||||||
- launch + test (builds and tests, like 'dlv test')
|
- launch + test (builds and tests, like 'dlv test')
|
||||||
- launch + replay (replays an rr trace, like 'dlv replay')
|
- launch + replay (replays an rr trace, like 'dlv replay')
|
||||||
- launch + core (replays a core dump file, like 'dlv core')
|
- launch + core (replays a core dump file, like 'dlv core')
|
||||||
- attach + local (attaches to a running process, like 'dlv attach')
|
- 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).
|
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
|
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.`,
|
execution is resumed at the start of the debug session.`,
|
||||||
|
|||||||
@ -864,8 +864,8 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
|
|||||||
|
|
||||||
var cmd string
|
var cmd string
|
||||||
var out []byte
|
var out []byte
|
||||||
// Log debug binary build
|
wd, _ := os.Getwd()
|
||||||
s.log.Debugf("building binary '%s' from '%s' with flags '%v'", debugbinary, program, buildFlags)
|
s.log.Debugf("building program '%s' in '%s' with flags '%v'", program, wd, buildFlags)
|
||||||
switch mode {
|
switch mode {
|
||||||
case "debug":
|
case "debug":
|
||||||
cmd, out, err = gobuild.GoBuildCombinedOutput(debugbinary, []string{program}, buildFlags)
|
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.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 {
|
if noDebug, ok := request.Arguments["noDebug"].(bool); ok && noDebug {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
cmd, err := s.newNoDebugProcess(program, targetArgs, s.config.Debugger.WorkingDir)
|
cmd, err := s.newNoDebugProcess(program, targetArgs, s.config.Debugger.WorkingDir)
|
||||||
|
|||||||
@ -3978,8 +3978,9 @@ type onBreakpoint struct {
|
|||||||
// so the test author has full control of its arguments.
|
// so the test author has full control of its arguments.
|
||||||
// Note that he rest of the test sequence assumes that
|
// Note that he rest of the test sequence assumes that
|
||||||
// stopOnEntry is false.
|
// 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
|
// 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) {
|
func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) {
|
||||||
client.InitializeRequest()
|
client.InitializeRequest()
|
||||||
client.ExpectInitializeResponseAndCapabilities(t)
|
client.ExpectInitializeResponseAndCapabilities(t)
|
||||||
@ -3994,8 +3995,10 @@ func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cm
|
|||||||
panic("expected launch or attach command")
|
panic("expected launch or attach command")
|
||||||
}
|
}
|
||||||
|
|
||||||
client.SetBreakpointsRequest(source, breakpoints)
|
if source != "" {
|
||||||
client.ExpectSetBreakpointsResponse(t)
|
client.SetBreakpointsRequest(source, breakpoints)
|
||||||
|
client.ExpectSetBreakpointsResponse(t)
|
||||||
|
}
|
||||||
|
|
||||||
// Skip no-op setExceptionBreakpoints
|
// 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
|
// runDebugSession is a helper for executing the standard init and shutdown
|
||||||
// sequences for a program that does not stop on entry
|
// sequences for a program that does not stop on entry
|
||||||
// while specifying unique launch criteria via parameters.
|
// while specifying unique launch criteria via parameters.
|
||||||
func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string) {
|
func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func()) {
|
||||||
runDebugSessionWithBPs(t, client, cmd, cmdRequest, source, nil, nil)
|
runDebugSessionWithBPs(t, client, cmd, cmdRequest, "", nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLaunchDebugRequest(t *testing.T) {
|
func TestLaunchDebugRequest(t *testing.T) {
|
||||||
@ -4061,7 +4064,7 @@ func TestLaunchDebugRequest(t *testing.T) {
|
|||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": tmpBin})
|
"mode": "debug", "program": fixture.Source, "output": tmpBin})
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
// Wait for the test to finish to capture all stderr
|
// Wait for the test to finish to capture all stderr
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@ -4098,13 +4101,13 @@ func TestLaunchRequestDefaults(t *testing.T) {
|
|||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin"})
|
"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin"})
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin"})
|
/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin"})
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSession(t, client, "launch", func() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
@ -4112,7 +4115,7 @@ func TestLaunchRequestDefaults(t *testing.T) {
|
|||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source})
|
"mode": "debug", "program": fixture.Source})
|
||||||
// writes to default output dir __debug_bin
|
// writes to default output dir __debug_bin
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// if noDebug is not a bool, behave as if it is the default value (false).
|
// 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() {
|
runDebugSession(t, client, "launch", func() {
|
||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "noDebug": "true"})
|
"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) {
|
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() {
|
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{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "test", "program": testdir, "output": "__mytestdir"})
|
"mode": "exec", "program": symlink})
|
||||||
}, fixture.Source)
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "exec", "program": fixture.Path,
|
"mode": "exec", "program": fixture.Path,
|
||||||
"args": []string{"test", "pass flag"}})
|
"args": []string{"test", "pass flag"}})
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4258,7 +4293,7 @@ func TestLaunchRequestWithBuildFlags(t *testing.T) {
|
|||||||
client.LaunchRequestWithArgs(map[string]interface{}{
|
client.LaunchRequestWithArgs(map[string]interface{}{
|
||||||
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
"mode": "debug", "program": fixture.Source, "output": "__mybin",
|
||||||
"buildFlags": "-ldflags '-X main.Hello=World'"})
|
"buildFlags": "-ldflags '-X main.Hello=World'"})
|
||||||
}, fixture.Source)
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user