Correctly handle hardware breakpoints across threads
* Set hardware breakpoints on all existing threads * Set hardware breakpoints on any new thread the spawns Fixes #111
This commit is contained in:
parent
d66dfbef54
commit
3fba1f7113
@ -1,8 +1,8 @@
|
||||
// fix lines
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -46,3 +46,8 @@ func main() {
|
||||
func testgoroutine(foo int, d chan int) {
|
||||
d <- foo
|
||||
}
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
runtime.GOMAXPROCS(4)
|
||||
}
|
||||
|
@ -120,8 +120,10 @@ func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Bre
|
||||
break
|
||||
}
|
||||
if v == nil {
|
||||
if err := setHardwareBreakpoint(i, tid, addr); err != nil {
|
||||
return nil, fmt.Errorf("could not set hardware breakpoint: %v", err)
|
||||
for t, _ := range dbp.Threads {
|
||||
if err := setHardwareBreakpoint(i, t, addr); err != nil {
|
||||
return nil, fmt.Errorf("could not set hardware breakpoint on thread %d: %s", t, err)
|
||||
}
|
||||
}
|
||||
dbp.HWBreakPoints[i] = dbp.newHardwareBreakpoint(fn.Name, f, l, addr, nil, temp, i)
|
||||
return dbp.HWBreakPoints[i], nil
|
||||
|
@ -46,11 +46,6 @@ func setHardwareBreakpoint(reg, tid int, addr uint64) error {
|
||||
return PtracePokeUser(tid, dr7off, dr7)
|
||||
}
|
||||
|
||||
// Error out if dr`reg` is already used
|
||||
if dr7&(0x3<<uint(reg*C.DR_ENABLE_SIZE)) != 0 {
|
||||
return fmt.Errorf("dr%d already enabled", reg)
|
||||
}
|
||||
|
||||
// Set the debug register `reg` with the address of the
|
||||
// instruction we want to trigger a debug exception.
|
||||
if err := PtracePokeUser(tid, drxoff, uintptr(addr)); err != nil {
|
||||
|
@ -263,27 +263,27 @@ func (dbp *DebuggedProcess) next() error {
|
||||
var goroutineExiting bool
|
||||
var waitCount int
|
||||
for _, th := range dbp.Threads {
|
||||
if th.blocked() { // Continue threads that aren't running go code.
|
||||
if err = th.Continue(); err != nil {
|
||||
return err
|
||||
}
|
||||
if th.blocked() {
|
||||
// Ignore threads that aren't running go code.
|
||||
continue
|
||||
}
|
||||
waitCount++
|
||||
if err = th.Next(); err != nil {
|
||||
if err = th.SetNextBreakpoints(); err != nil {
|
||||
if err, ok := err.(GoroutineExitingError); ok {
|
||||
waitCount = waitCount - 1 + chanRecvCount
|
||||
if err.goid == g.Id {
|
||||
goroutineExiting = true
|
||||
}
|
||||
if err := th.Continue(); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, th := range dbp.Threads {
|
||||
if err = th.Continue(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for waitCount > 0 {
|
||||
thread, err := dbp.trapWait(-1)
|
||||
|
@ -252,6 +252,14 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for reg, bp := range dbp.HWBreakPoints {
|
||||
if bp == nil {
|
||||
continue
|
||||
}
|
||||
if err = setHardwareBreakpoint(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)
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func (thread *ThreadContext) Location() (*Location, error) {
|
||||
// This functionality is implemented by finding all possible next lines
|
||||
// and setting a breakpoint at them. Once we've set a breakpoint at each
|
||||
// potential line, we continue the thread.
|
||||
func (thread *ThreadContext) Next() (err error) {
|
||||
func (thread *ThreadContext) SetNextBreakpoints() (err error) {
|
||||
curpc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -144,7 +144,7 @@ func (thread *ThreadContext) Next() (err error) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return thread.Continue()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Go routine is exiting.
|
||||
|
Loading…
Reference in New Issue
Block a user