mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 02:36:18 +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
	 Derek Parker
					Derek Parker