diff --git a/_fixtures/testnextprog.go b/_fixtures/testnextprog.go index fe755258..05651acb 100644 --- a/_fixtures/testnextprog.go +++ b/_fixtures/testnextprog.go @@ -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() } diff --git a/dwarf/frame/frame_table.go b/dwarf/frame/frame_table.go index 231bc2b0..916c46a1 100644 --- a/dwarf/frame/frame_table.go +++ b/dwarf/frame/frame_table.go @@ -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) { diff --git a/dwarf/frame/frame_table_test.go b/dwarf/frame/frame_table_test.go index 35f7b45f..992a3e18 100644 --- a/dwarf/frame/frame_table_test.go +++ b/dwarf/frame/frame_table_test.go @@ -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) } }) diff --git a/dwarf/line/state_machine.go b/dwarf/line/state_machine.go index 0edd026f..5e8db426 100644 --- a/dwarf/line/state_machine.go +++ b/dwarf/line/state_machine.go @@ -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 } } diff --git a/dwarf/line/state_machine_test.go b/dwarf/line/state_machine_test.go index d3cc863a..01cafd23 100644 --- a/dwarf/line/state_machine_test.go +++ b/dwarf/line/state_machine_test.go @@ -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) } } diff --git a/proctl/proctl_linux_amd64.go b/proctl/proctl_linux_amd64.go index 98143cf0..d07acac9 100644 --- a/proctl/proctl_linux_amd64.go +++ b/proctl/proctl_linux_amd64.go @@ -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 } diff --git a/proctl/proctl_test.go b/proctl/proctl_test.go index a7d73100..3f5ecb9d 100644 --- a/proctl/proctl_test.go +++ b/proctl/proctl_test.go @@ -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()") }) }