diff --git a/proc/breakpoints.go b/proc/breakpoints.go index 7a49d4ee..1ef2ee1b 100644 --- a/proc/breakpoints.go +++ b/proc/breakpoints.go @@ -1,9 +1,6 @@ package proc -import ( - "fmt" - "runtime" -) +import "fmt" // Represents a single breakpoint. Stores information on the break // point including the byte of data that originally was stored at that @@ -24,10 +21,6 @@ type Breakpoint struct { Stacktrace int // Number of stack frames to retrieve Goroutine bool // Retrieve goroutine information Variables []string // Variables to evaluate - - hardware bool // Breakpoint using CPU debug registers. - reg int // If hardware breakpoint, what debug register it belongs to. - } func (bp *Breakpoint) String() string { @@ -37,12 +30,6 @@ func (bp *Breakpoint) String() string { // Clear this breakpoint appropriately depending on whether it is a // hardware or software breakpoint. func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) { - if bp.hardware { - if err := thread.dbp.clearHardwareBreakpoint(bp.reg, thread.Id); err != nil { - return nil, err - } - return bp, nil - } if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil { return nil, fmt.Errorf("could not clear breakpoint %s", err) } @@ -97,34 +84,6 @@ func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, newBreakpoint.ID = dbp.breakpointIDCounter } - // Try and set a hardware breakpoint. - for i, used := range dbp.arch.HardwareBreakpointUsage() { - if runtime.GOOS == "darwin" { // TODO(dp): Implement hardware breakpoints on OSX. - break - } - if used { - continue - } - for tid, t := range dbp.Threads { - if t.running { - err := t.Halt() - if err != nil { - return nil, err - } - defer t.Continue() - } - if err := dbp.setHardwareBreakpoint(i, tid, addr); err != nil { - return nil, fmt.Errorf("could not set hardware breakpoint on thread %d: %s", t, err) - } - } - dbp.arch.SetHardwareBreakpointUsage(i, true) - newBreakpoint.reg = i - newBreakpoint.hardware = true - dbp.Breakpoints[addr] = newBreakpoint - return dbp.Breakpoints[addr], nil - } - - // Fall back to software breakpoint. thread := dbp.Threads[tid] originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize()) if err != nil { diff --git a/proc/breakpoints_darwin_amd64.go b/proc/breakpoints_darwin_amd64.go deleted file mode 100644 index ab071204..00000000 --- a/proc/breakpoints_darwin_amd64.go +++ /dev/null @@ -1,13 +0,0 @@ -package proc - -import "fmt" - -// TODO(darwin) -func (dbp *Process) setHardwareBreakpoint(reg, tid int, addr uint64) error { - return fmt.Errorf("not implemented on darwin") -} - -// TODO(darwin) -func (dbp *Process) clearHardwareBreakpoint(reg, tid int) error { - return fmt.Errorf("not implemented on darwin") -} diff --git a/proc/breakpoints_linux_amd64.go b/proc/breakpoints_linux_amd64.go deleted file mode 100644 index bb1ac196..00000000 --- a/proc/breakpoints_linux_amd64.go +++ /dev/null @@ -1,77 +0,0 @@ -package proc - -/* -#include -#include -#include -#include - -// Exposes C macro `offsetof` which is needed for getting -// the offset of the debug register we want, and passing -// that offset to PTRACE_POKE_USER. -int offset(int reg) { - return offsetof(struct user, u_debugreg[reg]); -} -*/ -import "C" - -import "fmt" - -// Sets a hardware breakpoint by setting the contents of the -// debug register `reg` with the address of the instruction -// that we want to break at. There are only 4 debug registers -// DR0-DR3. Debug register 7 is the control register. -func (dbp *Process) setHardwareBreakpoint(reg, tid int, addr uint64) error { - if reg < 0 || reg > 3 { - return fmt.Errorf("invalid debug register value") - } - - var ( - dr7off = uintptr(C.offset(C.DR_CONTROL)) - drxoff = uintptr(C.offset(C.int(reg))) - drxmask = uintptr((((1 << C.DR_CONTROL_SIZE) - 1) << uintptr(reg*C.DR_CONTROL_SIZE)) | (((1 << C.DR_ENABLE_SIZE) - 1) << uintptr(reg*C.DR_ENABLE_SIZE))) - drxenable = uintptr(0x1) << uintptr(reg*C.DR_ENABLE_SIZE) - drxctl = uintptr(C.DR_RW_EXECUTE|C.DR_LEN_1) << uintptr(reg*C.DR_CONTROL_SIZE) - dr7 uintptr - ) - - // Get current state - var err error - dbp.execPtraceFunc(func() { dr7, err = PtracePeekUser(tid, dr7off) }) - if err != nil { - return err - } - - // If addr == 0 we are expected to disable the breakpoint - if addr == 0 { - dr7 &= ^drxmask - dbp.execPtraceFunc(func() { err = PtracePokeUser(tid, dr7off, dr7) }) - return err - } - - // Set the debug register `reg` with the address of the - // instruction we want to trigger a debug exception. - dbp.execPtraceFunc(func() { err = PtracePokeUser(tid, drxoff, uintptr(addr)) }) - if err != nil { - return err - } - - // Clear dr`reg` flags - dr7 &= ^drxmask - // Enable dr`reg` - dr7 |= (drxctl << C.DR_CONTROL_SHIFT) | drxenable - - // Set the debug control register. This - // instructs the cpu to raise a debug - // exception when hitting the address of - // an instruction stored in dr0-dr3. - dbp.execPtraceFunc(func() { err = PtracePokeUser(tid, dr7off, dr7) }) - return err -} - -// Clears a hardware breakpoint. Essentially sets -// the debug reg to 0 and clears the control register -// flags for that reg. -func (dbp *Process) clearHardwareBreakpoint(reg, tid int) error { - return dbp.setHardwareBreakpoint(reg, tid, 0) -} diff --git a/proc/proc.go b/proc/proc.go index 1780f4a7..b9624ab2 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -25,7 +25,7 @@ type Process struct { Pid int // Process Pid Process *os.Process // Pointer to process struct for the actual process we are debugging - // Breakpoint table, hold information on software / hardware breakpoints. + // Breakpoint table, holds information on breakpoints. // Maps instruction address to Breakpoint struct. Breakpoints map[uint64]*Breakpoint @@ -205,12 +205,6 @@ func (dbp *Process) RequestManualStop() error { // Sets a breakpoint at addr, and stores it in the process wide // break point table. Setting a break point must be thread specific due to // ptrace actions needing the thread to be in a signal-delivery-stop. -// -// Depending on hardware support, Delve will choose to either -// set a hardware or software breakpoint. Essentially, if the -// hardware supports it, and there are free debug registers, Delve -// will set a hardware breakpoint. Otherwise we fall back to software -// breakpoints, which are a bit more work for us. func (dbp *Process) SetBreakpoint(addr uint64) (*Breakpoint, error) { return dbp.setBreakpoint(dbp.CurrentThread.Id, addr, false) } @@ -221,27 +215,16 @@ func (dbp *Process) SetTempBreakpoint(addr uint64) (*Breakpoint, error) { } // Clears a breakpoint. -// -// If it is a hardware assisted breakpoint, iterate through all threads -// and clear the debug register. Otherwise, restore original instruction. func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) { - bp, ok := dbp.Breakpoints[addr] + bp, ok := dbp.FindBreakpoint(addr) if !ok { return nil, NoBreakpointError{addr: addr} } - for _, thread := range dbp.Threads { - if _, err := bp.Clear(thread); err != nil { - return nil, err - } - if !bp.hardware { - break - } + if _, err := bp.Clear(dbp.CurrentThread); err != nil { + return nil, err } - if bp.hardware { - dbp.arch.SetHardwareBreakpointUsage(bp.reg, false) - } delete(dbp.Breakpoints, addr) return bp, nil @@ -566,15 +549,11 @@ func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) { // Finds the breakpoint for the given pc. func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) { - // Check for software breakpoint. PC will be at - // breakpoint instruction + size of breakpoint. + // Check to see if address is past the breakpoint, (i.e. breakpoint was hit). if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok { return bp, true } - // Check for hardware breakpoint. PC will equal - // the breakpoint address since the CPU will stop - // the process without executing the instruction at - // this address. + // Directly use addr to lookup breakpoint. if bp, ok := dbp.Breakpoints[pc]; ok { return bp, true } diff --git a/proc/proc_linux.go b/proc/proc_linux.go index d1a9e658..3585b440 100644 --- a/proc/proc_linux.go +++ b/proc/proc_linux.go @@ -267,15 +267,6 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) { if err != nil { return nil, err } - // Set all hardware breakpoints on the new thread. - for _, bp := range dbp.Breakpoints { - if !bp.hardware { - continue - } - if err = dbp.setHardwareBreakpoint(bp.reg, th.Id, bp.Addr); err != nil { - return nil, err - } - } if err = th.Continue(); err != nil { return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) } diff --git a/proc/proc_test.go b/proc/proc_test.go index 8cb29382..8256e6c0 100644 --- a/proc/proc_test.go +++ b/proc/proc_test.go @@ -727,7 +727,7 @@ func TestContinueMulti(t *testing.T) { sayhiCount := 0 for { err := p.Continue() - if p.exited { + if p.Exited() { break } assertNoError(err, t, "Continue()") diff --git a/proc/threads.go b/proc/threads.go index fd8944d8..6600d892 100644 --- a/proc/threads.go +++ b/proc/threads.go @@ -50,11 +50,9 @@ func (thread *Thread) Continue() error { } // Check whether we are stopped at a breakpoint, and // if so, single step over it before continuing. - if bp, ok := thread.dbp.FindBreakpoint(pc); ok { - if !bp.hardware { - if err := thread.Step(); err != nil { - return err - } + if _, ok := thread.dbp.FindBreakpoint(pc); ok { + if err := thread.Step(); err != nil { + return err } } return thread.resume() @@ -78,7 +76,7 @@ func (thread *Thread) Step() (err error) { return err } - bp, ok := thread.dbp.Breakpoints[pc] + bp, ok := thread.dbp.FindBreakpoint(pc) if ok { // Clear the breakpoint so that we can continue execution. _, err = bp.Clear(thread) @@ -88,11 +86,7 @@ func (thread *Thread) Step() (err error) { // Restore breakpoint now that we have passed it. defer func() { - if bp.hardware { - err = thread.dbp.setHardwareBreakpoint(bp.reg, thread.Id, bp.Addr) - } else { - err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr) - } + err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr) }() }