proc: add test for attach/detach, fix detach (#773)
Detach did not work for processes we attach to via PID. Linux: we were only detaching from the main thread, all threads are detached independently Windows: we must resume all threads before detaching. macOS: still broken. Updates #772
This commit is contained in:
parent
ecc2eb6da6
commit
7b19fe9e69
@ -12,5 +12,13 @@ func main() {
|
|||||||
header := w.Header().Get("Content-Type")
|
header := w.Header().Get("Content-Type")
|
||||||
w.Write([]byte(msg + header))
|
w.Write([]byte(msg + header))
|
||||||
})
|
})
|
||||||
http.ListenAndServe(":9191", nil)
|
http.HandleFunc("/nobp", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
msg := "hello, world!"
|
||||||
|
header := w.Header().Get("Content-Type")
|
||||||
|
w.Write([]byte(msg + header))
|
||||||
|
})
|
||||||
|
err := http.ListenAndServe(":9191", nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
err = PtraceDetach(dbp.pid, 0)
|
err = dbp.detach()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -509,3 +509,7 @@ func (dbp *Process) resume() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) detach() error {
|
||||||
|
return PtraceDetach(dbp.pid, 0)
|
||||||
|
}
|
||||||
|
@ -484,6 +484,24 @@ func (dbp *Process) resume() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) detach() error {
|
||||||
|
for threadID := range dbp.threads {
|
||||||
|
err := PtraceDetach(threadID, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For some reason the process will sometimes enter stopped state after a
|
||||||
|
// detach, this doesn't happen immediately either.
|
||||||
|
// We have to wait a bit here, then check if the main thread is stopped and
|
||||||
|
// SIGCONT it if it is.
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if s := status(dbp.pid, dbp.os.comm); s == 'T' {
|
||||||
|
sys.Kill(dbp.pid, sys.SIGCONT)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func killProcess(pid int) error {
|
func killProcess(pid int) error {
|
||||||
return sys.Kill(pid, sys.SIGINT)
|
return sys.Kill(pid, sys.SIGINT)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -2581,3 +2582,55 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAttachDetach(t *testing.T) {
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// does not work on darwin
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fixture := protest.BuildFixture("testnextnethttp")
|
||||||
|
cmd := exec.Command(fixture.Path)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
assertNoError(cmd.Start(), t, "starting fixture")
|
||||||
|
|
||||||
|
// wait for testnextnethttp to start listening
|
||||||
|
t0 := time.Now()
|
||||||
|
for {
|
||||||
|
conn, err := net.Dial("tcp", "localhost:9191")
|
||||||
|
if err == nil {
|
||||||
|
conn.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
if time.Since(t0) > 10*time.Second {
|
||||||
|
t.Fatal("fixture did not start")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := Attach(cmd.Process.Pid)
|
||||||
|
assertNoError(err, t, "Attach")
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
http.Get("http://localhost:9191")
|
||||||
|
}()
|
||||||
|
|
||||||
|
assertNoError(p.Continue(), t, "Continue")
|
||||||
|
|
||||||
|
f, ln := currentLineNumber(p, t)
|
||||||
|
if ln != 11 {
|
||||||
|
t.Fatalf("Expected line :11 got %s:%d", f, ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoError(p.Detach(false), t, "Detach")
|
||||||
|
|
||||||
|
resp, err := http.Get("http://localhost:9191/nobp")
|
||||||
|
assertNoError(err, t, "Page request after detach")
|
||||||
|
bs, err := ioutil.ReadAll(resp.Body)
|
||||||
|
assertNoError(err, t, "Reading /nobp page")
|
||||||
|
if out := string(bs); strings.Index(out, "hello, world!") < 0 {
|
||||||
|
t.Fatalf("/nobp page does not contain \"hello, world!\": %q", out)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
@ -663,6 +663,16 @@ func (dbp *Process) resume() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) detach() error {
|
||||||
|
for _, thread := range dbp.threads {
|
||||||
|
_, err := _ResumeThread(thread.os.hThread)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PtraceDetach(dbp.pid, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func killProcess(pid int) error {
|
func killProcess(pid int) error {
|
||||||
p, err := os.FindProcess(pid)
|
p, err := os.FindProcess(pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user