提交 3fba1f71 编写于 作者: D Derek Parker

Correctly handle hardware breakpoints across threads

* Set hardware breakpoints on all existing threads
* Set hardware breakpoints on any new thread the spawns

Fixes #111
上级 d66dfbef
// fix lines
package main package main
import ( import (
"fmt" "fmt"
"runtime"
"time" "time"
) )
...@@ -46,3 +46,8 @@ func main() { ...@@ -46,3 +46,8 @@ func main() {
func testgoroutine(foo int, d chan int) { func testgoroutine(foo int, d chan int) {
d <- foo d <- foo
} }
func init() {
runtime.LockOSThread()
runtime.GOMAXPROCS(4)
}
...@@ -120,8 +120,10 @@ func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Bre ...@@ -120,8 +120,10 @@ func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Bre
break break
} }
if v == nil { if v == nil {
if err := setHardwareBreakpoint(i, tid, addr); err != nil { for t, _ := range dbp.Threads {
return nil, fmt.Errorf("could not set hardware breakpoint: %v", err) 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) dbp.HWBreakPoints[i] = dbp.newHardwareBreakpoint(fn.Name, f, l, addr, nil, temp, i)
return dbp.HWBreakPoints[i], nil return dbp.HWBreakPoints[i], nil
......
...@@ -46,11 +46,6 @@ func setHardwareBreakpoint(reg, tid int, addr uint64) error { ...@@ -46,11 +46,6 @@ func setHardwareBreakpoint(reg, tid int, addr uint64) error {
return PtracePokeUser(tid, dr7off, dr7) 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 // Set the debug register `reg` with the address of the
// instruction we want to trigger a debug exception. // instruction we want to trigger a debug exception.
if err := PtracePokeUser(tid, drxoff, uintptr(addr)); err != nil { if err := PtracePokeUser(tid, drxoff, uintptr(addr)); err != nil {
......
...@@ -263,27 +263,27 @@ func (dbp *DebuggedProcess) next() error { ...@@ -263,27 +263,27 @@ func (dbp *DebuggedProcess) next() error {
var goroutineExiting bool var goroutineExiting bool
var waitCount int var waitCount int
for _, th := range dbp.Threads { for _, th := range dbp.Threads {
if th.blocked() { // Continue threads that aren't running go code. if th.blocked() {
if err = th.Continue(); err != nil { // Ignore threads that aren't running go code.
return err
}
continue continue
} }
waitCount++ waitCount++
if err = th.Next(); err != nil { if err = th.SetNextBreakpoints(); err != nil {
if err, ok := err.(GoroutineExitingError); ok { if err, ok := err.(GoroutineExitingError); ok {
waitCount = waitCount - 1 + chanRecvCount waitCount = waitCount - 1 + chanRecvCount
if err.goid == g.Id { if err.goid == g.Id {
goroutineExiting = true goroutineExiting = true
} }
if err := th.Continue(); err != nil {
return err
}
continue continue
} }
return err return err
} }
} }
for _, th := range dbp.Threads {
if err = th.Continue(); err != nil {
return err
}
}
for waitCount > 0 { for waitCount > 0 {
thread, err := dbp.trapWait(-1) thread, err := dbp.trapWait(-1)
......
...@@ -252,6 +252,14 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) { ...@@ -252,6 +252,14 @@ func (dbp *DebuggedProcess) trapWait(pid int) (*ThreadContext, error) {
if err != nil { if err != nil {
return nil, err 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 { if err = th.Continue(); err != nil {
return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err) return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
} }
......
...@@ -117,7 +117,7 @@ func (thread *ThreadContext) Location() (*Location, error) { ...@@ -117,7 +117,7 @@ func (thread *ThreadContext) Location() (*Location, error) {
// This functionality is implemented by finding all possible next lines // This functionality is implemented by finding all possible next lines
// and setting a breakpoint at them. Once we've set a breakpoint at each // and setting a breakpoint at them. Once we've set a breakpoint at each
// potential line, we continue the thread. // potential line, we continue the thread.
func (thread *ThreadContext) Next() (err error) { func (thread *ThreadContext) SetNextBreakpoints() (err error) {
curpc, err := thread.PC() curpc, err := thread.PC()
if err != nil { if err != nil {
return err return err
...@@ -144,7 +144,7 @@ func (thread *ThreadContext) Next() (err error) { ...@@ -144,7 +144,7 @@ func (thread *ThreadContext) Next() (err error) {
return err return err
} }
} }
return thread.Continue() return nil
} }
// Go routine is exiting. // Go routine is exiting.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册