Further improve next command
* Fixes incorrect loop `next`ing behaviour * Includes fix for determining return address
This commit is contained in:
parent
3566fd5237
commit
07fec48272
@ -8,6 +8,13 @@ import (
|
||||
"github.com/derekparker/dbg/dwarf/util"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
File string
|
||||
Line int
|
||||
Address uint64
|
||||
Delta int
|
||||
}
|
||||
|
||||
type StateMachine struct {
|
||||
Dbl *DebugLineInfo
|
||||
File string
|
||||
@ -18,6 +25,7 @@ type StateMachine struct {
|
||||
BasicBlock bool
|
||||
EndSeq bool
|
||||
LastWasStandard bool
|
||||
LastDelta int
|
||||
}
|
||||
|
||||
type opcodefn func(*StateMachine, *bytes.Buffer)
|
||||
@ -66,18 +74,7 @@ func newStateMachine(dbl *DebugLineInfo) *StateMachine {
|
||||
|
||||
// Returns the filename, line number and PC for the next executable line in
|
||||
// the traced program.
|
||||
func (dbl *DebugLineInfo) NextLocAfterPC(pc uint64) (string, int, uint64) {
|
||||
var (
|
||||
sm = newStateMachine(dbl)
|
||||
buf = bytes.NewBuffer(dbl.Instructions)
|
||||
)
|
||||
|
||||
executeUntilPC(sm, buf, pc)
|
||||
|
||||
return sm.File, sm.Line, sm.Address
|
||||
}
|
||||
|
||||
func (dbl *DebugLineInfo) LoopExitLocation(pc uint64) (string, int, uint64) {
|
||||
func (dbl *DebugLineInfo) NextLocAfterPC(pc uint64) *Location {
|
||||
var (
|
||||
sm = newStateMachine(dbl)
|
||||
buf = bytes.NewBuffer(dbl.Instructions)
|
||||
@ -85,6 +82,33 @@ func (dbl *DebugLineInfo) LoopExitLocation(pc uint64) (string, int, uint64) {
|
||||
|
||||
executeUntilPC(sm, buf, pc)
|
||||
line := sm.Line
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
|
||||
if sm.Line > line {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &Location{sm.File, sm.Line, sm.Address, sm.LastDelta}
|
||||
}
|
||||
|
||||
func (dbl *DebugLineInfo) LocationInfoForPC(pc uint64) *Location {
|
||||
var (
|
||||
sm = newStateMachine(dbl)
|
||||
buf = bytes.NewBuffer(dbl.Instructions)
|
||||
)
|
||||
|
||||
executeUntilPC(sm, buf, pc)
|
||||
|
||||
return &Location{sm.File, sm.Line, sm.Address, sm.LastDelta}
|
||||
}
|
||||
|
||||
func (dbl *DebugLineInfo) LoopEntryLocation(line int) *Location {
|
||||
var (
|
||||
sm = newStateMachine(dbl)
|
||||
buf = bytes.NewBuffer(dbl.Instructions)
|
||||
)
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
@ -94,25 +118,48 @@ func (dbl *DebugLineInfo) LoopExitLocation(pc uint64) (string, int, uint64) {
|
||||
}
|
||||
}
|
||||
|
||||
return sm.File, sm.Line, sm.Address
|
||||
return &Location{sm.File, sm.Line, sm.Address, sm.LastDelta}
|
||||
}
|
||||
|
||||
func executeUntilPC(sm *StateMachine, buf *bytes.Buffer, pc uint64) {
|
||||
func (dbl *DebugLineInfo) LoopExitLocation(pc uint64) *Location {
|
||||
var (
|
||||
line int
|
||||
sm = newStateMachine(dbl)
|
||||
buf = bytes.NewBuffer(dbl.Instructions)
|
||||
)
|
||||
|
||||
executeUntilPC(sm, buf, pc)
|
||||
line = sm.Line
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
|
||||
if sm.Address > pc {
|
||||
if sm.LastWasStandard {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
}
|
||||
if sm.Line > line {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &Location{sm.File, sm.Line, sm.Address, sm.LastDelta}
|
||||
}
|
||||
|
||||
func executeUntilPC(sm *StateMachine, buf *bytes.Buffer, pc uint64) {
|
||||
var line int
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
|
||||
if line != 0 && sm.Line > line {
|
||||
break
|
||||
}
|
||||
|
||||
if sm.Address == pc {
|
||||
if !sm.LastWasStandard {
|
||||
break
|
||||
}
|
||||
|
||||
line = sm.Line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
|
||||
@ -136,7 +183,8 @@ func execSpecialOpcode(sm *StateMachine, instr byte) {
|
||||
sm.IsStmt = true
|
||||
}
|
||||
|
||||
sm.Line += int(sm.Dbl.Prologue.LineBase + int8(decoded%sm.Dbl.Prologue.LineRange))
|
||||
sm.LastDelta = int(sm.Dbl.Prologue.LineBase + int8(decoded%sm.Dbl.Prologue.LineRange))
|
||||
sm.Line += sm.LastDelta
|
||||
sm.Address += uint64(decoded / sm.Dbl.Prologue.LineRange)
|
||||
sm.BasicBlock = false
|
||||
sm.LastWasStandard = false
|
||||
@ -174,8 +222,9 @@ func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
}
|
||||
|
||||
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
||||
l, _ := util.DecodeSLEB128(buf)
|
||||
sm.Line += int(l)
|
||||
line, _ := util.DecodeSLEB128(buf)
|
||||
sm.Line += int(line)
|
||||
sm.LastDelta = int(line)
|
||||
}
|
||||
|
||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
|
||||
@ -21,13 +21,13 @@ func TestNextLocAfterPC(t *testing.T) {
|
||||
pc, _, _ = gosym.LineToPC(testfile+".go", 20)
|
||||
)
|
||||
|
||||
f, l, _ := dbl.NextLocAfterPC(pc)
|
||||
loc := dbl.NextLocAfterPC(pc)
|
||||
|
||||
if f != testfile+".go" {
|
||||
t.Fatal("File not returned correctly", f)
|
||||
if loc.File != testfile+".go" {
|
||||
t.Fatal("File not returned correctly", loc.File)
|
||||
}
|
||||
|
||||
if l != 22 {
|
||||
t.Fatal("Line not returned correctly", l)
|
||||
if loc.Line != 22 {
|
||||
t.Fatal("Line not returned correctly", loc.Line)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
package proctl
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
@ -40,6 +41,16 @@ type BreakPoint struct {
|
||||
OriginalData []byte
|
||||
}
|
||||
|
||||
type BreakPointExistsError struct {
|
||||
file string
|
||||
line int
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
func (bpe BreakPointExistsError) Error() string {
|
||||
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
|
||||
}
|
||||
|
||||
// Returns a new DebuggedProcess struct with sensible defaults.
|
||||
func NewDebugProcess(pid int) (*DebuggedProcess, error) {
|
||||
proc, err := os.FindProcess(pid)
|
||||
@ -122,6 +133,10 @@ func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(originalData, int3) {
|
||||
return nil, BreakPointExistsError{f, l, addr}
|
||||
}
|
||||
|
||||
_, err = syscall.PtracePokeData(dbp.Pid, addr, int3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -196,47 +211,41 @@ func (dbp *DebuggedProcess) Step() (err error) {
|
||||
|
||||
// Step over function calls.
|
||||
func (dbp *DebuggedProcess) Next() error {
|
||||
addrs := make([]uint64, 0, 3)
|
||||
pc, err := dbp.CurrentPC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, l, _ := dbp.GoSymTable.PCToLine(pc)
|
||||
fde, _ := dbp.FrameEntries.FDEForPC(pc)
|
||||
_, nl, addr := dbp.DebugLine.NextLocAfterPC(pc)
|
||||
|
||||
if !fde.AddressRange.Cover(addr) {
|
||||
offset := fde.ReturnAddressOffset(pc)
|
||||
addr = dbp.ReturnAddressFromOffset(offset)
|
||||
fde, err := dbp.FrameEntries.FDEForPC(pc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nl < l {
|
||||
// We are likely in a loop, set a breakpoint at the
|
||||
// first instruction following the loop.
|
||||
_, _, loopaddr := dbp.DebugLine.LoopExitLocation(pc)
|
||||
_, ok := dbp.PCtoBP(loopaddr)
|
||||
if !ok {
|
||||
_, err = dbp.Break(uintptr(loopaddr))
|
||||
if err != nil {
|
||||
loc := dbp.DebugLine.NextLocAfterPC(pc - 1)
|
||||
if !fde.AddressRange.Cover(loc.Address) {
|
||||
// Next line is outside current frame, use return addr.
|
||||
addr := dbp.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
|
||||
loc = dbp.DebugLine.LocationInfoForPC(addr)
|
||||
}
|
||||
addrs = append(addrs, loc.Address)
|
||||
|
||||
if loc.Delta < 0 {
|
||||
// We are likely in a loop, set breakpoints at entry and exit.
|
||||
entry := dbp.DebugLine.LoopEntryLocation(loc.Line)
|
||||
exit := dbp.DebugLine.LoopExitLocation(pc - 1)
|
||||
addrs = append(addrs, entry.Address, exit.Address)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if _, err := dbp.Break(uintptr(addr)); err != nil {
|
||||
if _, ok := err.(BreakPointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, ok := dbp.PCtoBP(addr)
|
||||
if !ok {
|
||||
_, err = dbp.Break(uintptr(addr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = dbp.Continue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// Continue process until next breakpoint.
|
||||
|
||||
@ -185,6 +185,7 @@ func TestNext(t *testing.T) {
|
||||
{22, 25},
|
||||
{25, 26},
|
||||
{26, 30},
|
||||
{30, 31},
|
||||
}
|
||||
|
||||
fp, err := filepath.Abs("../_fixtures/testnextprog.go")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user