Optimize Next implementation

Once the program detects that we have stepped into another function,
we simply calculate the return address and then set a breakpoint and
continue to that location, avoiding numerous syscalls.
This commit is contained in:
Derek Parker
2014-10-11 17:29:10 -05:00
parent c60f3aafde
commit dc8c9cc2a4
4 changed files with 53 additions and 6 deletions

View File

@ -37,7 +37,7 @@ func testnext() {
func main() { func main() {
runtime.LockOSThread() runtime.LockOSThread()
for { for {
sleepytime() helloworld()
testnext() testnext()
} }
} }

View File

@ -56,7 +56,7 @@ func TestFindReturnAddress(t *testing.T) {
syscall.PtracePeekText(p.Pid, uintptr(addr), data) syscall.PtracePeekText(p.Pid, uintptr(addr), data)
addr = binary.LittleEndian.Uint64(data) addr = binary.LittleEndian.Uint64(data)
expected := uint64(0x400f04) expected := uint64(0x400ed4)
if addr != expected { if addr != expected {
t.Fatalf("return address not found correctly, expected %#v got %#v", expected, addr) t.Fatalf("return address not found correctly, expected %#v got %#v", expected, addr)
} }

View File

@ -140,6 +140,10 @@ func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) {
originalData = make([]byte, 1) originalData = make([]byte, 1)
) )
if fn == nil {
return nil, fmt.Errorf("could not set breakpoint")
}
_, err := syscall.PtracePeekData(dbp.Pid, addr, originalData) _, err := syscall.PtracePeekData(dbp.Pid, addr, originalData)
if err != nil { if err != nil {
return nil, err return nil, err
@ -280,9 +284,12 @@ func (dbp *DebuggedProcess) Next() error {
} }
if !fde.Cover(pc) { if !fde.Cover(pc) {
// We've stepped into a function, keep going. err = dbp.continueToReturnAddress(pc, fde)
// TODO: Use DWARF frame info to continue to return address. if err != nil {
continue return err
}
pc, _ = dbp.CurrentPC()
} }
_, nl, _ := dbp.GoSymTable.PCToLine(pc) _, nl, _ := dbp.GoSymTable.PCToLine(pc)
@ -294,6 +301,47 @@ func (dbp *DebuggedProcess) Next() error {
return nil return nil
} }
func (dbp *DebuggedProcess) continueToReturnAddress(pc uint64, fde *frame.FrameDescriptionEntry) error {
for !fde.Cover(pc) {
// Our offset here is be 0 because we
// have stepped into the first instruction
// of this function. Therefore the function
// has not had a chance to modify its' stack
// and change our offset.
addr := dbp.ReturnAddressFromOffset(0)
bp, err := dbp.Break(uintptr(addr))
if err != nil {
if _, ok := err.(BreakPointExistsError); !ok {
for !fde.Cover(pc) {
err = dbp.Step()
if err != nil {
return err
}
pc, err = dbp.CurrentPC()
if err != nil {
return err
}
}
return nil
}
}
err = dbp.Continue()
if err != nil {
return err
}
err = dbp.clearTempBreakpoint(bp.Addr)
if err != nil {
return err
}
pc, _ = dbp.CurrentPC()
}
return nil
}
// Continue process until next breakpoint. // Continue process until next breakpoint.
func (dbp *DebuggedProcess) Continue() error { func (dbp *DebuggedProcess) Continue() error {
// Stepping first will ensure we are able to continue // Stepping first will ensure we are able to continue

View File

@ -2,7 +2,6 @@ package proctl_test
import ( import (
"bytes" "bytes"
"path/filepath" "path/filepath"
"syscall" "syscall"
"testing" "testing"