mirror of
https://github.com/go-delve/delve.git
synced 2025-11-02 12:59:01 +08:00
Handle process natural death a bit better
This commit is contained in:
committed by
Derek Parker
parent
f39e134d1d
commit
8b04d877a0
@ -87,9 +87,14 @@ func Run(run bool, pid int, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := cmds.Find(cmdstr)
|
cmd := cmds.Find(cmdstr)
|
||||||
err = cmd(dbp, args...)
|
if err := cmd(dbp, args...); err != nil {
|
||||||
if err != nil {
|
switch err.(type) {
|
||||||
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
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"
|
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.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int) (*DebuggedProcess, error) {
|
func Attach(pid int) (*DebuggedProcess, error) {
|
||||||
dbp, err := newDebugProcess(pid, true)
|
dbp, err := newDebugProcess(pid, true)
|
||||||
@ -238,7 +249,7 @@ func (dbp *DebuggedProcess) Continue() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn := func() error {
|
fn := func() error {
|
||||||
wpid, _, err := trapWait(dbp, -1)
|
wpid, err := trapWait(dbp, -1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -380,11 +391,3 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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))
|
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)
|
port := C.mach_port_wait(dbp.os.exceptionPort)
|
||||||
if port == 0 {
|
if port == 0 {
|
||||||
return -1, nil, ProcessExitedError{}
|
return -1, ProcessExitedError{Pid: dbp.Pid}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp.updateThreadList()
|
dbp.updateThreadList()
|
||||||
return int(port), nil, nil
|
return int(port), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func wait(pid, options int) (int, *sys.WaitStatus, error) {
|
func wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
|
|||||||
@ -223,11 +223,11 @@ func stopped(pid int) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
|
func trapWait(dbp *DebuggedProcess, pid int) (int, error) {
|
||||||
for {
|
for {
|
||||||
wpid, status, err := wait(pid, 0)
|
wpid, status, err := wait(pid, 0)
|
||||||
if err != nil {
|
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 {
|
if wpid == 0 {
|
||||||
continue
|
continue
|
||||||
@ -235,38 +235,39 @@ func trapWait(dbp *DebuggedProcess, pid int) (int, *sys.WaitStatus, error) {
|
|||||||
if th, ok := dbp.Threads[wpid]; ok {
|
if th, ok := dbp.Threads[wpid]; ok {
|
||||||
th.Status = status
|
th.Status = status
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.Exited() && wpid == dbp.Pid {
|
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 {
|
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||||
// A traced thread has cloned a new thread, grab the pid and
|
// A traced thread has cloned a new thread, grab the pid and
|
||||||
// add it to our list of traced threads.
|
// add it to our list of traced threads.
|
||||||
cloned, err := sys.PtraceGetEventMsg(wpid)
|
cloned, err := sys.PtraceGetEventMsg(wpid)
|
||||||
if err != nil {
|
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)
|
th, err := dbp.addThread(int(cloned), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, nil, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = th.Continue()
|
err = th.Continue()
|
||||||
if err != nil {
|
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()
|
err = dbp.Threads[int(wpid)].Continue()
|
||||||
if err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
if status.StopSignal() == sys.SIGTRAP {
|
if status.StopSignal() == sys.SIGTRAP {
|
||||||
return wpid, status, nil
|
return wpid, nil
|
||||||
}
|
}
|
||||||
if status.StopSignal() == sys.SIGSTOP && dbp.halt {
|
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
|
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) {
|
func TestStep(t *testing.T) {
|
||||||
withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
|
withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
|
||||||
helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
|
helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
|
||||||
|
|||||||
@ -35,7 +35,7 @@ type Registers interface {
|
|||||||
func (thread *ThreadContext) Registers() (Registers, error) {
|
func (thread *ThreadContext) Registers() (Registers, error) {
|
||||||
regs, err := registers(thread)
|
regs, err := registers(thread)
|
||||||
if err != nil {
|
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
|
return regs, nil
|
||||||
}
|
}
|
||||||
@ -70,13 +70,13 @@ func (thread *ThreadContext) PrintInfo() error {
|
|||||||
// we step over any breakpoints. It will restore the instruction,
|
// we step over any breakpoints. It will restore the instruction,
|
||||||
// step, and then restore the breakpoint and continue.
|
// step, and then restore the breakpoint and continue.
|
||||||
func (thread *ThreadContext) Continue() error {
|
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()
|
regs, err := thread.Registers()
|
||||||
if err != nil {
|
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 {
|
if _, ok := thread.Process.BreakPoints[regs.PC()-1]; ok {
|
||||||
err := thread.Step()
|
err := thread.Step()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -195,7 +195,7 @@ func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.Frame
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Wait on -1, just in case scheduler switches threads for this G.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user