2018-04-18 07:36:40 +00:00
|
|
|
package main_test
|
2016-09-25 15:26:59 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2017-07-26 18:51:44 +00:00
|
|
|
"bytes"
|
2017-02-10 14:11:40 +00:00
|
|
|
"flag"
|
2017-07-26 18:51:44 +00:00
|
|
|
"fmt"
|
2018-11-20 15:13:09 +00:00
|
|
|
"go/ast"
|
|
|
|
"go/token"
|
|
|
|
"go/types"
|
2020-02-28 17:48:59 +00:00
|
|
|
"io"
|
2017-07-26 18:51:44 +00:00
|
|
|
"io/ioutil"
|
2016-09-25 15:26:59 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"runtime"
|
2018-11-20 15:13:09 +00:00
|
|
|
"strconv"
|
2017-03-13 17:59:34 +00:00
|
|
|
"strings"
|
2016-09-25 15:26:59 +00:00
|
|
|
"testing"
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
"time"
|
2016-09-25 15:26:59 +00:00
|
|
|
|
2019-01-04 18:39:25 +00:00
|
|
|
protest "github.com/go-delve/delve/pkg/proc/test"
|
|
|
|
"github.com/go-delve/delve/pkg/terminal"
|
2020-02-28 17:48:59 +00:00
|
|
|
"github.com/go-delve/delve/service/dap/daptest"
|
2019-01-04 18:39:25 +00:00
|
|
|
"github.com/go-delve/delve/service/rpc2"
|
2018-11-20 15:13:09 +00:00
|
|
|
|
|
|
|
"golang.org/x/tools/go/packages"
|
2016-09-25 15:26:59 +00:00
|
|
|
)
|
|
|
|
|
2017-02-10 14:11:40 +00:00
|
|
|
var testBackend string
|
|
|
|
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
flag.StringVar(&testBackend, "backend", "", "selects backend")
|
|
|
|
flag.Parse()
|
|
|
|
if testBackend == "" {
|
|
|
|
testBackend = os.Getenv("PROCTEST")
|
|
|
|
if testBackend == "" {
|
|
|
|
testBackend = "native"
|
2018-11-12 22:52:13 +00:00
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
testBackend = "lldb"
|
|
|
|
}
|
2017-02-10 14:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|
|
|
|
|
2016-09-25 15:26:59 +00:00
|
|
|
func assertNoError(err error, t testing.TB, s string) {
|
|
|
|
if err != nil {
|
|
|
|
_, file, line, _ := runtime.Caller(1)
|
|
|
|
fname := filepath.Base(file)
|
|
|
|
t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 00:22:53 +00:00
|
|
|
func projectRoot() string {
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2017-03-13 17:59:34 +00:00
|
|
|
}
|
2018-11-28 18:22:11 +00:00
|
|
|
|
|
|
|
gopaths := strings.FieldsFunc(os.Getenv("GOPATH"), func(r rune) bool { return r == os.PathListSeparator })
|
|
|
|
for _, curpath := range gopaths {
|
|
|
|
// Detects "gopath mode" when GOPATH contains several paths ex. "d:\\dir\\gopath;f:\\dir\\gopath2"
|
|
|
|
if strings.Contains(wd, curpath) {
|
2019-01-04 18:39:25 +00:00
|
|
|
return filepath.Join(curpath, "src", "github.com", "go-delve", "delve")
|
2018-11-28 18:22:11 +00:00
|
|
|
}
|
2018-11-13 00:22:53 +00:00
|
|
|
}
|
2020-02-11 01:31:54 +00:00
|
|
|
val, err := exec.Command("go", "list", "-mod=", "-m", "-f", "{{ .Dir }}").Output()
|
2017-03-13 17:59:34 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err) // the Go tool was tested to work earlier
|
|
|
|
}
|
2018-11-13 00:22:53 +00:00
|
|
|
return strings.TrimSuffix(string(val), "\n")
|
2017-03-13 17:59:34 +00:00
|
|
|
}
|
|
|
|
|
2016-09-25 15:26:59 +00:00
|
|
|
func TestBuild(t *testing.T) {
|
2019-07-30 15:38:25 +00:00
|
|
|
const listenAddr = "127.0.0.1:40573"
|
2016-11-18 07:21:50 +00:00
|
|
|
var err error
|
2018-05-28 18:28:39 +00:00
|
|
|
|
2020-03-28 18:18:55 +00:00
|
|
|
cmd := exec.Command("go", "run", "_scripts/make.go", "build")
|
2018-11-13 00:22:53 +00:00
|
|
|
cmd.Dir = projectRoot()
|
2018-05-28 18:28:39 +00:00
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("makefile error: %v\noutput %s\n", err, string(out))
|
2016-11-18 07:21:50 +00:00
|
|
|
}
|
2018-05-28 18:28:39 +00:00
|
|
|
|
|
|
|
dlvbin := filepath.Join(cmd.Dir, "dlv")
|
2016-09-25 15:26:59 +00:00
|
|
|
defer os.Remove(dlvbin)
|
|
|
|
|
|
|
|
fixtures := protest.FindFixturesDir()
|
|
|
|
|
|
|
|
buildtestdir := filepath.Join(fixtures, "buildtest")
|
|
|
|
|
2018-05-28 18:28:39 +00:00
|
|
|
cmd = exec.Command(dlvbin, "debug", "--headless=true", "--listen="+listenAddr, "--api-version=2", "--backend="+testBackend, "--log", "--log-output=debugger,rpc")
|
2016-09-25 15:26:59 +00:00
|
|
|
cmd.Dir = buildtestdir
|
2018-06-14 18:12:11 +00:00
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
assertNoError(err, t, "stderr pipe")
|
2016-09-25 15:26:59 +00:00
|
|
|
cmd.Start()
|
|
|
|
|
2018-06-14 18:12:11 +00:00
|
|
|
scan := bufio.NewScanner(stderr)
|
2016-09-25 15:26:59 +00:00
|
|
|
// wait for the debugger to start
|
|
|
|
scan.Scan()
|
2018-11-12 22:52:13 +00:00
|
|
|
t.Log(scan.Text())
|
2016-09-25 15:26:59 +00:00
|
|
|
go func() {
|
|
|
|
for scan.Scan() {
|
2018-11-12 22:52:13 +00:00
|
|
|
t.Log(scan.Text())
|
2016-09-25 15:26:59 +00:00
|
|
|
// keep pipe empty
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
client := rpc2.NewClient(listenAddr)
|
|
|
|
state := <-client.Continue()
|
|
|
|
|
|
|
|
if !state.Exited {
|
|
|
|
t.Fatal("Program did not exit")
|
|
|
|
}
|
2017-06-26 18:45:13 +00:00
|
|
|
|
|
|
|
client.Detach(true)
|
|
|
|
cmd.Wait()
|
2016-09-25 15:26:59 +00:00
|
|
|
}
|
2017-07-26 18:51:44 +00:00
|
|
|
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout, stderr []byte) {
|
2017-07-26 18:51:44 +00:00
|
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
|
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
|
|
|
|
c := []string{dlvbin, "debug"}
|
2019-05-31 17:26:19 +00:00
|
|
|
debugbin := filepath.Join(buildtestdir, "__debug_bin")
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
if output != "" {
|
|
|
|
c = append(c, "--output", output)
|
|
|
|
if filepath.IsAbs(output) {
|
|
|
|
debugbin = output
|
|
|
|
} else {
|
|
|
|
debugbin = filepath.Join(buildtestdir, output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cmd := exec.Command(c[0], c[1:]...)
|
2017-07-26 18:51:44 +00:00
|
|
|
cmd.Dir = buildtestdir
|
|
|
|
stdin, err := cmd.StdinPipe()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
cmd.Stdout = &stdoutBuf
|
|
|
|
cmd.Stderr = &stderrBuf
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
|
2017-07-26 18:51:44 +00:00
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
|
|
|
|
// Give delve some time to compile and write the binary.
|
|
|
|
foundIt := false
|
|
|
|
for wait := 0; wait < 30; wait++ {
|
|
|
|
_, err = os.Stat(debugbin)
|
|
|
|
if err == nil {
|
|
|
|
foundIt = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
}
|
|
|
|
if !foundIt {
|
|
|
|
t.Errorf("running %q: file not created: %v", delveCmds, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range delveCmds {
|
2017-07-26 18:51:44 +00:00
|
|
|
fmt.Fprintf(stdin, "%s\n", c)
|
|
|
|
}
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
|
2017-07-26 18:51:44 +00:00
|
|
|
// ignore "dlv debug" command error, it returns
|
|
|
|
// errors even after successful debug session.
|
|
|
|
cmd.Wait()
|
|
|
|
stdout, stderr = stdoutBuf.Bytes(), stderrBuf.Bytes()
|
|
|
|
|
|
|
|
_, err = os.Stat(debugbin)
|
|
|
|
if err == nil {
|
2020-03-13 13:14:32 +00:00
|
|
|
if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
|
|
|
|
// Sometimes delve on Travis on Windows can't remove the built binary before
|
2017-12-17 15:06:24 +00:00
|
|
|
// exiting and gets an "Access is denied" error when trying.
|
2020-03-13 13:14:32 +00:00
|
|
|
// Just ignore it.
|
|
|
|
// See: https://travis-ci.com/go-delve/delve/jobs/296325131
|
|
|
|
return
|
2017-12-17 15:06:24 +00:00
|
|
|
}
|
2020-03-13 13:14:32 +00:00
|
|
|
t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
|
2017-07-26 18:51:44 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if !os.IsNotExist(err) {
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
t.Errorf("running %q: %v\nstdout is %q, stderr is %q", delveCmds, err, stdout, stderr)
|
2017-07-26 18:51:44 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
func getDlvBin(t *testing.T) (string, string) {
|
|
|
|
tmpdir, err := ioutil.TempDir("", "TestDlv")
|
2017-07-26 18:51:44 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
dlvbin := filepath.Join(tmpdir, "dlv.exe")
|
2019-01-04 18:39:25 +00:00
|
|
|
out, err := exec.Command("go", "build", "-o", dlvbin, "github.com/go-delve/delve/cmd/dlv").CombinedOutput()
|
2017-07-26 18:51:44 +00:00
|
|
|
if err != nil {
|
2019-01-04 18:39:25 +00:00
|
|
|
t.Fatalf("go build -o %v github.com/go-delve/delve/cmd/dlv: %v\n%s", dlvbin, err, string(out))
|
2017-07-26 18:51:44 +00:00
|
|
|
}
|
|
|
|
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
return dlvbin, tmpdir
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestOutput verifies that the debug executable is created in the correct path
|
|
|
|
// and removed after exit.
|
|
|
|
func TestOutput(t *testing.T) {
|
|
|
|
dlvbin, tmpdir := getDlvBin(t)
|
|
|
|
defer os.RemoveAll(tmpdir)
|
2017-07-26 18:51:44 +00:00
|
|
|
|
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
|
|
|
for _, output := range []string{"", "myownname", filepath.Join(tmpdir, "absolute.path")} {
|
|
|
|
testOutput(t, dlvbin, output, []string{"exit"})
|
|
|
|
|
|
|
|
const hello = "hello world!"
|
|
|
|
stdout, _ := testOutput(t, dlvbin, output, []string{"continue", "exit"})
|
|
|
|
if !strings.Contains(string(stdout), hello) {
|
|
|
|
t.Errorf("stdout %q should contain %q", stdout, hello)
|
|
|
|
}
|
2017-07-26 18:51:44 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-18 07:36:40 +00:00
|
|
|
|
2019-07-19 17:59:38 +00:00
|
|
|
// TestContinue verifies that the debugged executable starts immediately with --continue
|
|
|
|
func TestContinue(t *testing.T) {
|
2019-07-30 15:38:25 +00:00
|
|
|
const listenAddr = "127.0.0.1:40573"
|
2019-07-19 17:59:38 +00:00
|
|
|
|
|
|
|
dlvbin, tmpdir := getDlvBin(t)
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
|
|
|
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
|
|
|
cmd := exec.Command(dlvbin, "debug", "--headless", "--continue", "--accept-multiclient", "--listen", listenAddr)
|
|
|
|
cmd.Dir = buildtestdir
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
assertNoError(err, t, "stderr pipe")
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatalf("could not start headless instance: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
scan := bufio.NewScanner(stdout)
|
|
|
|
// wait for the debugger to start
|
|
|
|
for scan.Scan() {
|
|
|
|
t.Log(scan.Text())
|
|
|
|
if scan.Text() == "hello world!" {
|
|
|
|
break
|
2019-08-01 23:28:38 +00:00
|
|
|
}
|
2019-07-19 17:59:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// and detach from and kill the headless instance
|
|
|
|
client := rpc2.NewClient(listenAddr)
|
|
|
|
if err := client.Detach(true); err != nil {
|
|
|
|
t.Fatalf("error detaching from headless instance: %v", err)
|
|
|
|
}
|
|
|
|
cmd.Wait()
|
|
|
|
}
|
|
|
|
|
2018-04-18 07:36:40 +00:00
|
|
|
func checkAutogenDoc(t *testing.T, filename, gencommand string, generated []byte) {
|
2018-11-13 00:22:53 +00:00
|
|
|
saved := slurpFile(t, filepath.Join(projectRoot(), filename))
|
2018-04-18 07:36:40 +00:00
|
|
|
|
|
|
|
if len(saved) != len(generated) {
|
2018-04-19 08:43:23 +00:00
|
|
|
t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
|
2018-04-18 07:36:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i := range saved {
|
|
|
|
if saved[i] != generated[i] {
|
|
|
|
t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func slurpFile(t *testing.T, filename string) []byte {
|
|
|
|
saved, err := ioutil.ReadFile(filename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Could not read %s: %v", filename, err)
|
|
|
|
}
|
|
|
|
return saved
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestGeneratedDoc tests that the autogenerated documentation has been
|
|
|
|
// updated.
|
|
|
|
func TestGeneratedDoc(t *testing.T) {
|
2020-03-13 13:14:32 +00:00
|
|
|
if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
|
|
|
|
t.Skip("skipping test on Windows in CI")
|
|
|
|
}
|
2018-04-18 07:36:40 +00:00
|
|
|
// Checks gen-cli-docs.go
|
|
|
|
var generatedBuf bytes.Buffer
|
|
|
|
commands := terminal.DebugCommands(nil)
|
|
|
|
commands.WriteMarkdown(&generatedBuf)
|
2020-03-28 18:18:55 +00:00
|
|
|
checkAutogenDoc(t, "Documentation/cli/README.md", "_scripts/gen-cli-docs.go", generatedBuf.Bytes())
|
2018-04-18 07:36:40 +00:00
|
|
|
|
|
|
|
// Checks gen-usage-docs.go
|
|
|
|
tempDir, err := ioutil.TempDir(os.TempDir(), "test-gen-doc")
|
|
|
|
assertNoError(err, t, "TempDir")
|
2019-10-21 18:48:04 +00:00
|
|
|
defer protest.SafeRemoveAll(tempDir)
|
2020-03-28 18:18:55 +00:00
|
|
|
cmd := exec.Command("go", "run", "_scripts/gen-usage-docs.go", tempDir)
|
2019-10-07 16:20:49 +00:00
|
|
|
cmd.Dir = projectRoot()
|
|
|
|
cmd.Run()
|
2018-04-18 07:36:40 +00:00
|
|
|
entries, err := ioutil.ReadDir(tempDir)
|
|
|
|
assertNoError(err, t, "ReadDir")
|
|
|
|
for _, doc := range entries {
|
|
|
|
docFilename := "Documentation/usage/" + doc.Name()
|
2020-03-28 18:18:55 +00:00
|
|
|
checkAutogenDoc(t, docFilename, "_scripts/gen-usage-docs.go", slurpFile(t, tempDir+"/"+doc.Name()))
|
2018-04-18 07:36:40 +00:00
|
|
|
}
|
2019-07-02 17:55:27 +00:00
|
|
|
|
|
|
|
runScript := func(args ...string) []byte {
|
|
|
|
a := []string{"run"}
|
|
|
|
a = append(a, args...)
|
|
|
|
cmd := exec.Command("go", a...)
|
|
|
|
cmd.Dir = projectRoot()
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("could not run script %v: %v (output: %q)", args, err, string(out))
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2020-03-28 18:18:55 +00:00
|
|
|
checkAutogenDoc(t, "pkg/terminal/starbind/starlark_mapping.go", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "go", "-"))
|
|
|
|
checkAutogenDoc(t, "Documentation/cli/starlark.md", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "doc/dummy", "Documentation/cli/starlark.md"))
|
2018-04-18 07:36:40 +00:00
|
|
|
}
|
2018-11-30 08:53:29 +00:00
|
|
|
|
|
|
|
func TestExitInInit(t *testing.T) {
|
|
|
|
dlvbin, tmpdir := getDlvBin(t)
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
|
|
|
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
|
|
|
exitInit := filepath.Join(protest.FindFixturesDir(), "exit.init")
|
|
|
|
cmd := exec.Command(dlvbin, "--init", exitInit, "debug")
|
|
|
|
cmd.Dir = buildtestdir
|
|
|
|
out, err := cmd.CombinedOutput()
|
|
|
|
t.Logf("%q %v\n", string(out), err)
|
|
|
|
// dlv will exit anyway because stdin is not a tty but it will print the
|
|
|
|
// prompt once if the init file didn't call exit successfully.
|
|
|
|
if strings.Contains(string(out), "(dlv)") {
|
|
|
|
t.Fatal("init did not cause dlv to exit")
|
|
|
|
}
|
|
|
|
}
|
2018-11-20 15:13:09 +00:00
|
|
|
|
|
|
|
func getMethods(pkg *types.Package, typename string) map[string]*types.Func {
|
|
|
|
r := make(map[string]*types.Func)
|
|
|
|
mset := types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup(typename).Type()))
|
|
|
|
for i := 0; i < mset.Len(); i++ {
|
|
|
|
fn := mset.At(i).Obj().(*types.Func)
|
|
|
|
r[fn.Name()] = fn
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func publicMethodOf(decl ast.Decl, receiver string) *ast.FuncDecl {
|
|
|
|
fndecl, isfunc := decl.(*ast.FuncDecl)
|
|
|
|
if !isfunc {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if fndecl.Name.Name[0] >= 'a' && fndecl.Name.Name[0] <= 'z' {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if fndecl.Recv == nil || len(fndecl.Recv.List) != 1 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
starexpr, isstar := fndecl.Recv.List[0].Type.(*ast.StarExpr)
|
|
|
|
if !isstar {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
identexpr, isident := starexpr.X.(*ast.Ident)
|
|
|
|
if !isident || identexpr.Name != receiver {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if fndecl.Body == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return fndecl
|
|
|
|
}
|
|
|
|
|
|
|
|
func findCallCall(fndecl *ast.FuncDecl) *ast.CallExpr {
|
|
|
|
for _, stmt := range fndecl.Body.List {
|
|
|
|
var x ast.Expr = nil
|
|
|
|
|
|
|
|
switch s := stmt.(type) {
|
|
|
|
case *ast.AssignStmt:
|
|
|
|
if len(s.Rhs) == 1 {
|
|
|
|
x = s.Rhs[0]
|
|
|
|
}
|
|
|
|
case *ast.ReturnStmt:
|
|
|
|
if len(s.Results) == 1 {
|
|
|
|
x = s.Results[0]
|
|
|
|
}
|
|
|
|
case *ast.ExprStmt:
|
|
|
|
x = s.X
|
|
|
|
}
|
|
|
|
|
|
|
|
callx, iscall := x.(*ast.CallExpr)
|
|
|
|
if !iscall {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fun, issel := callx.Fun.(*ast.SelectorExpr)
|
|
|
|
if !issel || fun.Sel.Name != "call" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return callx
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func qf(*types.Package) string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTypecheckRPC(t *testing.T) {
|
|
|
|
fset := &token.FileSet{}
|
|
|
|
cfg := &packages.Config{
|
2020-03-19 17:10:08 +00:00
|
|
|
Mode: packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedName | packages.NeedCompiledGoFiles | packages.NeedTypes,
|
2018-11-20 15:13:09 +00:00
|
|
|
Fset: fset,
|
|
|
|
}
|
|
|
|
pkgs, err := packages.Load(cfg, "github.com/go-delve/delve/service/rpc2")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
var clientAst *ast.File
|
|
|
|
var serverMethods map[string]*types.Func
|
|
|
|
var info *types.Info
|
|
|
|
packages.Visit(pkgs, func(pkg *packages.Package) bool {
|
|
|
|
if pkg.PkgPath != "github.com/go-delve/delve/service/rpc2" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
t.Logf("package found: %v", pkg.PkgPath)
|
|
|
|
serverMethods = getMethods(pkg.Types, "RPCServer")
|
|
|
|
info = pkg.TypesInfo
|
|
|
|
for i := range pkg.Syntax {
|
|
|
|
t.Logf("file %q", pkg.CompiledGoFiles[i])
|
|
|
|
if strings.HasSuffix(pkg.CompiledGoFiles[i], string(os.PathSeparator)+"client.go") {
|
|
|
|
clientAst = pkg.Syntax[i]
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
errcount := 0
|
|
|
|
|
|
|
|
for _, decl := range clientAst.Decls {
|
|
|
|
fndecl := publicMethodOf(decl, "RPCClient")
|
|
|
|
if fndecl == nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
switch fndecl.Name.Name {
|
|
|
|
case "Continue", "Rewind":
|
|
|
|
// wrappers over continueDir
|
|
|
|
continue
|
|
|
|
case "SetReturnValuesLoadConfig", "Disconnect":
|
|
|
|
// support functions
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
|
|
|
if fndecl.Name.Name == "Continue" || fndecl.Name.Name == "Rewind" || fndecl.Name.Name == "DirectionCongruentContinue" {
|
2018-11-20 15:13:09 +00:00
|
|
|
// using continueDir
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
callx := findCallCall(fndecl)
|
|
|
|
|
|
|
|
if callx == nil {
|
|
|
|
t.Errorf("%s: could not find RPC call", fset.Position(fndecl.Pos()))
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(callx.Args) != 3 {
|
|
|
|
t.Errorf("%s: wrong number of arguments for RPC call", fset.Position(callx.Pos()))
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
arg0, arg0islit := callx.Args[0].(*ast.BasicLit)
|
|
|
|
arg1 := callx.Args[1]
|
|
|
|
arg2 := callx.Args[2]
|
|
|
|
if !arg0islit || arg0.Kind != token.STRING {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name, _ := strconv.Unquote(arg0.Value)
|
|
|
|
serverMethod := serverMethods[name]
|
|
|
|
if serverMethod == nil {
|
|
|
|
t.Errorf("%s: could not find RPC method %q", fset.Position(callx.Pos()), name)
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
params := serverMethod.Type().(*types.Signature).Params()
|
|
|
|
|
|
|
|
if a, e := info.TypeOf(arg1), params.At(0).Type(); !types.AssignableTo(a, e) {
|
|
|
|
t.Errorf("%s: wrong type of first argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.HasSuffix(params.At(1).Type().String(), "/service.RPCCallback") {
|
|
|
|
if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) {
|
|
|
|
t.Errorf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if clit, ok := arg1.(*ast.CompositeLit); ok {
|
|
|
|
typ := params.At(0).Type()
|
|
|
|
st := typ.Underlying().(*types.Struct)
|
|
|
|
if len(clit.Elts) != st.NumFields() && types.TypeString(typ, qf) != "DebuggerCommand" {
|
|
|
|
t.Errorf("%s: wrong number of fields in first argument's literal %d, expected %d", fset.Position(callx.Pos()), len(clit.Elts), st.NumFields())
|
|
|
|
errcount++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if errcount > 0 {
|
|
|
|
t.Errorf("%d errors", errcount)
|
|
|
|
}
|
|
|
|
}
|
2020-02-28 17:48:59 +00:00
|
|
|
|
|
|
|
// TestDap verifies that a dap server can be started and shut down.
|
|
|
|
func TestDap(t *testing.T) {
|
|
|
|
const listenAddr = "127.0.0.1:40575"
|
|
|
|
|
|
|
|
dlvbin, tmpdir := getDlvBin(t)
|
|
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
|
|
|
|
cmd := exec.Command(dlvbin, "dap", "--log-output=dap", "--log", "--listen", listenAddr)
|
|
|
|
stdout, err := cmd.StdoutPipe()
|
|
|
|
assertNoError(err, t, "stdout pipe")
|
|
|
|
stderr, err := cmd.StderrPipe()
|
|
|
|
assertNoError(err, t, "stderr pipe")
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
|
|
t.Fatalf("could not start dap instance: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
scanOut := bufio.NewScanner(stdout)
|
|
|
|
scanErr := bufio.NewScanner(stderr)
|
|
|
|
// Wait for the debug server to start
|
|
|
|
scanOut.Scan()
|
|
|
|
listening := "DAP server listening at: " + listenAddr
|
|
|
|
if scanOut.Text() != listening {
|
|
|
|
cmd.Process.Kill() // release the port
|
|
|
|
t.Fatalf("Unexpected stdout:\ngot %q\nwant %q", scanOut.Text(), listening)
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
for scanErr.Scan() {
|
|
|
|
t.Log(scanErr.Text())
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Connect a client and request shutdown.
|
|
|
|
client := daptest.NewClient(listenAddr)
|
|
|
|
client.DisconnectRequest()
|
|
|
|
client.ExpectDisconnectResponse(t)
|
|
|
|
if _, err := client.ReadMessage(); err != io.EOF {
|
|
|
|
t.Errorf("got %q, want \"EOF\"\n", err)
|
|
|
|
}
|
|
|
|
client.Close()
|
|
|
|
cmd.Wait()
|
|
|
|
}
|