// doTrap handles SIGTRAP debug events with a cause of 0. These can // be caused either by an installed breakpoint, a breakpoint in the // program text, or by single stepping. // // TODO(austin) I think we also get this on an execve syscall. func (ev *debugEvent) doTrap() (threadState, os.Error) { t := ev.t if t.state == singleStepping { return stopped, nil } // Hit a breakpoint. Linux leaves the program counter after // the breakpoint. If this is an installed breakpoint, we // need to back the PC up to the breakpoint PC. var regs syscall.PtraceRegs err := t.ptraceGetRegs(®s) if err != nil { return stopped, err } b, ok := t.proc.breakpoints[uintptr(regs.PC())-uintptr(len(bpinst386))] if !ok { // We must have hit a breakpoint that was actually in // the program. Leave the IP where it is so we don't // re-execute the breakpoint instruction. Expose the // fact that we stopped with a SIGTRAP. return stoppedSignal, nil } t.breakpoint = b t.logTrace("at breakpoint %v, backing up PC from %#x", b, regs.PC()) regs.SetPC(uint64(b.pc)) err = t.ptraceSetRegs(®s) if err != nil { return stopped, err } return stoppedBreakpoint, nil }
func (t *thread) logTrace(format string, args ...interface{}) { if !trace { return } logLock.Lock() defer logLock.Unlock() fmt.Fprintf(os.Stderr, "Thread %d", t.tid) if traceIP { var regs syscall.PtraceRegs err := t.ptraceGetRegs(®s) if err == nil { fmt.Fprintf(os.Stderr, "@%x", regs.PC()) } } fmt.Fprint(os.Stderr, ": ") fmt.Fprintf(os.Stderr, format, args...) fmt.Fprint(os.Stderr, "\n") }
func (p *process) Continue() os.Error { // Single step any threads that are stopped at breakpoints so // we can reinstall breakpoints. var ready chan os.Error count := 0 err := p.do(func() os.Error { // We make the ready channel big enough to hold all // ready message so we don't jam up the monitor if we // stop listening (e.g., if there's an error). ready = make(chan os.Error, len(p.threads)) for _, t := range p.threads { if !t.state.isStopped() { continue } // We use the breakpoint map directly here // instead of checking the stop cause because // it could have been stopped at a breakpoint // for some other reason, or the breakpoint // could have been added since it was stopped. var regs syscall.PtraceRegs err := t.ptraceGetRegs(®s) if err != nil { return err } if b, ok := p.breakpoints[uintptr(regs.PC())]; ok { t.logTrace("stepping over breakpoint %v", b) if err := t.stepAsync(ready); err != nil { return err } count++ } } return nil }) if err != nil { p.stopMonitor(err) return err } // Wait for single stepping threads for count > 0 { err = <-ready if err != nil { p.stopMonitor(err) return err } count-- } // Continue all threads err = p.do(func() os.Error { if err := p.installBreakpoints(); err != nil { return err } for _, t := range p.threads { var err os.Error switch { case !t.state.isStopped(): continue case t.state == stoppedSignal && t.signal != syscall.SIGSTOP && t.signal != syscall.SIGTRAP: t.logTrace("continuing with signal %d", t.signal) err = t.ptraceContWithSignal(t.signal) default: t.logTrace("continuing") err = t.ptraceCont() } if err != nil { return err } if t.state == stoppedExiting { t.setState(exiting) } else { t.setState(running) } } return nil }) if err != nil { // TODO(austin) Do we need to stop the monitor with // this error atomically with the do-routine above? p.stopMonitor(err) return err } return nil }