Fix Next impl
Needs some refactoring and some optimization, but fixes several bugs.
This commit is contained in:
parent
9084276012
commit
6a71009954
@ -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()")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user