diff --git a/proctl/proctl.go b/proctl/proctl.go index 4ef933856d0254539252e619e229fde283789e3c..bc1bb2edf72bf550987044f84e116c29eb93796e 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -279,17 +279,15 @@ func (dbp *DebuggedProcess) next() error { } for { - thread, err := trapWait(dbp, -1) + thread, breakpoint, err := trapWait(dbp, -1) if err != nil { return err } - pc, err := thread.CurrentPC() - if err != nil { - return err - } - // Check if we've hit a software breakpoint. If so, reset PC. - if err = thread.clearTempBreakpoint(pc - 1); err != nil { - return err + // Check if we've hit a breakpoint. + if breakpoint != nil { + if err = thread.clearTempBreakpoint(breakpoint.Addr); err != nil { + return err + } } // Grab the current goroutine for this thread. tg, err := thread.curG() @@ -316,54 +314,39 @@ func (dbp *DebuggedProcess) Continue() error { return err } } + return dbp.run(dbp.resume) +} - fn := func() error { - thread, err := trapWait(dbp, -1) - if err != nil { - return err - } - if dbp.CurrentThread != thread { - dbp.SwitchThread(thread.Id) - } - pc, err := thread.CurrentPC() - if err != nil { - return err - } - - // Check to see if we hit a runtime.breakpoint - fn := dbp.GoSymTable.PCToFunc(pc) - if fn != nil && fn.Name == "runtime.breakpoint" { - // step twice to get back to user code - for i := 0; i < 2; i++ { - err = thread.Step() - if err != nil { - return err - } - } - dbp.Halt() - return nil - } - - // Check for hardware breakpoint - for _, bp := range dbp.HWBreakPoints { - if bp != nil && bp.Addr == pc { - if !bp.Temp { - return dbp.Halt() - } - return nil - } +func (dbp *DebuggedProcess) resume() error { + thread, breakpoint, err := trapWait(dbp, -1) + if err != nil { + return err + } + if dbp.CurrentThread != thread { + dbp.SwitchThread(thread.Id) + } + pc, err := thread.CurrentPC() + if err != nil { + return err + } + if breakpoint != nil { + if !breakpoint.Temp { + return dbp.Halt() } - // Check to see if we have hit a software breakpoint. - if bp, ok := dbp.BreakPoints[pc-1]; ok { - if !bp.Temp { - return dbp.Halt() + } + // Check to see if we hit a runtime.breakpoint + fn := dbp.GoSymTable.PCToFunc(pc) + if fn != nil && fn.Name == "runtime.breakpoint" { + // step twice to get back to user code + for i := 0; i < 2; i++ { + if err = thread.Step(); err != nil { + return err } - return nil } - - return fmt.Errorf("unrecognized breakpoint %#v", pc) + return dbp.Halt() } - return dbp.run(fn) + + return fmt.Errorf("unrecognized breakpoint %#v", pc) } // Steps through process. diff --git a/proctl/proctl_darwin.go b/proctl/proctl_darwin.go index 4896e4777377bc1e8f57741971cd5cb7e992989d..47e0325d2b5c4ee60b38408491283aa35f05cb0f 100644 --- a/proctl/proctl_darwin.go +++ b/proctl/proctl_darwin.go @@ -171,17 +171,17 @@ func (dbp *DebuggedProcess) findExecutable() (*macho.File, error) { return exe, nil } -func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, error) { +func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, *BreakPoint, error) { port := C.mach_port_wait(dbp.os.portSet) switch port { case dbp.os.notificationPort: _, status, err := wait(dbp.Pid, 0) if err != nil { - return nil, err + return nil, nil, err } dbp.exited = true - return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()} + return nil, nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()} case C.MACH_RCV_INTERRUPTED: if !dbp.halt { // Call trapWait again, it seems @@ -189,9 +189,9 @@ func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, error) { // process natural death _sometimes_. return trapWait(dbp, pid) } - return nil, ManualStopError{} + return nil, nil, ManualStopError{} case 0: - return nil, fmt.Errorf("error while waiting for task") + return nil, nil, fmt.Errorf("error while waiting for task") } // Since we cannot be notified of new threads on OS X @@ -199,9 +199,24 @@ func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, error) { dbp.updateThreadList() thread, ok := dbp.Threads[int(port)] if !ok { - return nil, fmt.Errorf("could not find thread for %d", port) + return nil, nil, fmt.Errorf("could not find thread for %d", port) } - return thread, nil + pc, err := thread.CurrentPC() + if err != nil { + return nil, nil, err + } + // Check for hardware breakpoint + for _, bp := range dbp.HWBreakPoints { + if bp != nil && bp.Addr == pc { + return thread, bp, nil + } + } + // Check to see if we have hit a software breakpoint. + if bp, ok := dbp.BreakPoints[pc-1]; ok { + return thread, bp, nil + } + + return thread, nil, nil } func wait(pid, options int) (int, *sys.WaitStatus, error) { diff --git a/proctl/proctl_linux.go b/proctl/proctl_linux.go index a0b7f8858235116cdff8be472b68a47841606911..0d72bccff1bf645becab863f7f7785ea46a42200 100644 --- a/proctl/proctl_linux.go +++ b/proctl/proctl_linux.go @@ -216,11 +216,11 @@ func stopped(pid int) bool { return false } -func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, error) { +func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, *BreakPoint, error) { for { wpid, status, err := wait(pid, 0) if err != nil { - return nil, fmt.Errorf("wait err %s %d", err, pid) + return nil, nil, fmt.Errorf("wait err %s %d", err, pid) } if wpid == 0 { continue @@ -231,41 +231,55 @@ func trapWait(dbp *DebuggedProcess, pid int) (*ThreadContext, error) { if status.Exited() && wpid == dbp.Pid { dbp.exited = true - return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()} + return nil, nil, 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 nil, fmt.Errorf("could not get event message: %s", err) + return nil, nil, fmt.Errorf("could not get event message: %s", err) } th, err := dbp.addThread(int(cloned), false) if err != nil { - return nil, err + return nil, nil, err } err = th.Continue() if err != nil { - return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) + return nil, nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) } err = dbp.Threads[int(wpid)].Continue() if err != nil { - return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) + return nil, nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) } continue } if status.StopSignal() == sys.SIGTRAP { thread, ok := dbp.Threads[wpid] if !ok { - return nil, fmt.Errorf("could not find thread for %d", wpid) + return nil, nil, fmt.Errorf("could not find thread for %d", wpid) } - return thread, nil + pc, err := thread.CurrentPC() + if err != nil { + return nil, nil, err + } + // Check for hardware breakpoint + for _, bp := range dbp.HWBreakPoints { + if bp != nil && bp.Addr == pc { + return thread, bp, nil + } + } + // Check to see if we have hit a software breakpoint. + if bp, ok := dbp.BreakPoints[pc-1]; ok { + return thread, bp, nil + } + return thread, nil, nil } if status.StopSignal() == sys.SIGSTOP && dbp.halt { - return nil, ManualStopError{} + return nil, nil, ManualStopError{} } } } diff --git a/proctl/threads.go b/proctl/threads.go index a5d4686013f9e5f5b7456ded7f3805109a44b797..f05c351248fb419064addc324faa9981822662cf 100644 --- a/proctl/threads.go +++ b/proctl/threads.go @@ -131,7 +131,7 @@ func (thread *ThreadContext) CallFn(name string, fn func(*ThreadContext) error) if err := thread.Continue(); err != nil { return err } - if _, err = trapWait(thread.Process, -1); err != nil { + if _, _, err = trapWait(thread.Process, -1); err != nil { return err } return fn(thread)