delve/dwarf/line/state_machine.go
2014-07-21 18:20:16 -05:00

281 lines
6.0 KiB
Go

package line
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/derekparker/dbg/dwarf/util"
)
type Location struct {
File string
Line int
Address uint64
Delta int
}
type StateMachine struct {
Dbl *DebugLineInfo
File string
Line int
Address uint64
Column uint
IsStmt bool
BasicBlock bool
EndSeq bool
LastWasStandard bool
LastDelta int
}
type opcodefn func(*StateMachine, *bytes.Buffer)
// Special opcodes
const (
DW_LNS_copy = 1
DW_LNS_advance_pc = 2
DW_LNS_advance_line = 3
DW_LNS_set_file = 4
DW_LNS_set_column = 5
DW_LNS_negate_stmt = 6
DW_LNS_set_basic_block = 7
DW_LNS_const_add_pc = 8
DW_LNS_fixed_advance_pc = 9
)
// Extended opcodes
const (
DW_LINE_end_sequence = 1
DW_LINE_set_address = 2
DW_LINE_define_file = 3
)
var standardopcodes = map[byte]opcodefn{
DW_LNS_copy: copyfn,
DW_LNS_advance_pc: advancepc,
DW_LNS_advance_line: advanceline,
DW_LNS_set_file: setfile,
DW_LNS_set_column: setcolumn,
DW_LNS_negate_stmt: negatestmt,
DW_LNS_set_basic_block: setbasicblock,
DW_LNS_const_add_pc: constaddpc,
DW_LNS_fixed_advance_pc: fixedadvancepc,
}
var extendedopcodes = map[byte]opcodefn{
DW_LINE_end_sequence: endsequence,
DW_LINE_set_address: setaddress,
DW_LINE_define_file: definefile,
}
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
return &StateMachine{Dbl: dbl, File: dbl.FileNames[0].Name, Line: 1}
}
// Returns the filename, line number and PC for the next executable line in
// the traced program.
func (dbl *DebugLineInfo) NextLocAfterPC(pc uint64) *Location {
var (
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.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)
if sm.Line > line {
break
}
}
return &Location{sm.File, sm.Line, sm.Address, sm.LastDelta}
}
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.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) {
switch {
case b == 0:
execExtendedOpcode(sm, b, buf)
case b < sm.Dbl.Prologue.OpcodeBase:
execStandardOpcode(sm, b, buf)
default:
execSpecialOpcode(sm, b)
}
}
func execSpecialOpcode(sm *StateMachine, instr byte) {
var (
opcode = uint8(instr)
decoded = opcode - sm.Dbl.Prologue.OpcodeBase
)
if sm.Dbl.Prologue.InitialIsStmt == uint8(1) {
sm.IsStmt = true
}
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
}
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
_, _ = util.DecodeULEB128(buf)
b, _ := buf.ReadByte()
fn, ok := extendedopcodes[b]
if !ok {
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", b))
}
sm.LastWasStandard = false
fn(sm, buf)
}
func execStandardOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
fn, ok := standardopcodes[instr]
if !ok {
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
}
sm.LastWasStandard = true
fn(sm, buf)
}
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
sm.BasicBlock = false
}
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
addr, _ := util.DecodeULEB128(buf)
sm.Address += addr * uint64(sm.Dbl.Prologue.MinInstrLength)
}
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
line, _ := util.DecodeSLEB128(buf)
sm.Line += int(line)
sm.LastDelta = int(line)
}
func setfile(sm *StateMachine, buf *bytes.Buffer) {
i, _ := util.DecodeULEB128(buf)
sm.File = sm.Dbl.FileNames[i-1].Name
}
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
c, _ := util.DecodeULEB128(buf)
sm.Column = uint(c)
}
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
sm.IsStmt = !sm.IsStmt
}
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
sm.BasicBlock = true
}
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
sm.Address += (255 / uint64(sm.Dbl.Prologue.LineRange))
}
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
var operand uint16
binary.Read(buf, binary.LittleEndian, &operand)
sm.Address += uint64(operand)
}
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
sm.EndSeq = true
}
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
var addr uint64
binary.Read(buf, binary.LittleEndian, &addr)
sm.Address = addr
}
func definefile(sm *StateMachine, buf *bytes.Buffer) {
var (
_, _ = util.ParseString(buf)
_, _ = util.DecodeULEB128(buf)
_, _ = util.DecodeULEB128(buf)
_, _ = util.DecodeULEB128(buf)
)
// Don't do anything here yet.
}