proc: replace all uses of gosymtab/gopclntab with uses of debug_line
gosymtab and gopclntab only contain informations about go code, linked C code isn't there, we should use debug_line instead to also cover C. Updates #935
This commit is contained in:
parent
913153e7ff
commit
6d40517944
@ -3,6 +3,7 @@ package line
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
@ -28,7 +29,7 @@ type DebugLineInfo struct {
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Name string
|
||||
Path string
|
||||
DirIdx uint64
|
||||
LastModTime uint64
|
||||
Length uint64
|
||||
@ -36,17 +37,8 @@ type FileEntry struct {
|
||||
|
||||
type DebugLines []*DebugLineInfo
|
||||
|
||||
func (d *DebugLines) GetLineInfo(name string) *DebugLineInfo {
|
||||
// Find in which table file exists and return it.
|
||||
for _, l := range *d {
|
||||
if _, ok := l.Lookup[name]; ok {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Parse(data []byte) DebugLines {
|
||||
// ParseAll parses all debug_line segments found in data
|
||||
func ParseAll(data []byte) DebugLines {
|
||||
var (
|
||||
lines = make(DebugLines, 0)
|
||||
buf = bytes.NewBuffer(data)
|
||||
@ -54,8 +46,20 @@ func Parse(data []byte) DebugLines {
|
||||
|
||||
// We have to parse multiple file name tables here.
|
||||
for buf.Len() > 0 {
|
||||
lines = append(lines, Parse("", buf))
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
// Parse parses a single debug_line segment from buf. Compdir is the
|
||||
// DW_AT_comp_dir attribute of the associated compile unit.
|
||||
func Parse(compdir string, buf *bytes.Buffer) *DebugLineInfo {
|
||||
dbl := new(DebugLineInfo)
|
||||
dbl.Lookup = make(map[string]*FileEntry)
|
||||
if compdir != "" {
|
||||
dbl.IncludeDirs = append(dbl.IncludeDirs, compdir)
|
||||
}
|
||||
|
||||
parseDebugLinePrologue(dbl, buf)
|
||||
parseIncludeDirs(dbl, buf)
|
||||
@ -67,10 +71,7 @@ func Parse(data []byte) DebugLines {
|
||||
// - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
|
||||
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
|
||||
|
||||
lines = append(lines, dbl)
|
||||
}
|
||||
|
||||
return lines
|
||||
return dbl
|
||||
}
|
||||
|
||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||
@ -106,17 +107,21 @@ func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
for {
|
||||
entry := new(FileEntry)
|
||||
|
||||
name, _ := util.ParseString(buf)
|
||||
if name == "" {
|
||||
entry.Path, _ = util.ParseString(buf)
|
||||
if entry.Path == "" {
|
||||
break
|
||||
}
|
||||
|
||||
entry.Name = name
|
||||
entry.DirIdx, _ = util.DecodeULEB128(buf)
|
||||
entry.LastModTime, _ = util.DecodeULEB128(buf)
|
||||
entry.Length, _ = util.DecodeULEB128(buf)
|
||||
if !filepath.IsAbs(entry.Path) {
|
||||
if entry.DirIdx >= 0 && entry.DirIdx < uint64(len(info.IncludeDirs)) {
|
||||
entry.Path = filepath.Join(info.IncludeDirs[entry.DirIdx], entry.Path)
|
||||
}
|
||||
}
|
||||
|
||||
info.FileNames = append(info.FileNames, entry)
|
||||
info.Lookup[name] = entry
|
||||
info.Lookup[entry.Path] = entry
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ const (
|
||||
|
||||
func testDebugLinePrologueParser(p string, t *testing.T) {
|
||||
data := grabDebugLineSection(p, t)
|
||||
debugLines := Parse(data)
|
||||
debugLines := ParseAll(data)
|
||||
dbl := debugLines[0]
|
||||
prologue := dbl.Prologue
|
||||
|
||||
@ -105,7 +105,7 @@ func testDebugLinePrologueParser(p string, t *testing.T) {
|
||||
|
||||
ok := false
|
||||
for _, n := range dbl.FileNames {
|
||||
if strings.Contains(n.Name, "/delve/_fixtures/testnextprog.go") {
|
||||
if strings.Contains(n.Path, "/delve/_fixtures/testnextprog.go") {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
@ -154,6 +154,6 @@ func BenchmarkLineParser(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = Parse(data)
|
||||
_ = ParseAll(data)
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,11 @@ const (
|
||||
DW_LNS_set_basic_block = 7
|
||||
DW_LNS_const_add_pc = 8
|
||||
DW_LNS_fixed_advance_pc = 9
|
||||
|
||||
// DWARF v4
|
||||
DW_LNS_set_prologue_end = 10
|
||||
DW_LNS_set_epilouge_begin = 11
|
||||
DW_LNS_set_isa = 12
|
||||
)
|
||||
|
||||
// Extended opcodes
|
||||
@ -66,6 +71,11 @@ var standardopcodes = map[byte]opcodefn{
|
||||
DW_LNS_set_basic_block: setbasicblock,
|
||||
DW_LNS_const_add_pc: constaddpc,
|
||||
DW_LNS_fixed_advance_pc: fixedadvancepc,
|
||||
|
||||
// DWARF v4
|
||||
DW_LNS_set_prologue_end: donothing0,
|
||||
DW_LNS_set_epilouge_begin: donothing0,
|
||||
DW_LNS_set_isa: donothing1,
|
||||
}
|
||||
|
||||
var extendedopcodes = map[byte]opcodefn{
|
||||
@ -75,16 +85,19 @@ var extendedopcodes = map[byte]opcodefn{
|
||||
}
|
||||
|
||||
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
|
||||
return &StateMachine{dbl: dbl, file: dbl.FileNames[0].Name, line: 1}
|
||||
return &StateMachine{dbl: dbl, file: dbl.FileNames[0].Path, line: 1}
|
||||
}
|
||||
|
||||
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
|
||||
// could be split amongst 2 PCs.
|
||||
func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
||||
func (lineInfo *DebugLineInfo) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
||||
if lineInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
foundFile bool
|
||||
lastAddr uint64
|
||||
lineInfo = dbl.GetLineInfo(f)
|
||||
sm = newStateMachine(lineInfo)
|
||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
||||
)
|
||||
@ -116,11 +129,11 @@ func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
||||
|
||||
var NoSourceError = errors.New("no source available")
|
||||
|
||||
func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint64, error) {
|
||||
lineInfo := dbl.GetLineInfo(filename)
|
||||
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64) ([]uint64, error) {
|
||||
if lineInfo == nil {
|
||||
return nil, NoSourceError
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
pcs []uint64
|
||||
lastaddr uint64
|
||||
@ -144,6 +157,63 @@ func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint
|
||||
return pcs, nil
|
||||
}
|
||||
|
||||
// PCToLine returns the filename and line number associated with pc.
|
||||
// If pc isn't found inside lineInfo's table it will return the filename and
|
||||
// line number associated with the closest PC address preceding pc.
|
||||
func (lineInfo *DebugLineInfo) PCToLine(pc uint64) (string, int) {
|
||||
if lineInfo == nil {
|
||||
return "", 0
|
||||
}
|
||||
|
||||
var (
|
||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
||||
sm = newStateMachine(lineInfo)
|
||||
lastFilename string
|
||||
lastLineno int
|
||||
)
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
if !sm.valid {
|
||||
continue
|
||||
}
|
||||
if sm.address > pc {
|
||||
return lastFilename, lastLineno
|
||||
}
|
||||
if sm.address == pc {
|
||||
return sm.file, sm.line
|
||||
}
|
||||
lastFilename, lastLineno = sm.file, sm.line
|
||||
}
|
||||
return "", 0
|
||||
}
|
||||
|
||||
// LineToPC returns the first PC address associated with filename:lineno.
|
||||
func (lineInfo *DebugLineInfo) LineToPC(filename string, lineno int) uint64 {
|
||||
if lineInfo == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
var (
|
||||
foundFile bool
|
||||
sm = newStateMachine(lineInfo)
|
||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
||||
)
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
if foundFile && sm.file != filename {
|
||||
break
|
||||
}
|
||||
if sm.line == lineno && sm.file == filename {
|
||||
foundFile = true
|
||||
if sm.valid {
|
||||
return sm.address
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
|
||||
switch {
|
||||
case b == 0:
|
||||
@ -215,7 +285,7 @@ func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
||||
|
||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
i, _ := util.DecodeULEB128(buf)
|
||||
sm.file = sm.dbl.FileNames[i-1].Name
|
||||
sm.file = sm.dbl.FileNames[i-1].Path
|
||||
}
|
||||
|
||||
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
|
||||
@ -242,6 +312,15 @@ func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.address += uint64(operand)
|
||||
}
|
||||
|
||||
func donothing0(sm *StateMachine, buf *bytes.Buffer) {
|
||||
// does nothing, no operands
|
||||
}
|
||||
|
||||
func donothing1(sm *StateMachine, buf *bytes.Buffer) {
|
||||
// does nothing, consumes one operand
|
||||
util.DecodeSLEB128(buf)
|
||||
}
|
||||
|
||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.endSeq = true
|
||||
sm.valid = true
|
||||
|
@ -3,13 +3,14 @@ package proc
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"debug/elf"
|
||||
"debug/gosym"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -31,13 +32,18 @@ type BinaryInfo struct {
|
||||
Arch Arch
|
||||
dwarf *dwarf.Data
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
lineInfo line.DebugLines
|
||||
goSymTable *gosym.Table
|
||||
compileUnits []*compileUnit
|
||||
types map[string]dwarf.Offset
|
||||
packageVars map[string]dwarf.Offset
|
||||
functions []functionDebugInfo
|
||||
gStructOffset uint64
|
||||
|
||||
// Functions is a list of all DW_TAG_subprogram entries in debug_info.
|
||||
Functions []Function
|
||||
// Sources is a list of all source files found in debug_line.
|
||||
Sources []string
|
||||
// LookupFunc maps function names to a description of the function.
|
||||
LookupFunc map[string]*Function
|
||||
|
||||
typeCache map[dwarf.Offset]godwarf.Type
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
@ -52,6 +58,64 @@ var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/
|
||||
var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
||||
|
||||
type compileUnit struct {
|
||||
entry *dwarf.Entry // debug_info entry describing this compile unit
|
||||
isgo bool // true if this is the go compile unit
|
||||
Name string // univocal name for non-go compile units
|
||||
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
|
||||
LowPC, HighPC uint64
|
||||
}
|
||||
|
||||
// Function describes a function in the target program.
|
||||
type Function struct {
|
||||
Name string
|
||||
Entry, End uint64 // same as DW_AT_lowpc and DW_AT_highpc
|
||||
offset dwarf.Offset
|
||||
cu *compileUnit
|
||||
}
|
||||
|
||||
// PackageName returns the package part of the symbol name,
|
||||
// or the empty string if there is none.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) PackageName() string {
|
||||
pathend := strings.LastIndex(fn.Name, "/")
|
||||
if pathend < 0 {
|
||||
pathend = 0
|
||||
}
|
||||
|
||||
if i := strings.Index(fn.Name[pathend:], "."); i != -1 {
|
||||
return fn.Name[:pathend+i]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ReceiverName returns the receiver type name of this symbol,
|
||||
// or the empty string if there is none.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) ReceiverName() string {
|
||||
pathend := strings.LastIndex(fn.Name, "/")
|
||||
if pathend < 0 {
|
||||
pathend = 0
|
||||
}
|
||||
l := strings.Index(fn.Name[pathend:], ".")
|
||||
r := strings.LastIndex(fn.Name[pathend:], ".")
|
||||
if l == -1 || r == -1 || l == r {
|
||||
return ""
|
||||
}
|
||||
return fn.Name[pathend+l+1 : pathend+r]
|
||||
}
|
||||
|
||||
// BaseName returns the symbol name without the package or receiver name.
|
||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
|
||||
func (fn *Function) BaseName() string {
|
||||
if i := strings.LastIndex(fn.Name, "."); i != -1 {
|
||||
return fn.Name[i+1:]
|
||||
}
|
||||
return fn.Name
|
||||
}
|
||||
|
||||
func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
||||
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||
|
||||
@ -96,16 +160,6 @@ func (bi *BinaryInfo) DwarfReader() *reader.Reader {
|
||||
return reader.New(bi.dwarf)
|
||||
}
|
||||
|
||||
// Sources returns list of source files that comprise the debugged binary.
|
||||
func (bi *BinaryInfo) Sources() map[string]*gosym.Obj {
|
||||
return bi.goSymTable.Files
|
||||
}
|
||||
|
||||
// Funcs returns list of functions present in the debugged program.
|
||||
func (bi *BinaryInfo) Funcs() []gosym.Func {
|
||||
return bi.goSymTable.Funcs
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (bi *BinaryInfo) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(bi.types))
|
||||
@ -116,18 +170,44 @@ func (bi *BinaryInfo) Types() ([]string, error) {
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return bi.goSymTable.PCToLine(pc)
|
||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) {
|
||||
fn := bi.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return "", 0, nil
|
||||
}
|
||||
f, ln := fn.cu.lineInfo.PCToLine(pc)
|
||||
return f, ln, fn
|
||||
}
|
||||
|
||||
// LineToPC converts a file:line into a memory address.
|
||||
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *gosym.Func, err error) {
|
||||
return bi.goSymTable.LineToPC(filename, lineno)
|
||||
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Function, err error) {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.lineInfo.Lookup[filename] != nil {
|
||||
pc = cu.lineInfo.LineToPC(filename, lineno)
|
||||
fn = bi.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
err = fmt.Errorf("no code at %s:%d", filename, lineno)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("could not find %s:%d", filename, lineno)
|
||||
return
|
||||
}
|
||||
|
||||
// PCToFunc returns the function containing the given PC address
|
||||
func (bi *BinaryInfo) PCToFunc(pc uint64) *gosym.Func {
|
||||
return bi.goSymTable.PCToFunc(pc)
|
||||
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
||||
i := sort.Search(len(bi.Functions), func(i int) bool {
|
||||
fn := bi.Functions[i]
|
||||
return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End)
|
||||
})
|
||||
if i != len(bi.Functions) {
|
||||
fn := &bi.Functions[i]
|
||||
if fn.Entry <= pc && pc < fn.End {
|
||||
return fn
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) Close() error {
|
||||
@ -164,11 +244,14 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(5)
|
||||
debugLineBytes, err := getDebugLineInfoElf(elfFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(3)
|
||||
go bi.parseDebugFrameElf(elfFile, wg)
|
||||
go bi.obtainGoSymbolsElf(elfFile, wg)
|
||||
go bi.parseDebugLineInfoElf(elfFile, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||
go bi.setGStructOffsetElf(elfFile, wg)
|
||||
return nil
|
||||
}
|
||||
@ -197,55 +280,15 @@ func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) obtainGoSymbolsElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section(".gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .gosymtab section: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section(".gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .gopclntab section: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get initialize line table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.goSymTable = tab
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
func getDebugLineInfoElf(exe *elf.File) ([]byte, error) {
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section(".debug_line").Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .debug_line section: %v", err)
|
||||
return
|
||||
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
bi.setLoadError("could not find .debug_line section in binary")
|
||||
return
|
||||
return debugLine, nil
|
||||
}
|
||||
return nil, errors.New("could not find .debug_line section in binary")
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
@ -302,11 +345,14 @@ func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(4)
|
||||
debugLineBytes, err := getDebugLineInfoPE(peFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
go bi.parseDebugFramePE(peFile, wg)
|
||||
go bi.obtainGoSymbolsPE(peFile, wg)
|
||||
go bi.parseDebugLineInfoPE(peFile, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||
|
||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||
// to G struct per:
|
||||
@ -356,25 +402,6 @@ func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) obtainGoSymbolsPE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, symdat, pclndat, err := pclnPE(exe)
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get Go symbols: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get initialize line table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.goSymTable = tab
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
@ -445,23 +472,18 @@ func pclnPE(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error)
|
||||
return textStart, symtab, pclntab, nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoPE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
func getDebugLineInfoPE(exe *pe.File) ([]byte, error) {
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := sec.Data()
|
||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||
bi.setLoadError("could not get .debug_line section: %v", err)
|
||||
return
|
||||
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
debugLine = debugLine[:sec.VirtualSize]
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
bi.setLoadError("could not find .debug_line section in binary")
|
||||
return
|
||||
return debugLine, nil
|
||||
}
|
||||
return nil, errors.New("could not find .debug_line section in binary")
|
||||
}
|
||||
|
||||
// MACH-O ////////////////////////////////////////////////////////////
|
||||
@ -480,11 +502,14 @@ func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(4)
|
||||
debugLineBytes, err := getDebugLineInfoMacho(exe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(2)
|
||||
go bi.parseDebugFrameMacho(exe, wg)
|
||||
go bi.obtainGoSymbolsMacho(exe, wg)
|
||||
go bi.parseDebugLineInfoMacho(exe, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||
bi.gStructOffset = 0x8a0
|
||||
return nil
|
||||
}
|
||||
@ -513,53 +538,13 @@ func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup)
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) obtainGoSymbolsMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section("__gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .gosymtab section: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section("__gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get .gopclntab section: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get initialize line table: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
bi.goSymTable = tab
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
func getDebugLineInfoMacho(exe *macho.File) ([]byte, error) {
|
||||
if sec := exe.Section("__debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section("__debug_line").Data()
|
||||
if err != nil {
|
||||
bi.setLoadError("could not get __debug_line section: %v", err)
|
||||
return
|
||||
return nil, fmt.Errorf("could not get __debug_line section: %v", err)
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
bi.setLoadError("could not find __debug_line section in binary")
|
||||
return
|
||||
return debugLine, nil
|
||||
}
|
||||
return nil, errors.New("could not find __debug_line section in binary")
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
@ -141,7 +140,7 @@ func init() {
|
||||
|
||||
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
|
||||
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
|
||||
func FirstPCAfterPrologue(p Process, fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error) {
|
||||
var mem MemoryReadWriter = p.CurrentThread()
|
||||
breakpoints := p.Breakpoints()
|
||||
bi := p.BinInfo()
|
||||
|
@ -1,7 +1,6 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -11,11 +10,6 @@ import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type functionDebugInfo struct {
|
||||
lowpc, highpc uint64
|
||||
offset dwarf.Offset
|
||||
}
|
||||
|
||||
var NotExecutableErr = errors.New("not an executable file")
|
||||
var NotRecordedErr = errors.New("not a recording")
|
||||
|
||||
@ -35,7 +29,7 @@ func (pe ProcessExitedError) Error() string {
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := p.BinInfo().goSymTable.LineToPC(fileName, lineno)
|
||||
pc, fn, err := p.BinInfo().LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -53,7 +47,7 @@ func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
bi := p.BinInfo()
|
||||
origfn := bi.goSymTable.LookupFunc(funcName)
|
||||
origfn := bi.LookupFunc[funcName]
|
||||
if origfn == nil {
|
||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
||||
}
|
||||
@ -61,8 +55,8 @@ func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset
|
||||
if firstLine {
|
||||
return FirstPCAfterPrologue(p, origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno, _ := bi.goSymTable.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := bi.goSymTable.LineToPC(filename, lineno+lineOffset)
|
||||
filename, lineno := origfn.cu.lineInfo.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := bi.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,6 @@ type Stackframe struct {
|
||||
CFA int64
|
||||
// High address of the stack.
|
||||
StackHi uint64
|
||||
// Description of the stack frame.
|
||||
FDE *frame.FrameDescriptionEntry
|
||||
// Return address for this stack frame (as read from the stack frame itself).
|
||||
Ret uint64
|
||||
// Address to the memory location containing the return address
|
||||
@ -108,11 +106,11 @@ type savedLR struct {
|
||||
}
|
||||
|
||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
||||
stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
|
||||
stackBarrierFunc := bi.LookupFunc[runtimeStackBarrier] // stack barriers were removed in Go 1.9
|
||||
var stackBarrierPC uint64
|
||||
if stackBarrierFunc != nil && stkbar != nil {
|
||||
stackBarrierPC = stackBarrierFunc.Entry
|
||||
fn := bi.goSymTable.PCToFunc(pc)
|
||||
fn := bi.PCToFunc(pc)
|
||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
||||
// We caught the goroutine as it's executing the stack barrier, we must
|
||||
// determine whether or not g.stackPos has already been incremented or not.
|
||||
@ -211,7 +209,7 @@ func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fd
|
||||
if err != nil {
|
||||
it.err = err
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi}
|
||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi}
|
||||
if !top {
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
|
||||
r.Call.PC = r.Current.PC
|
||||
|
@ -1,9 +1,9 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
@ -42,7 +42,7 @@ type Location struct {
|
||||
PC uint64
|
||||
File string
|
||||
Line int
|
||||
Fn *gosym.Func
|
||||
Fn *Function
|
||||
}
|
||||
|
||||
// ThreadBlockedError is returned when the thread
|
||||
@ -104,6 +104,10 @@ func next(dbp Process, stepInto bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if topframe.Current.Fn == nil {
|
||||
return fmt.Errorf("no source for pc %#x", topframe.Current.PC)
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
@ -123,7 +127,7 @@ func next(dbp Process, stepInto bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.FDE.Begin(), topframe.FDE.End())
|
||||
text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.Current.Fn.Entry, topframe.Current.Fn.End)
|
||||
if err != nil && stepInto {
|
||||
return err
|
||||
}
|
||||
@ -203,7 +207,7 @@ func next(dbp Process, stepInto bool) error {
|
||||
}
|
||||
|
||||
// Add breakpoints on all the lines in the current function
|
||||
pcs, err := dbp.BinInfo().lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
|
||||
pcs, err := topframe.Current.Fn.cu.lineInfo.AllPCsBetween(topframe.Current.Fn.Entry, topframe.Current.Fn.End-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -211,14 +215,14 @@ func next(dbp Process, stepInto bool) error {
|
||||
if !csource {
|
||||
var covered bool
|
||||
for i := range pcs {
|
||||
if topframe.FDE.Cover(pcs[i]) {
|
||||
if topframe.Current.Fn.Entry <= pcs[i] && pcs[i] < topframe.Current.Fn.End {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
fn := dbp.BinInfo().goSymTable.PCToFunc(topframe.Ret)
|
||||
fn := dbp.BinInfo().PCToFunc(topframe.Ret)
|
||||
if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||
return nil
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
@ -16,6 +17,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
)
|
||||
|
||||
@ -155,62 +157,128 @@ func (bi *BinaryInfo) loadPackageMap() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type sortFunctionsDebugInfoByLowpc []functionDebugInfo
|
||||
type functionsDebugInfoByEntry []Function
|
||||
|
||||
func (v sortFunctionsDebugInfoByLowpc) Len() int { return len(v) }
|
||||
func (v sortFunctionsDebugInfoByLowpc) Less(i, j int) bool { return v[i].lowpc < v[j].lowpc }
|
||||
func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) {
|
||||
temp := v[i]
|
||||
v[i] = v[j]
|
||||
v[j] = temp
|
||||
}
|
||||
func (v functionsDebugInfoByEntry) Len() int { return len(v) }
|
||||
func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry }
|
||||
func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
||||
|
||||
func (bi *BinaryInfo) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
||||
type compileUnitsByLowpc []*compileUnit
|
||||
|
||||
func (v compileUnitsByLowpc) Len() int { return len(v) }
|
||||
func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].LowPC < v[j].LowPC }
|
||||
func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
||||
|
||||
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
bi.types = make(map[string]dwarf.Offset)
|
||||
bi.packageVars = make(map[string]dwarf.Offset)
|
||||
bi.functions = []functionDebugInfo{}
|
||||
bi.Functions = []Function{}
|
||||
bi.compileUnits = []*compileUnit{}
|
||||
reader := bi.DwarfReader()
|
||||
var cu *compileUnit = nil
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch entry.Tag {
|
||||
case dwarf.TagCompileUnit:
|
||||
cu = &compileUnit{}
|
||||
cu.entry = entry
|
||||
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
||||
cu.isgo = true
|
||||
}
|
||||
cu.Name, _ = entry.Val(dwarf.AttrName).(string)
|
||||
compdir, _ := entry.Val(dwarf.AttrCompDir).(string)
|
||||
if compdir != "" {
|
||||
cu.Name = filepath.Join(compdir, cu.Name)
|
||||
}
|
||||
if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
|
||||
cu.LowPC = ranges[0][0]
|
||||
cu.HighPC = ranges[0][1]
|
||||
}
|
||||
lineInfoOffset, _ := entry.Val(dwarf.AttrStmtList).(int64)
|
||||
if lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) {
|
||||
cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]))
|
||||
}
|
||||
bi.compileUnits = append(bi.compileUnits, cu)
|
||||
|
||||
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
||||
if name, ok := entry.Val(dwarf.AttrName).(string); ok {
|
||||
if !cu.isgo {
|
||||
name = "C." + name
|
||||
}
|
||||
if _, exists := bi.types[name]; !exists {
|
||||
bi.types[name] = entry.Offset
|
||||
}
|
||||
}
|
||||
reader.SkipChildren()
|
||||
|
||||
case dwarf.TagVariable:
|
||||
if n, ok := entry.Val(dwarf.AttrName).(string); ok {
|
||||
if !cu.isgo {
|
||||
n = "C." + n
|
||||
}
|
||||
bi.packageVars[n] = entry.Offset
|
||||
}
|
||||
|
||||
case dwarf.TagSubprogram:
|
||||
lowpc, ok1 := entry.Val(dwarf.AttrLowpc).(uint64)
|
||||
highpc, ok2 := entry.Val(dwarf.AttrHighpc).(uint64)
|
||||
ok1 := false
|
||||
var lowpc, highpc uint64
|
||||
if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
|
||||
ok1 = true
|
||||
lowpc = ranges[0][0]
|
||||
highpc = ranges[0][1]
|
||||
}
|
||||
name, ok2 := entry.Val(dwarf.AttrName).(string)
|
||||
if ok1 && ok2 {
|
||||
bi.functions = append(bi.functions, functionDebugInfo{lowpc, highpc, entry.Offset})
|
||||
if !cu.isgo {
|
||||
name = "C." + name
|
||||
}
|
||||
bi.Functions = append(bi.Functions, Function{
|
||||
Name: name,
|
||||
Entry: lowpc, End: highpc,
|
||||
offset: entry.Offset,
|
||||
cu: cu,
|
||||
})
|
||||
}
|
||||
reader.SkipChildren()
|
||||
|
||||
}
|
||||
}
|
||||
sort.Sort(sortFunctionsDebugInfoByLowpc(bi.functions))
|
||||
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
|
||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||
|
||||
bi.LookupFunc = make(map[string]*Function)
|
||||
for i := range bi.Functions {
|
||||
bi.LookupFunc[bi.Functions[i].Name] = &bi.Functions[i]
|
||||
}
|
||||
|
||||
bi.Sources = []string{}
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.lineInfo != nil {
|
||||
for _, fileEntry := range cu.lineInfo.FileNames {
|
||||
bi.Sources = append(bi.Sources, fileEntry.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(bi.Sources)
|
||||
bi.Sources = uniq(bi.Sources)
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
|
||||
i := sort.Search(len(bi.functions), func(i int) bool {
|
||||
fn := bi.functions[i]
|
||||
return pc <= fn.lowpc || (fn.lowpc <= pc && pc < fn.highpc)
|
||||
})
|
||||
if i != len(bi.functions) {
|
||||
fn := bi.functions[i]
|
||||
if fn.lowpc <= pc && pc < fn.highpc {
|
||||
return fn.offset, nil
|
||||
func uniq(s []string) []string {
|
||||
if len(s) <= 0 {
|
||||
return s
|
||||
}
|
||||
src, dst := 1, 1
|
||||
for src < len(s) {
|
||||
if s[src] != s[dst-1] {
|
||||
s[dst] = s[src]
|
||||
dst++
|
||||
}
|
||||
return 0, errors.New("unable to find function context")
|
||||
src++
|
||||
}
|
||||
return s[:dst]
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
|
||||
|
@ -511,7 +511,7 @@ func (g *G) UserCurrent() Location {
|
||||
// Go returns the location of the 'go' statement
|
||||
// that spawned this goroutine.
|
||||
func (g *G) Go() Location {
|
||||
f, l, fn := g.variable.bi.goSymTable.PCToLine(g.GoPC)
|
||||
f, l, fn := g.variable.bi.PCToLine(g.GoPC)
|
||||
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
@ -1203,7 +1203,7 @@ func (v *Variable) readFunctionPtr() {
|
||||
}
|
||||
|
||||
v.Base = uintptr(binary.LittleEndian.Uint64(val))
|
||||
fn := v.bi.goSymTable.PCToFunc(uint64(v.Base))
|
||||
fn := v.bi.PCToFunc(uint64(v.Base))
|
||||
if fn == nil {
|
||||
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
|
||||
return
|
||||
@ -1660,14 +1660,14 @@ func (v *variablesByDepth) Swap(i int, j int) {
|
||||
|
||||
// Fetches all variables of a specific type in the current function scope
|
||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Variable, error) {
|
||||
off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
fn := scope.BinInfo.PCToFunc(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, errors.New("unable to find function context")
|
||||
}
|
||||
|
||||
var vars []*Variable
|
||||
var depths []int
|
||||
varReader := reader.Variables(scope.BinInfo.dwarf, off, scope.PC, tag == dwarf.TagVariable)
|
||||
varReader := reader.Variables(scope.BinInfo.dwarf, fn.offset, scope.PC, tag == dwarf.TagVariable)
|
||||
hasScopes := false
|
||||
for varReader.Next() {
|
||||
entry := varReader.Entry()
|
||||
|
@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/gosym"
|
||||
"go/constant"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
@ -187,16 +186,20 @@ func ConvertVar(v *proc.Variable) *Variable {
|
||||
|
||||
// ConvertFunction converts from gosym.Func to
|
||||
// api.Function.
|
||||
func ConvertFunction(fn *gosym.Func) *Function {
|
||||
func ConvertFunction(fn *proc.Function) *Function {
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fn here used to be a *gosym.Func, the fields Type and GoType below
|
||||
// corresponded to the omonymous field of gosym.Func. Since the contents of
|
||||
// those fields is not documented their value was replaced with 0 when
|
||||
// gosym.Func was replaced by debug_info entries.
|
||||
return &Function{
|
||||
Name: fn.Name,
|
||||
Type: fn.Type,
|
||||
Value: fn.Value,
|
||||
GoType: fn.GoType,
|
||||
Type: 0,
|
||||
Value: fn.Entry,
|
||||
GoType: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
@ -305,7 +304,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||
for symFile := range d.target.BinInfo().Sources() {
|
||||
for _, symFile := range d.target.BinInfo().Sources {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break
|
||||
@ -637,7 +636,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
|
||||
}
|
||||
|
||||
files := []string{}
|
||||
for f := range d.target.BinInfo().Sources() {
|
||||
for _, f := range d.target.BinInfo().Sources {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
}
|
||||
@ -650,7 +649,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return regexFilterFuncs(filter, d.target.BinInfo().Funcs())
|
||||
return regexFilterFuncs(filter, d.target.BinInfo().Functions)
|
||||
}
|
||||
|
||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
@ -677,7 +676,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||
func regexFilterFuncs(filter string, allFuncs []proc.Function) ([]string, error) {
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
@ -685,7 +684,7 @@ func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||
|
||||
funcs := []string{}
|
||||
for _, f := range allFuncs {
|
||||
if f.Sym != nil && regex.Match([]byte(f.Name)) {
|
||||
if regex.Match([]byte(f.Name)) {
|
||||
funcs = append(funcs, f.Name)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"path/filepath"
|
||||
@ -216,7 +215,7 @@ func stripReceiverDecoration(in string) string {
|
||||
return in[2 : len(in)-1]
|
||||
}
|
||||
|
||||
func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
||||
func (spec *FuncLocationSpec) Match(sym proc.Function) bool {
|
||||
if spec.BaseName != sym.BaseName() {
|
||||
return false
|
||||
}
|
||||
@ -243,7 +242,7 @@ func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
||||
}
|
||||
|
||||
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.BinInfo().Funcs()
|
||||
funcs := d.target.BinInfo().Functions
|
||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -329,7 +328,7 @@ func (ale AmbiguousLocationError) Error() string {
|
||||
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
limit := maxFindLocationCandidates
|
||||
var candidateFiles []string
|
||||
for file := range d.target.BinInfo().Sources() {
|
||||
for _, file := range d.target.BinInfo().Sources {
|
||||
if loc.FileMatch(file) {
|
||||
candidateFiles = append(candidateFiles, file)
|
||||
if len(candidateFiles) >= limit {
|
||||
@ -342,11 +341,8 @@ func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
|
||||
|
||||
var candidateFuncs []string
|
||||
if loc.FuncBase != nil {
|
||||
for _, f := range d.target.BinInfo().Funcs() {
|
||||
if f.Sym == nil {
|
||||
continue
|
||||
}
|
||||
if !loc.FuncBase.Match(f.Sym) {
|
||||
for _, f := range d.target.BinInfo().Functions {
|
||||
if !loc.FuncBase.Match(f) {
|
||||
continue
|
||||
}
|
||||
if loc.Base == f.Name {
|
||||
|
Loading…
Reference in New Issue
Block a user