mirror of
https://github.com/go-delve/delve.git
synced 2025-11-01 12:01:35 +08:00
Improve handling of process natural death (OS X)
This commit is contained in:
@ -105,11 +105,15 @@ func Run(args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmdstr, args := parseCommand(cmdstr)
|
cmdstr, args := parseCommand(cmdstr)
|
||||||
|
|
||||||
if cmdstr == "exit" {
|
if cmdstr == "exit" {
|
||||||
handleExit(dbp, t, 0)
|
handleExit(dbp, t, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dbp.Exited() && cmdstr != "help" {
|
||||||
|
fmt.Fprintf(os.Stderr, "Process has already exited.\n")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
cmd := cmds.Find(cmdstr)
|
cmd := cmds.Find(cmdstr)
|
||||||
if err := cmd(dbp, args...); err != nil {
|
if err := cmd(dbp, args...); err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
@ -132,39 +136,41 @@ func handleExit(dbp *proctl.DebuggedProcess, t *Term, status int) {
|
|||||||
f.Close()
|
f.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
answer, err := t.line.Prompt("Would you like to kill the process? [y/n]")
|
if !dbp.Exited() {
|
||||||
if err != nil {
|
for _, bp := range dbp.HWBreakPoints {
|
||||||
t.die(2, io.EOF)
|
if bp == nil {
|
||||||
}
|
continue
|
||||||
answer = strings.TrimSuffix(answer, "\n")
|
}
|
||||||
|
if _, err := dbp.Clear(bp.Addr); err != nil {
|
||||||
for _, bp := range dbp.HWBreakPoints {
|
fmt.Printf("Can't clear breakpoint @%x: %s\n", bp.Addr, err)
|
||||||
if bp == nil {
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if _, err := dbp.Clear(bp.Addr); err != nil {
|
|
||||||
fmt.Printf("Can't clear breakpoint @%x: %s\n", bp.Addr, err)
|
for pc := range dbp.BreakPoints {
|
||||||
|
if _, err := dbp.Clear(pc); err != nil {
|
||||||
|
fmt.Printf("Can't clear breakpoint @%x: %s\n", pc, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for pc := range dbp.BreakPoints {
|
answer, err := t.line.Prompt("Would you like to kill the process? [y/n]")
|
||||||
if _, err := dbp.Clear(pc); err != nil {
|
|
||||||
fmt.Printf("Can't clear breakpoint @%x: %s\n", pc, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Detaching from process...")
|
|
||||||
err = sys.PtraceDetach(dbp.Process.Pid)
|
|
||||||
if err != nil {
|
|
||||||
t.die(2, "Could not detach", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if answer == "y" {
|
|
||||||
fmt.Println("Killing process", dbp.Process.Pid)
|
|
||||||
|
|
||||||
err := dbp.Process.Kill()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Could not kill process", err)
|
t.die(2, io.EOF)
|
||||||
|
}
|
||||||
|
answer = strings.TrimSuffix(answer, "\n")
|
||||||
|
|
||||||
|
fmt.Println("Detaching from process...")
|
||||||
|
err = sys.PtraceDetach(dbp.Process.Pid)
|
||||||
|
if err != nil {
|
||||||
|
t.die(2, "Could not detach", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if answer == "y" {
|
||||||
|
fmt.Println("Killing process", dbp.Process.Pid)
|
||||||
|
|
||||||
|
err := dbp.Process.Kill()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Could not kill process", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ type DebuggedProcess struct {
|
|||||||
breakpointIDCounter int
|
breakpointIDCounter int
|
||||||
running bool
|
running bool
|
||||||
halt bool
|
halt bool
|
||||||
|
exited bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// A ManualStopError happens when the user triggers a
|
// A ManualStopError happens when the user triggers a
|
||||||
@ -87,6 +88,12 @@ func Launch(cmd []string) (*DebuggedProcess, error) {
|
|||||||
return newDebugProcess(proc.Process.Pid, false)
|
return newDebugProcess(proc.Process.Pid, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns whether or not Delve thinks the debugged
|
||||||
|
// process has exited.
|
||||||
|
func (dbp *DebuggedProcess) Exited() bool {
|
||||||
|
return dbp.exited
|
||||||
|
}
|
||||||
|
|
||||||
// Returns whether or not Delve thinks the debugged
|
// Returns whether or not Delve thinks the debugged
|
||||||
// process is currently executing.
|
// process is currently executing.
|
||||||
func (dbp *DebuggedProcess) Running() bool {
|
func (dbp *DebuggedProcess) Running() bool {
|
||||||
@ -242,6 +249,7 @@ func (dbp *DebuggedProcess) Continue() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
thread, ok := dbp.Threads[wpid]
|
thread, ok := dbp.Threads[wpid]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("could not find thread for %d", wpid)
|
return fmt.Errorf("could not find thread for %d", wpid)
|
||||||
@ -380,6 +388,9 @@ func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) run(fn func() error) error {
|
func (dbp *DebuggedProcess) run(fn func() error) error {
|
||||||
|
if dbp.exited {
|
||||||
|
return fmt.Errorf("process has already exited")
|
||||||
|
}
|
||||||
dbp.running = true
|
dbp.running = true
|
||||||
dbp.halt = false
|
dbp.halt = false
|
||||||
defer func() { dbp.running = false }()
|
defer func() { dbp.running = false }()
|
||||||
|
|||||||
@ -122,14 +122,14 @@ mach_port_wait(mach_port_t port_set) {
|
|||||||
// Wait for mach msg.
|
// Wait for mach msg.
|
||||||
kret = mach_msg(&msg.hdr, MACH_RCV_MSG|MACH_RCV_INTERRUPT,
|
kret = mach_msg(&msg.hdr, MACH_RCV_MSG|MACH_RCV_INTERRUPT,
|
||||||
0, sizeof(msg.data), port_set, 0, MACH_PORT_NULL);
|
0, sizeof(msg.data), port_set, 0, MACH_PORT_NULL);
|
||||||
|
if (kret == MACH_RCV_INTERRUPTED) return kret;
|
||||||
if (kret == MACH_RCV_INTERRUPTED || kret != MACH_MSG_SUCCESS) return 0;
|
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||||
|
|
||||||
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
||||||
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
||||||
thread = desc[0].name;
|
thread = desc[0].name;
|
||||||
|
|
||||||
// Exception
|
|
||||||
switch (msg.hdr.msgh_id) {
|
switch (msg.hdr.msgh_id) {
|
||||||
case 2401: // Exception
|
case 2401: // Exception
|
||||||
kret = thread_suspend(thread);
|
kret = thread_suspend(thread);
|
||||||
|
|||||||
@ -177,20 +177,31 @@ func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) {
|
|||||||
|
|
||||||
func trapWait(dbp *DebuggedProcess, pid int) (int, error) {
|
func trapWait(dbp *DebuggedProcess, pid int) (int, error) {
|
||||||
port := C.mach_port_wait(dbp.os.portSet)
|
port := C.mach_port_wait(dbp.os.portSet)
|
||||||
|
|
||||||
switch port {
|
switch port {
|
||||||
case C.MACH_RCV_INTERRUPTED:
|
|
||||||
return -1, ManualStopError{}
|
|
||||||
case dbp.os.notificationPort:
|
case dbp.os.notificationPort:
|
||||||
_, status, err := wait(dbp.Pid, 0)
|
_, status, err := wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
dbp.exited = true
|
||||||
return -1, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
return -1, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||||
|
case C.MACH_RCV_INTERRUPTED:
|
||||||
|
if !dbp.halt {
|
||||||
|
// Call trapWait again, it seems
|
||||||
|
// MACH_RCV_INTERRUPTED is emitted before
|
||||||
|
// process natural death _sometimes_.
|
||||||
|
return trapWait(dbp, pid)
|
||||||
|
}
|
||||||
|
return -1, ManualStopError{}
|
||||||
case 0:
|
case 0:
|
||||||
return -1, fmt.Errorf("error while waiting for task")
|
return -1, fmt.Errorf("error while waiting for task")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since we cannot be notified of new threads on OS X
|
||||||
|
// this is as good a time as any to check for them.
|
||||||
dbp.updateThreadList()
|
dbp.updateThreadList()
|
||||||
|
|
||||||
return int(port), nil
|
return int(port), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user