From 3fba1f7113cdee372ce51d82aa2fece55b03697c Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Thu, 11 Jun 2015 22:46:06 -0500 Subject: [PATCH] Correctly handle hardware breakpoints across threads * Set hardware breakpoints on all existing threads * Set hardware breakpoints on any new thread the spawns Fixes #111 --- _fixtures/testnextprog.go | 7 ++++++- proctl/breakpoints.go | 6 ++++-- proctl/breakpoints_linux_amd64.go | 5 ----- proctl/proctl.go | 16 ++++++++-------- proctl/proctl_linux.go | 8 ++++++++ proctl/threads.go | 4 ++-- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/_fixtures/testnextprog.go b/_fixtures/testnextprog.go index 8cc93e2e..2465b56b 100644 --- a/_fixtures/testnextprog.go +++ b/_fixtures/testnextprog.go @@ -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) +} diff --git a/proctl/breakpoints.go b/proctl/breakpoints.go index deb6a9fc..79451323 100644 --- a/proctl/breakpoints.go +++ b/proctl/breakpoints.go @@ -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 diff --git a/proctl/breakpoints_linux_amd64.go b/proctl/breakpoints_linux_amd64.go index 656a81e0..ed536b08 100644 --- a/proctl/breakpoints_linux_amd64.go +++ b/proctl/breakpoints_linux_amd64.go @@ -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< 0 { thread, err := dbp.trapWait(-1) diff --git a/proctl/proctl_linux.go b/proctl/proctl_linux.go index 93787be9..bf332058 100644 --- a/proctl/proctl_linux.go +++ b/proctl/proctl_linux.go @@ -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) } diff --git a/proctl/threads.go b/proctl/threads.go index 399ce111..06f67dbe 100644 --- a/proctl/threads.go +++ b/proctl/threads.go @@ -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.