Handle process natural death a bit better
This commit is contained in:
parent
f39e134d1d
commit
8b04d877a0
@ -87,9 +87,14 @@ func Run(run bool, pid int, args []string) {
|
||||
}
|
||||
|
||||
cmd := cmds.Find(cmdstr)
|
||||
err = cmd(dbp, args...)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
||||
if err := cmd(dbp, args...); err != nil {
|
||||
switch err.(type) {
|
||||
case proctl.ProcessExitedError:
|
||||
pe := err.(proctl.ProcessExitedError)
|
||||
fmt.Fprintf(os.Stderr, "Process exited with status %d\n", pe.Status)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,17 @@ func (mse ManualStopError) Error() string {
|
||||
return "Manual stop requested"
|
||||
}
|
||||
|
||||
// ProcessExitedError indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ProcessExitedError struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*DebuggedProcess, error) {
|
||||
dbp, err := newDebugProcess(pid, true)
|
||||
@ -238,7 +249,7 @@ func (dbp *DebuggedProcess) Continue() error {
|
||||
}
|
||||
|
||||
fn := func() error {
|
||||
wpid, _, err := trapWait(dbp, -1)
|
||||
wpid, err := trapWait(dbp, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -380,11 +391,3 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ProcessExitedError struct {
|
||||
pid int
|
||||
}
|
||||
|
||||
func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("process %d has exited", pe.pid)
|
||||
}
|
||||
|
@ -172,14 +172,14 @@ func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) {
|
||||
return macho.Open(C.GoString(pathptr))
|
||||
}
|
||||
|
||||
func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
|
||||
func trapWait(dbp *DebuggedProcess, pid int) (int, error) {
|
||||
port := C.mach_port_wait(dbp.os.exceptionPort)
|
||||
if port == 0 {
|
||||
return -1, nil, ProcessExitedError{}
|
||||
return -1, ProcessExitedError{Pid: dbp.Pid}
|
||||
}
|
||||
|
||||
dbp.updateThreadList()
|
||||
return int(port), nil, nil
|
||||
return int(port), nil
|
||||
}
|
||||
|
||||
func wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
|
@ -223,11 +223,11 @@ func stopped(pid int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
|
||||
func trapWait(dbp *DebuggedProcess, pid int) (int, error) {
|
||||
for {
|
||||
wpid, status, err := wait(pid, 0)
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||
return -1, fmt.Errorf("wait err %s %d", err, pid)
|
||||
}
|
||||
if wpid == 0 {
|
||||
continue
|
||||
@ -235,38 +235,39 @@ func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
|
||||
if th, ok := dbp.Threads[wpid]; ok {
|
||||
th.Status = status
|
||||
}
|
||||
|
||||
if status.Exited() && wpid == dbp.Pid {
|
||||
return -1, status, ProcessExitedError{wpid}
|
||||
return -1, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||
// A traced thread has cloned a new thread, grab the pid and
|
||||
// add it to our list of traced threads.
|
||||
cloned, err := sys.PtraceGetEventMsg(wpid)
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("could not get event message: %s", err)
|
||||
return -1, fmt.Errorf("could not get event message: %s", err)
|
||||
}
|
||||
|
||||
th, err := dbp.addThread(int(cloned), false)
|
||||
if err != nil {
|
||||
return -1, nil, err
|
||||
return -1, err
|
||||
}
|
||||
|
||||
err = th.Continue()
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
return -1, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
}
|
||||
|
||||
err = dbp.Threads[int(wpid)].Continue()
|
||||
if err != nil {
|
||||
return -1, nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
return -1, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP {
|
||||
return wpid, status, nil
|
||||
return wpid, nil
|
||||
}
|
||||
if status.StopSignal() == sys.SIGSTOP && dbp.halt {
|
||||
return -1, nil, ManualStopError{}
|
||||
return -1, ManualStopError{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,22 @@ func currentLineNumber(p *DebuggedProcess, t *testing.T) (string, int) {
|
||||
return f, l
|
||||
}
|
||||
|
||||
func TestExit(t *testing.T) {
|
||||
withTestProcess("../_fixtures/continuetestprog", t, func(p *DebuggedProcess) {
|
||||
err := p.Continue()
|
||||
pe, ok := err.(ProcessExitedError)
|
||||
if !ok {
|
||||
t.Fatalf("Continue() returned unexpected error type")
|
||||
}
|
||||
if pe.Status != 0 {
|
||||
t.Errorf("Unexpected error status: %d", pe.Status)
|
||||
}
|
||||
if pe.Pid != p.Pid {
|
||||
t.Errorf("Unexpected process id: %d", pe.Pid)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStep(t *testing.T) {
|
||||
withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
|
||||
helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
|
||||
|
@ -35,7 +35,7 @@ type Registers interface {
|
||||
func (thread *ThreadContext) Registers() (Registers, error) {
|
||||
regs, err := registers(thread)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get registers %s", err)
|
||||
return nil, fmt.Errorf("could not get registers: %s", err)
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
@ -70,13 +70,13 @@ func (thread *ThreadContext) PrintInfo() error {
|
||||
// we step over any breakpoints. It will restore the instruction,
|
||||
// step, and then restore the breakpoint and continue.
|
||||
func (thread *ThreadContext) Continue() error {
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
regs, err := thread.Registers()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get registers %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := thread.Process.BreakPoints[regs.PC()-1]; ok {
|
||||
err := thread.Step()
|
||||
if err != nil {
|
||||
@ -195,7 +195,7 @@ func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.Frame
|
||||
return err
|
||||
}
|
||||
// Wait on -1, just in case scheduler switches threads for this G.
|
||||
wpid, _, err := trapWait(thread.Process, -1)
|
||||
wpid, err := trapWait(thread.Process, -1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user