proc: bugfix: status does not work with programs containing spaces
/proc/pid/stat needs more complex parsing Fixes #239
This commit is contained in:
parent
db95b67b23
commit
e509c3ce36
28
_fixtures/is sue239.go
Normal file
28
_fixtures/is sue239.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fibonacci(n int, c chan int) {
|
||||||
|
x, y := 0, 1
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
c <- x
|
||||||
|
x, y = y, x+y
|
||||||
|
}
|
||||||
|
close(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
a := struct { // set breakpoint here
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
}{A: "demo", B: 10}
|
||||||
|
fmt.Printf("%#v\n", a)
|
||||||
|
|
||||||
|
c := make(chan int, 10)
|
||||||
|
go fibonacci(cap(c), c)
|
||||||
|
for i := range c {
|
||||||
|
fmt.Println(i)
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,7 @@ type Process struct {
|
|||||||
exited bool
|
exited bool
|
||||||
ptraceChan chan func()
|
ptraceChan chan func()
|
||||||
ptraceDoneChan chan interface{}
|
ptraceDoneChan chan interface{}
|
||||||
|
comm string
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(pid int) *Process {
|
func New(pid int) *Process {
|
||||||
@ -142,7 +143,8 @@ func (dbp *Process) LoadInformation(path string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(3)
|
wg.Add(4)
|
||||||
|
go dbp.loadProcessInformation(&wg)
|
||||||
go dbp.parseDebugFrame(exe, &wg)
|
go dbp.parseDebugFrame(exe, &wg)
|
||||||
go dbp.obtainGoSymbols(exe, &wg)
|
go dbp.obtainGoSymbols(exe, &wg)
|
||||||
go dbp.parseDebugLineInfo(exe, &wg)
|
go dbp.parseDebugLineInfo(exe, &wg)
|
||||||
@ -570,7 +572,7 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, _, err = wait(dbp.Pid, dbp.Pid, 0)
|
_, _, err = dbp.wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -272,7 +272,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
|
|
||||||
switch port {
|
switch port {
|
||||||
case dbp.os.notificationPort:
|
case dbp.os.notificationPort:
|
||||||
_, status, err := wait(dbp.Pid, dbp.Pid, 0)
|
_, status, err := dbp.wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -318,7 +318,10 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func wait(pid, tgid, options int) (int, *sys.WaitStatus, error) {
|
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
var status sys.WaitStatus
|
var status sys.WaitStatus
|
||||||
wpid, err := sys.Wait4(pid, &status, options, nil)
|
wpid, err := sys.Wait4(pid, &status, options, nil)
|
||||||
return wpid, &status, err
|
return wpid, &status, err
|
||||||
|
@ -5,10 +5,12 @@ import (
|
|||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
@ -51,7 +53,7 @@ func Launch(cmd []string) (*Process, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbp.Pid = proc.Process.Pid
|
dbp.Pid = proc.Process.Pid
|
||||||
_, _, err = wait(proc.Process.Pid, proc.Process.Pid, 0)
|
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||||
}
|
}
|
||||||
@ -73,7 +75,7 @@ func (dbp *Process) Kill() (err error) {
|
|||||||
if err = sys.Kill(-dbp.Pid, sys.SIGKILL); err != nil {
|
if err = sys.Kill(-dbp.Pid, sys.SIGKILL); err != nil {
|
||||||
return errors.New("could not deliver signal " + err.Error())
|
return errors.New("could not deliver signal " + err.Error())
|
||||||
}
|
}
|
||||||
if _, _, err = wait(dbp.Pid, dbp.Pid, 0); err != nil {
|
if _, _, err = dbp.wait(dbp.Pid, 0); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dbp.exited = true
|
dbp.exited = true
|
||||||
@ -101,7 +103,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
|||||||
// if we truly don't have permissions.
|
// if we truly don't have permissions.
|
||||||
return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
|
return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
|
||||||
}
|
}
|
||||||
pid, status, err := wait(tid, dbp.Pid, 0)
|
pid, status, err := dbp.wait(tid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -112,7 +114,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
|||||||
|
|
||||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||||
if err == syscall.ESRCH {
|
if err == syscall.ESRCH {
|
||||||
if _, _, err = wait(tid, dbp.Pid, 0); err != nil {
|
if _, _, err = dbp.wait(tid, 0); err != nil {
|
||||||
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
||||||
}
|
}
|
||||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||||
@ -239,7 +241,7 @@ func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
|
|||||||
|
|
||||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||||
for {
|
for {
|
||||||
wpid, status, err := wait(pid, dbp.Pid, 0)
|
wpid, status, err := dbp.wait(pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||||
}
|
}
|
||||||
@ -309,25 +311,44 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func status(pid int) rune {
|
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.Pid))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not read process comm name: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
// removes newline character
|
||||||
|
comm = comm[:len(comm)-1]
|
||||||
|
dbp.comm = strings.Replace(string(comm), "%", "%%", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func status(pid int, comm string) rune {
|
||||||
|
// The second field of /proc/pid/stat is the name of the task in parenthesis.
|
||||||
|
// The name of the task is the base name of the executable for this process limited to 15 characters
|
||||||
|
// Since both parenthesis and spaces can appear inside the name of the task and no escaping happens we need to read the name of the executable first
|
||||||
|
// See: include/linux/sched.c:315 and include/linux/sched.c:1510
|
||||||
|
|
||||||
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return '\000'
|
return '\000'
|
||||||
}
|
}
|
||||||
|
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
p int
|
p int
|
||||||
comm string
|
|
||||||
state rune
|
state rune
|
||||||
)
|
)
|
||||||
fmt.Fscanf(f, "%d %s %c", &p, &comm, &state)
|
|
||||||
|
fmt.Fscanf(f, "%d ("+comm+") %c", &p, &state)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
func wait(pid, tgid, options int) (int, *sys.WaitStatus, error) {
|
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
var s sys.WaitStatus
|
var s sys.WaitStatus
|
||||||
if (pid != tgid) || (options != 0) {
|
if (pid != dbp.Pid) || (options != 0) {
|
||||||
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
||||||
return wpid, &s, err
|
return wpid, &s, err
|
||||||
} else {
|
} else {
|
||||||
@ -350,7 +371,7 @@ func wait(pid, tgid, options int) (int, *sys.WaitStatus, error) {
|
|||||||
if wpid != 0 {
|
if wpid != 0 {
|
||||||
return wpid, &s, err
|
return wpid, &s, err
|
||||||
}
|
}
|
||||||
if status(pid) == STATUS_ZOMBIE {
|
if status(pid, dbp.comm) == STATUS_ZOMBIE {
|
||||||
return pid, nil, nil
|
return pid, nil, nil
|
||||||
}
|
}
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
@ -800,3 +800,13 @@ func TestProcessReceivesSIGCHLD(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue239(t *testing.T) {
|
||||||
|
withTestProcess("is sue239", t, func(p *Process, fixture protest.Fixture) {
|
||||||
|
pos, _, err := p.goSymTable.LineToPC(fixture.Source, 17)
|
||||||
|
assertNoError(err, t, "LineToPC()")
|
||||||
|
_, err = p.SetBreakpoint(pos)
|
||||||
|
assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos))
|
||||||
|
assertNoError(p.Continue(), t, fmt.Sprintf("Continue()"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -18,7 +18,7 @@ func (t *Thread) halt() (err error) {
|
|||||||
err = fmt.Errorf("halt err %s on thread %d", err, t.Id)
|
err = fmt.Errorf("halt err %s on thread %d", err, t.Id)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _, err = wait(t.Id, t.dbp.Pid, 0)
|
_, _, err = t.dbp.wait(t.Id, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("wait err %s on thread %d", err, t.Id)
|
err = fmt.Errorf("wait err %s on thread %d", err, t.Id)
|
||||||
return
|
return
|
||||||
@ -27,7 +27,7 @@ func (t *Thread) halt() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (thread *Thread) stopped() bool {
|
func (thread *Thread) stopped() bool {
|
||||||
state := status(thread.Id)
|
state := status(thread.Id, thread.dbp.comm)
|
||||||
return state == STATUS_TRACE_STOP
|
return state == STATUS_TRACE_STOP
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ func (t *Thread) singleStep() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, _, err = wait(t.Id, t.dbp.Pid, 0)
|
_, _, err = t.dbp.wait(t.Id, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user