proc: Continue does not work with breakpoints set on NOP (OSX)

Fixes #262
This commit is contained in:
aarzilli 2015-11-10 10:49:47 +01:00
parent 708cf2f290
commit b21686e6c4
5 changed files with 58 additions and 5 deletions

@ -11,7 +11,7 @@ func demo(id int, wait *sync.WaitGroup) {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
sleep := rand.Intn(10) + 1 sleep := rand.Intn(10) + 1
fmt.Printf("id: %d step: %d sleeping %d\n", id, i, sleep) fmt.Printf("id: %d step: %d sleeping %d\n", id, i, sleep)
time.Sleep(time.Duration(sleep) * time.Millisecond) time.Sleep(time.Duration(sleep) * time.Millisecond)
} }
wait.Done() wait.Done()

16
_fixtures/issue262.go Normal file

@ -0,0 +1,16 @@
package main
import "fmt"
func typicalFunction() (res int) {
defer func() {
res = 2
return
}()
res = 10
return // setup breakpoint here
}
func main() {
fmt.Println(typicalFunction())
}

@ -329,5 +329,13 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
} }
func (dbp *Process) exitGuard(err error) error { func (dbp *Process) exitGuard(err error) error {
if err != ErrContinueThread {
return err
}
_, status, werr := dbp.wait(dbp.Pid, sys.WNOHANG)
if werr == nil && status.Exited() {
dbp.postExit()
return ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
}
return err return err
} }

@ -953,7 +953,7 @@ func TestFrameEvaluation(t *testing.T) {
assertNoError(err, t, "setFunctionBreakpoint") assertNoError(err, t, "setFunctionBreakpoint")
assertNoError(p.Continue(), t, "Continue()") assertNoError(p.Continue(), t, "Continue()")
/**** Testing evaluation on goroutines ****/ // Testing evaluation on goroutines
gs, err := p.GoroutinesInfo() gs, err := p.GoroutinesInfo()
assertNoError(err, t, "GoroutinesInfo") assertNoError(err, t, "GoroutinesInfo")
found := make([]bool, 10) found := make([]bool, 10)
@ -992,7 +992,7 @@ func TestFrameEvaluation(t *testing.T) {
} }
} }
/**** Testing evaluation on frames ****/ // Testing evaluation on frames
assertNoError(p.Continue(), t, "Continue() 2") assertNoError(p.Continue(), t, "Continue() 2")
g, err := p.CurrentThread.GetG() g, err := p.CurrentThread.GetG()
assertNoError(err, t, "GetG()") assertNoError(err, t, "GetG()")
@ -1129,3 +1129,23 @@ func TestBreakpointCounts(t *testing.T) {
} }
}) })
} }
func TestIssue262(t *testing.T) {
// Continue does not work when the current breakpoint is set on a NOP instruction
withTestProcess("issue262", t, func(p *Process, fixture protest.Fixture) {
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 11)
assertNoError(err, t, "LineToPC")
_, err = p.SetBreakpoint(addr)
assertNoError(err, t, "SetBreakpoint()")
assertNoError(p.Continue(), t, "Continue()")
err = p.Continue()
if err == nil {
t.Fatalf("No error on second continue")
}
_, exited := err.(ProcessExitedError)
if !exited {
t.Fatalf("Process did not exit after second continue: %v", err)
}
})
}

@ -1,6 +1,7 @@
package proc package proc
// #include "threads_darwin.h" // #include "threads_darwin.h"
// #include "proc_darwin.h"
import "C" import "C"
import ( import (
"fmt" "fmt"
@ -12,6 +13,8 @@ type OSSpecificDetails struct {
registers C.x86_thread_state64_t registers C.x86_thread_state64_t
} }
var ErrContinueThread = fmt.Errorf("could not continue thread")
func (t *Thread) halt() (err error) { func (t *Thread) halt() (err error) {
kret := C.thread_suspend(t.os.thread_act) kret := C.thread_suspend(t.os.thread_act)
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
@ -27,7 +30,13 @@ func (t *Thread) singleStep() error {
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not single step") return fmt.Errorf("could not single step")
} }
t.dbp.trapWait(0) for {
port := C.mach_port_wait(t.dbp.os.portSet)
if port == C.mach_port_t(t.Id) {
break
}
}
kret = C.clear_trap_flag(t.os.thread_act) kret = C.clear_trap_flag(t.os.thread_act)
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not clear CPU trap flag") return fmt.Errorf("could not clear CPU trap flag")
@ -45,7 +54,7 @@ func (t *Thread) resume() error {
} }
kret := C.resume_thread(t.os.thread_act) kret := C.resume_thread(t.os.thread_act)
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not continue thread") return ErrContinueThread
} }
return nil return nil
} }