Fix Next impl

Needs some refactoring and some optimization, but fixes several bugs.
This commit is contained in:
Derek Parker 2014-10-07 22:35:57 -05:00
parent 9084276012
commit 6a71009954
7 changed files with 68 additions and 91 deletions

@ -2,6 +2,7 @@ package main
import (
"fmt"
"runtime"
"time"
)
@ -19,21 +20,23 @@ func testnext() {
f = 2
)
for i := 0; i <= 1; i++ {
for i := 0; i <= 5; i++ {
j += j * (j ^ 3) / 100
helloworld()
}
if i == f {
fmt.Println("foo")
break
}
if f == 1 {
fmt.Println("should never get here")
helloworld()
}
helloworld()
}
func main() {
for i := 0; i <= 1; i++ {
runtime.LockOSThread()
for {
sleepytime()
testnext()
}

@ -161,7 +161,9 @@ func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
executeDwarfInstruction(frame)
}
// make sure we get the update cfa offset
executeDwarfInstruction(frame)
if frame.buf.Len() > 0 {
executeDwarfInstruction(frame)
}
}
func executeDwarfInstruction(frame *FrameContext) {

@ -20,7 +20,7 @@ func TestFindReturnAddress(t *testing.T) {
)
testsourcefile := testfile + ".go"
start, _, err := gsd.LineToPC(testsourcefile, 22)
start, _, err := gsd.LineToPC(testsourcefile, 24)
if err != nil {
t.Fatal(err)
}
@ -56,7 +56,7 @@ func TestFindReturnAddress(t *testing.T) {
syscall.PtracePeekText(p.Pid, uintptr(addr), data)
addr = binary.LittleEndian.Uint64(data)
if addr != 0x400f15 {
if addr != 0x400f04 {
t.Fatalf("return address not found correctly, expected %#v got %#v", uintptr(0x400f15), addr)
}
})

@ -74,18 +74,16 @@ func newStateMachine(dbl *DebugLineInfo) *StateMachine {
// Returns the filename, line number and PC for the next executable line in
// the traced program.
func (dbl *DebugLineInfo) NextLocation(pc uint64, line int) *Location {
func (dbl *DebugLineInfo) NextLocation(pc uint64, file string, line int) *Location {
var (
sm = newStateMachine(dbl)
buf = bytes.NewBuffer(dbl.Instructions)
)
executeUntilPC(sm, buf, pc)
l := sm.Line
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if sm.Line != l && sm.Line > line {
if sm.File == file && sm.Line > line {
break
}
}

@ -75,13 +75,13 @@ func TestNextLocAfterPC(t *testing.T) {
if err != nil {
t.Fatal(err)
}
loc := dbl.NextLocation(pc, 23)
loc := dbl.NextLocation(pc, testfile+".go", 23)
if loc.File != testfile+".go" {
t.Fatal("File not returned correctly", loc.File)
}
if loc.Line != 25 {
if loc.Line != 24 {
t.Fatal("Line not returned correctly", loc.Line)
}
}

@ -233,13 +233,13 @@ func (dbp *DebuggedProcess) Next() error {
pc--
}
_, l, fn := dbp.GoSymTable.PCToLine(pc)
f, l, _ := dbp.GoSymTable.PCToLine(pc)
fde, err := dbp.FrameEntries.FDEForPC(pc)
if err != nil {
return err
}
loc := dbp.DebugLine.NextLocation(pc, l)
loc := dbp.DebugLine.NextLocation(pc, f, l)
if !fde.AddressRange.Cover(loc.Address) {
// Unconditionally step out of current function
// Don't bother looking up ret addr, next line is
@ -271,11 +271,13 @@ func (dbp *DebuggedProcess) Next() error {
}
if !fde.AddressRange.Cover(pc) {
return dbp.continueToReturnAddress(pc, fde)
// We've stepped into a function, keep going.
// TODO: Use DWARF frame info to continue to return address.
continue
}
_, nl, nfn := dbp.GoSymTable.PCToLine(pc)
if nfn == fn && nl != l {
_, nl, _ := dbp.GoSymTable.PCToLine(pc)
if nl != l {
break
}
}
@ -284,31 +286,33 @@ func (dbp *DebuggedProcess) Next() error {
}
func (dbp *DebuggedProcess) continueToReturnAddress(pc uint64, fde *frame.FrameDescriptionEntry) error {
for !fde.AddressRange.Cover(pc) {
addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
bp, err := dbp.Break(uintptr(addr))
if err != nil {
if _, ok := err.(BreakPointExistsError); !ok {
return err
}
}
err = dbp.Continue()
if err != nil {
return err
}
err = dbp.clearTempBreakpoint(bp.Addr)
if err != nil {
return err
}
pc, err = dbp.CurrentPC()
if err != nil {
// for !fde.AddressRange.Cover(pc) {
fmt.Printf("START ADDR %#v\n", pc)
addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
fmt.Printf("RET ADDR %#v\n", addr)
bp, err := dbp.Break(uintptr(addr))
if err != nil {
if _, ok := err.(BreakPointExistsError); !ok {
return err
}
}
err = dbp.Continue()
if err != nil {
return err
}
err = dbp.clearTempBreakpoint(bp.Addr)
if err != nil {
return err
}
// pc, err = dbp.CurrentPC()
// if err != nil {
// return err
// }
// }
return nil
}

@ -2,12 +2,11 @@ package proctl_test
import (
"bytes"
"fmt"
"os"
"path/filepath"
"syscall"
"testing"
"time"
"github.com/derekparker/dbg/_helper"
"github.com/derekparker/dbg/proctl"
@ -165,18 +164,23 @@ func TestNext(t *testing.T) {
testcases := []struct {
begin, end int
}{
{18, 19},
{19, 22},
{22, 23},
{23, 25},
{25, 22},
{22, 23},
{23, 25},
{25, 22},
{22, 28},
{28, 32},
{32, 33},
{33, 36},
{19, 20},
{20, 23},
{23, 24},
{24, 26},
{26, 31},
{31, 23},
{23, 24},
{24, 26},
{26, 31},
{31, 23},
{23, 24},
{24, 26},
{26, 27},
{27, 34},
{34, 35},
{35, 40},
{40, 41},
}
fp, err := filepath.Abs("../_fixtures/testnextprog.go")
@ -186,11 +190,12 @@ func TestNext(t *testing.T) {
helper.WithTestProcess(executablePath, t, func(p *proctl.DebuggedProcess) {
pc, _, _ := p.GoSymTable.LineToPC(fp, testcases[0].begin)
bp, err := p.Break(uintptr(pc))
_, err := p.Break(uintptr(pc))
assertNoError(err, t, "Break()")
assertNoError(p.Continue(), t, "Continue()")
for _, tc := range testcases {
fmt.Println(tc.begin)
f, ln := currentLineNumber(p, t)
if ln != tc.begin {
t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, f, ln)
@ -207,41 +212,6 @@ func TestNext(t *testing.T) {
if len(p.BreakPoints) != 1 {
t.Fatal("Not all breakpoints were cleaned up")
}
// Test that next will properly clean up after itself.
// Since we kind of spray breakpoints all around, we want to
// make sure here that after next'ing a couple time, we can
// still continue to execute the program without hitting a
// rogue breakpoint.
exited := make(chan interface{})
_, err = p.Clear(bp.Addr)
assertNoError(err, t, "Clear()")
go func() {
select {
case <-time.After(2 * time.Second):
syscall.PtraceDetach(p.Pid)
p.Process.Kill()
os.Exit(1)
case <-exited:
p.Process.Kill()
}
}()
go func() {
_, err := syscall.Wait4(p.Pid, nil, 0, nil)
if err != nil && err != syscall.ECHILD {
fmt.Println(err)
os.Exit(1)
}
exited <- nil
}()
// We must call Continue outside of the goroutine
// because all ptrace commands must be executed
// from the thread that started the trace.
assertNoError(p.Continue(), t, "Continue()")
})
}