
* proc/native: make sure debugged executable can be deleted on windows Delve opens debugged executable to read binary info it contains, but it never closes the file. Windows will not let you delete file that is opened. So close Process.bi in Process.postExit, and actually call Process.postExit from windows Process.Kill. Also Windows sends some debugging events (EXIT_PROCESS_DEBUG_EVENT event in particular) after Delve calls TerminateProcess. The events need to be consumed by debugger before debugged process will be released by Windows. So call Process.waitForDebugEvent after TerminateProcess in Process.Kill. Fixes #398 * cmd/dlv: make TestIssue398 pass on darwin * cmd/dlv: add comment for TestIssue398 * proc/native: wait for debuggee to exit before returning from windows Process.Kill * proc/native: close process handle before returning from windows killProcess * proc/native: remove not used Process.Process
160 lines
3.9 KiB
Go
160 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
protest "github.com/derekparker/delve/pkg/proc/test"
|
|
"github.com/derekparker/delve/service/rpc2"
|
|
)
|
|
|
|
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"
|
|
}
|
|
}
|
|
os.Exit(m.Run())
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
func goEnv(name string) string {
|
|
if val := os.Getenv(name); val != "" {
|
|
return val
|
|
}
|
|
val, err := exec.Command("go", "env", name).Output()
|
|
if err != nil {
|
|
panic(err) // the Go tool was tested to work earlier
|
|
}
|
|
return strings.TrimSpace(string(val))
|
|
}
|
|
|
|
func TestBuild(t *testing.T) {
|
|
const listenAddr = "localhost:40573"
|
|
var err error
|
|
makedir := filepath.Join(goEnv("GOPATH"), "src", "github.com", "derekparker", "delve")
|
|
for _, makeProgram := range []string{"make", "mingw32-make"} {
|
|
var out []byte
|
|
cmd := exec.Command(makeProgram, "build")
|
|
cmd.Dir = makedir
|
|
out, err = cmd.CombinedOutput()
|
|
if err == nil {
|
|
break
|
|
} else {
|
|
t.Logf("makefile error %s (%s): %v", makeProgram, makedir, err)
|
|
t.Logf("output %s", string(out))
|
|
}
|
|
}
|
|
assertNoError(err, t, "make")
|
|
dlvbin := filepath.Join(makedir, "dlv")
|
|
defer os.Remove(dlvbin)
|
|
|
|
fixtures := protest.FindFixturesDir()
|
|
|
|
buildtestdir := filepath.Join(fixtures, "buildtest")
|
|
|
|
cmd := exec.Command(dlvbin, "debug", "--headless=true", "--listen="+listenAddr, "--api-version=2", "--backend="+testBackend)
|
|
cmd.Dir = buildtestdir
|
|
stdout, err := cmd.StdoutPipe()
|
|
assertNoError(err, t, "stdout pipe")
|
|
cmd.Start()
|
|
|
|
scan := bufio.NewScanner(stdout)
|
|
// wait for the debugger to start
|
|
scan.Scan()
|
|
go func() {
|
|
for scan.Scan() {
|
|
// keep pipe empty
|
|
}
|
|
}()
|
|
|
|
client := rpc2.NewClient(listenAddr)
|
|
state := <-client.Continue()
|
|
|
|
if !state.Exited {
|
|
t.Fatal("Program did not exit")
|
|
}
|
|
|
|
client.Detach(true)
|
|
cmd.Wait()
|
|
}
|
|
|
|
func testIssue398(t *testing.T, dlvbin string, cmds []string) (stdout, stderr []byte) {
|
|
var stdoutBuf, stderrBuf bytes.Buffer
|
|
buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
|
|
cmd := exec.Command(dlvbin, "debug")
|
|
cmd.Dir = buildtestdir
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cmd.Stdout = &stdoutBuf
|
|
cmd.Stderr = &stderrBuf
|
|
if err := cmd.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, c := range cmds {
|
|
fmt.Fprintf(stdin, "%s\n", c)
|
|
}
|
|
// ignore "dlv debug" command error, it returns
|
|
// errors even after successful debug session.
|
|
cmd.Wait()
|
|
stdout, stderr = stdoutBuf.Bytes(), stderrBuf.Bytes()
|
|
|
|
debugbin := filepath.Join(buildtestdir, "debug")
|
|
_, err = os.Stat(debugbin)
|
|
if err == nil {
|
|
t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", cmds, debugbin, stdout, stderr)
|
|
return
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
t.Errorf("running %q: %v\nstdout is %q, stderr is %q", cmds, err, stdout, stderr)
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// TestIssue398 verifies that the debug executable is removed after exit.
|
|
func TestIssue398(t *testing.T) {
|
|
tmpdir, err := ioutil.TempDir("", "TestIssue398")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
dlvbin := filepath.Join(tmpdir, "dlv.exe")
|
|
out, err := exec.Command("go", "build", "-o", dlvbin, "github.com/derekparker/delve/cmd/dlv").CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("go build -o %v github.com/derekparker/delve/cmd/dlv: %v\n%s", dlvbin, err, string(out))
|
|
}
|
|
|
|
testIssue398(t, dlvbin, []string{"exit"})
|
|
|
|
const hello = "hello world!"
|
|
stdout, _ := testIssue398(t, dlvbin, []string{"continue", "exit"})
|
|
if !strings.Contains(string(stdout), hello) {
|
|
t.Errorf("stdout %q should contain %q", stdout, hello)
|
|
}
|
|
}
|