提交 6b80a726 编写于 作者: D Derek Parker

Remove DWARF .debug_line parser / util funcs

上级 414db82e
package line
import (
"bytes"
"encoding/binary"
"github.com/derekparker/dbg/dwarf/util"
)
type DebugLinePrologue struct {
Length uint32
Version uint16
PrologueLength uint32
MinInstrLength uint8
InitialIsStmt uint8
LineBase int8
LineRange uint8
OpcodeBase uint8
StdOpLengths []uint8
}
type DebugLineInfo struct {
Prologue *DebugLinePrologue
IncludeDirs []string
FileNames []*FileEntry
Instructions []byte
}
type FileEntry struct {
Name string
DirIdx uint64
LastModTime uint64
Length uint64
}
func Parse(data []byte) *DebugLineInfo {
var (
dbl = new(DebugLineInfo)
buf = bytes.NewBuffer(data)
)
parseDebugLinePrologue(dbl, buf)
parseIncludeDirs(dbl, buf)
parseFileEntries(dbl, buf)
dbl.Instructions = buf.Bytes()
return dbl
}
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
p := new(DebugLinePrologue)
p.Length = binary.LittleEndian.Uint32(buf.Next(4))
p.Version = binary.LittleEndian.Uint16(buf.Next(2))
p.PrologueLength = binary.LittleEndian.Uint32(buf.Next(4))
p.MinInstrLength = uint8(buf.Next(1)[0])
p.InitialIsStmt = uint8(buf.Next(1)[0])
p.LineBase = int8(buf.Next(1)[0])
p.LineRange = uint8(buf.Next(1)[0])
p.OpcodeBase = uint8(buf.Next(1)[0])
p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
dbl.Prologue = p
}
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
for {
str, _ := util.ParseString(buf)
if str == "" {
break
}
info.IncludeDirs = append(info.IncludeDirs, str)
}
}
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
for {
entry := new(FileEntry)
name, _ := util.ParseString(buf)
if name == "" {
break
}
entry.Name = name
entry.DirIdx, _ = util.DecodeULEB128(buf)
entry.LastModTime, _ = util.DecodeULEB128(buf)
entry.Length, _ = util.DecodeULEB128(buf)
info.FileNames = append(info.FileNames, entry)
}
}
package line
import (
"debug/elf"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/davecheney/profile"
)
func grabDebugLineSection(p string, t *testing.T) []byte {
f, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
defer f.Close()
ef, err := elf.NewFile(f)
if err != nil {
t.Fatal(err)
}
data, err := ef.Section(".debug_line").Data()
if err != nil {
t.Fatal(err)
}
return data
}
func TestDebugLinePrologueParser(t *testing.T) {
// Test against known good values, from readelf --debug-dump=rawline _fixtures/testnextprog
p, err := filepath.Abs("../../_fixtures/testnextprog")
if err != nil {
t.Fatal(err)
}
err = exec.Command("go", "build", "-gcflags=-N -l", "-o", p, p+".go").Run()
if err != nil {
t.Fatal("Could not compile test file", p, err)
}
defer os.Remove(p)
data := grabDebugLineSection(p, t)
dbl := Parse(data)
prologue := dbl.Prologue
if prologue.Version != uint16(2) {
t.Fatal("Version not parsed correctly", prologue.Version)
}
if prologue.MinInstrLength != uint8(1) {
t.Fatal("Minimun Instruction Length not parsed correctly", prologue.MinInstrLength)
}
if prologue.InitialIsStmt != uint8(1) {
t.Fatal("Initial value of 'is_stmt' not parsed correctly", prologue.InitialIsStmt)
}
if prologue.LineBase != int8(-1) {
t.Fatal("Line base not parsed correctly", prologue.LineBase)
}
if prologue.LineRange != uint8(4) {
t.Fatal("Line Range not parsed correctly", prologue.LineRange)
}
if prologue.OpcodeBase != uint8(10) {
t.Fatal("Opcode Base not parsed correctly", prologue.OpcodeBase)
}
lengths := []uint8{0, 1, 1, 1, 1, 0, 0, 0, 1}
for i, l := range prologue.StdOpLengths {
if l != lengths[i] {
t.Fatal("Length not parsed correctly", l)
}
}
if len(dbl.IncludeDirs) != 0 {
t.Fatal("Include dirs not parsed correctly")
}
if !strings.Contains(dbl.FileNames[0].Name, "/dbg/_fixtures/testnextprog.go") {
t.Fatal("First entry not parsed correctly")
}
}
func BenchmarkLineParser(b *testing.B) {
defer profile.Start(profile.MemProfile).Stop()
p, err := filepath.Abs("../../_fixtures/testnextprog")
if err != nil {
b.Fatal(err)
}
data := grabDebugLineSection(p, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = Parse(data)
}
}
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) NextLocation(pc uint64, file string, 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.File == file && 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 executeUntilFileAndLine(sm *StateMachine, buf *bytes.Buffer, file string, line int) {
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if sm.Line == line && sm.File == file {
break
}
}
}
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 extended 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.
}
package line
import (
"debug/elf"
"debug/gosym"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
)
func parseGoSymTab(exe *elf.File, t *testing.T) *gosym.Table {
var (
symdat []byte
pclndat []byte
err error
)
if sec := exe.Section(".gosymtab"); sec != nil {
symdat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gosymtab section", err)
os.Exit(1)
}
}
if sec := exe.Section(".gopclntab"); sec != nil {
pclndat, err = sec.Data()
if err != nil {
fmt.Println("could not get .gopclntab section", err)
os.Exit(1)
}
}
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
tab, err := gosym.NewTable(symdat, pcln)
if err != nil {
fmt.Println("could not get initialize line table", err)
os.Exit(1)
}
return tab
}
func TestNextLocAfterPC(t *testing.T) {
testfile, _ := filepath.Abs("../../_fixtures/testnextprog")
p, err := filepath.Abs("../../_fixtures/testnextprog")
if err != nil {
t.Fatal(err)
}
err = exec.Command("go", "build", "-gcflags=-N -l", "-o", p, p+".go").Run()
if err != nil {
t.Fatal("Could not compile test file", p, err)
}
defer os.Remove(p)
var (
data = grabDebugLineSection(p, t)
dbl = Parse(data)
)
f, err := os.Open(p)
if err != nil {
t.Fatal(err)
}
e, err := elf.NewFile(f)
if err != nil {
t.Fatal(err)
}
symtab := parseGoSymTab(e, t)
pc, _, err := symtab.LineToPC(testfile+".go", 23)
if err != nil {
t.Fatal(err)
}
loc := dbl.NextLocation(pc, testfile+".go", 23)
if loc.File != testfile+".go" {
t.Fatal("File not returned correctly", loc.File)
}
if loc.Line != 24 {
t.Fatal("Line not returned correctly", loc.Line)
}
}
......@@ -15,7 +15,6 @@ import (
"unsafe"
"github.com/derekparker/dbg/dwarf/frame"
"github.com/derekparker/dbg/dwarf/line"
"github.com/derekparker/dbg/dwarf/op"
"github.com/derekparker/dbg/vendor/dwarf"
"github.com/derekparker/dbg/vendor/elf"
......@@ -32,7 +31,6 @@ type DebuggedProcess struct {
Symbols []elf.Symbol
GoSymTable *gosym.Table
FrameEntries *frame.FrameDescriptionEntries
DebugLine *line.DebugLineInfo
BreakPoints map[uint64]*BreakPoint
}
......@@ -112,9 +110,8 @@ func (dbp *DebuggedProcess) LoadInformation() error {
return err
}
wg.Add(3)
wg.Add(2)
go dbp.parseDebugFrame(&wg)
go dbp.parseDebugLine(&wg)
go dbp.obtainGoSymbols(&wg)
wg.Wait()
......@@ -641,18 +638,6 @@ func (dbp *DebuggedProcess) findExecutable() error {
return nil
}
func (dbp *DebuggedProcess) parseDebugLine(wg *sync.WaitGroup) {
defer wg.Done()
debugLine, err := dbp.Executable.Section(".debug_line").Data()
if err != nil {
fmt.Println("could not get .debug_line section", err)
os.Exit(1)
}
dbp.DebugLine = line.Parse(debugLine)
}
func (dbp *DebuggedProcess) parseDebugFrame(wg *sync.WaitGroup) {
defer wg.Done()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册