func (dbp *Process) trapWait(pid int) (*Thread, error) { var ( th *Thread err error ) for { port := C.mach_port_wait(dbp.os.portSet) switch port { case dbp.os.notificationPort: _, status, err := wait(dbp.Pid, dbp.Pid, 0) if err != nil { return nil, err } dbp.exited = true return nil, 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_. continue } return nil, nil case 0: return nil, 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() th, err = dbp.handleBreakpointOnThread(int(port)) if err != nil { if _, ok := err.(NoBreakpointError); ok { if dbp.halt { dbp.halt = false return dbp.Threads[int(port)], nil } th := dbp.Threads[int(port)] if dbp.firstStart || th.singleStepping { dbp.firstStart = false return dbp.Threads[int(port)], nil } if err := th.Continue(); err != nil { return nil, err } continue } return nil, err } return th, nil } return th, nil }
func (t *Thread) singleStep() error { kret := C.single_step(t.os.threadAct) if kret != C.KERN_SUCCESS { return fmt.Errorf("could not single step") } for { port := C.mach_port_wait(t.dbp.os.portSet, C.int(0)) if port == C.mach_port_t(t.ID) { break } } kret = C.clear_trap_flag(t.os.threadAct) if kret != C.KERN_SUCCESS { return fmt.Errorf("could not clear CPU trap flag") } return nil }
func (dbp *Process) Kill() (err error) { err = sys.Kill(dbp.Pid, sys.SIGKILL) if err != nil { return errors.New("could not deliver signal: " + err.Error()) } for port := range dbp.Threads { if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS { return errors.New("could not resume task") } } for { port := C.mach_port_wait(dbp.os.portSet) if port == dbp.os.notificationPort { break } } dbp.exited = true return }
func (dbp *Process) waitForStop() ([]int, error) { ports := make([]int, 0, len(dbp.Threads)) count := 0 for { port := C.mach_port_wait(dbp.os.portSet, C.int(1)) if port != 0 { count = 0 ports = append(ports, int(port)) } else { n := C.num_running_threads(C.task_t(dbp.os.task)) if n == 0 { return ports, nil } else if n < 0 { return nil, fmt.Errorf("error waiting for thread stop %d", n) } else if count > 16 { return nil, fmt.Errorf("could not stop process %d", n) } } } }