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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -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
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
// 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) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return thread.Continue()
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go routine is exiting.
|
// Go routine is exiting.
|
||||||
|
Loading…
Reference in New Issue
Block a user