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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||||
)
|
)
|
||||||
@ -28,7 +29,7 @@ type DebugLineInfo struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FileEntry struct {
|
type FileEntry struct {
|
||||||
Name string
|
Path string
|
||||||
DirIdx uint64
|
DirIdx uint64
|
||||||
LastModTime uint64
|
LastModTime uint64
|
||||||
Length uint64
|
Length uint64
|
||||||
@ -36,17 +37,8 @@ type FileEntry struct {
|
|||||||
|
|
||||||
type DebugLines []*DebugLineInfo
|
type DebugLines []*DebugLineInfo
|
||||||
|
|
||||||
func (d *DebugLines) GetLineInfo(name string) *DebugLineInfo {
|
// ParseAll parses all debug_line segments found in data
|
||||||
// Find in which table file exists and return it.
|
func ParseAll(data []byte) DebugLines {
|
||||||
for _, l := range *d {
|
|
||||||
if _, ok := l.Lookup[name]; ok {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(data []byte) DebugLines {
|
|
||||||
var (
|
var (
|
||||||
lines = make(DebugLines, 0)
|
lines = make(DebugLines, 0)
|
||||||
buf = bytes.NewBuffer(data)
|
buf = bytes.NewBuffer(data)
|
||||||
@ -54,25 +46,34 @@ func Parse(data []byte) DebugLines {
|
|||||||
|
|
||||||
// We have to parse multiple file name tables here.
|
// We have to parse multiple file name tables here.
|
||||||
for buf.Len() > 0 {
|
for buf.Len() > 0 {
|
||||||
dbl := new(DebugLineInfo)
|
lines = append(lines, Parse("", buf))
|
||||||
dbl.Lookup = make(map[string]*FileEntry)
|
|
||||||
|
|
||||||
parseDebugLinePrologue(dbl, buf)
|
|
||||||
parseIncludeDirs(dbl, buf)
|
|
||||||
parseFileEntries(dbl, buf)
|
|
||||||
|
|
||||||
// Instructions size calculation breakdown:
|
|
||||||
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
|
|
||||||
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
|
|
||||||
// - 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 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)
|
||||||
|
parseFileEntries(dbl, buf)
|
||||||
|
|
||||||
|
// Instructions size calculation breakdown:
|
||||||
|
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
|
||||||
|
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
|
||||||
|
// - 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))
|
||||||
|
|
||||||
|
return dbl
|
||||||
|
}
|
||||||
|
|
||||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||||
p := new(DebugLinePrologue)
|
p := new(DebugLinePrologue)
|
||||||
|
|
||||||
@ -106,17 +107,21 @@ func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
|
|||||||
for {
|
for {
|
||||||
entry := new(FileEntry)
|
entry := new(FileEntry)
|
||||||
|
|
||||||
name, _ := util.ParseString(buf)
|
entry.Path, _ = util.ParseString(buf)
|
||||||
if name == "" {
|
if entry.Path == "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Name = name
|
|
||||||
entry.DirIdx, _ = util.DecodeULEB128(buf)
|
entry.DirIdx, _ = util.DecodeULEB128(buf)
|
||||||
entry.LastModTime, _ = util.DecodeULEB128(buf)
|
entry.LastModTime, _ = util.DecodeULEB128(buf)
|
||||||
entry.Length, _ = 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.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) {
|
func testDebugLinePrologueParser(p string, t *testing.T) {
|
||||||
data := grabDebugLineSection(p, t)
|
data := grabDebugLineSection(p, t)
|
||||||
debugLines := Parse(data)
|
debugLines := ParseAll(data)
|
||||||
dbl := debugLines[0]
|
dbl := debugLines[0]
|
||||||
prologue := dbl.Prologue
|
prologue := dbl.Prologue
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ func testDebugLinePrologueParser(p string, t *testing.T) {
|
|||||||
|
|
||||||
ok := false
|
ok := false
|
||||||
for _, n := range dbl.FileNames {
|
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
|
ok = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -154,6 +154,6 @@ func BenchmarkLineParser(b *testing.B) {
|
|||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = Parse(data)
|
_ = ParseAll(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,11 @@ const (
|
|||||||
DW_LNS_set_basic_block = 7
|
DW_LNS_set_basic_block = 7
|
||||||
DW_LNS_const_add_pc = 8
|
DW_LNS_const_add_pc = 8
|
||||||
DW_LNS_fixed_advance_pc = 9
|
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
|
// Extended opcodes
|
||||||
@ -66,6 +71,11 @@ var standardopcodes = map[byte]opcodefn{
|
|||||||
DW_LNS_set_basic_block: setbasicblock,
|
DW_LNS_set_basic_block: setbasicblock,
|
||||||
DW_LNS_const_add_pc: constaddpc,
|
DW_LNS_const_add_pc: constaddpc,
|
||||||
DW_LNS_fixed_advance_pc: fixedadvancepc,
|
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{
|
var extendedopcodes = map[byte]opcodefn{
|
||||||
@ -75,16 +85,19 @@ var extendedopcodes = map[byte]opcodefn{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
|
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
|
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
|
||||||
// could be split amongst 2 PCs.
|
// 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 (
|
var (
|
||||||
foundFile bool
|
foundFile bool
|
||||||
lastAddr uint64
|
lastAddr uint64
|
||||||
lineInfo = dbl.GetLineInfo(f)
|
|
||||||
sm = newStateMachine(lineInfo)
|
sm = newStateMachine(lineInfo)
|
||||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
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")
|
var NoSourceError = errors.New("no source available")
|
||||||
|
|
||||||
func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint64, error) {
|
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64) ([]uint64, error) {
|
||||||
lineInfo := dbl.GetLineInfo(filename)
|
|
||||||
if lineInfo == nil {
|
if lineInfo == nil {
|
||||||
return nil, NoSourceError
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pcs []uint64
|
pcs []uint64
|
||||||
lastaddr uint64
|
lastaddr uint64
|
||||||
@ -144,6 +157,63 @@ func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint
|
|||||||
return pcs, nil
|
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) {
|
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
|
||||||
switch {
|
switch {
|
||||||
case b == 0:
|
case b == 0:
|
||||||
@ -215,7 +285,7 @@ func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
|||||||
|
|
||||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
i, _ := util.DecodeULEB128(buf)
|
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) {
|
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
@ -242,6 +312,15 @@ func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
|||||||
sm.address += uint64(operand)
|
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) {
|
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
sm.endSeq = true
|
sm.endSeq = true
|
||||||
sm.valid = true
|
sm.valid = true
|
||||||
|
@ -3,13 +3,14 @@ package proc
|
|||||||
import (
|
import (
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"debug/gosym"
|
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
"debug/pe"
|
"debug/pe"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -31,13 +32,18 @@ type BinaryInfo struct {
|
|||||||
Arch Arch
|
Arch Arch
|
||||||
dwarf *dwarf.Data
|
dwarf *dwarf.Data
|
||||||
frameEntries frame.FrameDescriptionEntries
|
frameEntries frame.FrameDescriptionEntries
|
||||||
lineInfo line.DebugLines
|
compileUnits []*compileUnit
|
||||||
goSymTable *gosym.Table
|
|
||||||
types map[string]dwarf.Offset
|
types map[string]dwarf.Offset
|
||||||
packageVars map[string]dwarf.Offset
|
packageVars map[string]dwarf.Offset
|
||||||
functions []functionDebugInfo
|
|
||||||
gStructOffset uint64
|
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
|
typeCache map[dwarf.Offset]godwarf.Type
|
||||||
|
|
||||||
loadModuleDataOnce sync.Once
|
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 UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||||
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/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 {
|
func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
||||||
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
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)
|
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.
|
// Types returns list of types present in the debugged program.
|
||||||
func (bi *BinaryInfo) Types() ([]string, error) {
|
func (bi *BinaryInfo) Types() ([]string, error) {
|
||||||
types := make([]string, 0, len(bi.types))
|
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.
|
// PCToLine converts an instruction address to a file/line/function.
|
||||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) {
|
||||||
return bi.goSymTable.PCToLine(pc)
|
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.
|
// LineToPC converts a file:line into a memory address.
|
||||||
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *gosym.Func, err error) {
|
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Function, err error) {
|
||||||
return bi.goSymTable.LineToPC(filename, lineno)
|
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
|
// PCToFunc returns the function containing the given PC address
|
||||||
func (bi *BinaryInfo) PCToFunc(pc uint64) *gosym.Func {
|
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
||||||
return bi.goSymTable.PCToFunc(pc)
|
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 {
|
func (bi *BinaryInfo) Close() error {
|
||||||
@ -164,11 +244,14 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(5)
|
debugLineBytes, err := getDebugLineInfoElf(elfFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(3)
|
||||||
go bi.parseDebugFrameElf(elfFile, wg)
|
go bi.parseDebugFrameElf(elfFile, wg)
|
||||||
go bi.obtainGoSymbolsElf(elfFile, wg)
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||||
go bi.parseDebugLineInfoElf(elfFile, wg)
|
|
||||||
go bi.loadDebugInfoMaps(wg)
|
|
||||||
go bi.setGStructOffsetElf(elfFile, wg)
|
go bi.setGStructOffsetElf(elfFile, wg)
|
||||||
return nil
|
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) {
|
func getDebugLineInfoElf(exe *elf.File) ([]byte, error) {
|
||||||
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()
|
|
||||||
|
|
||||||
if sec := exe.Section(".debug_line"); sec != nil {
|
if sec := exe.Section(".debug_line"); sec != nil {
|
||||||
debugLine, err := exe.Section(".debug_line").Data()
|
debugLine, err := exe.Section(".debug_line").Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_line section: %v", err)
|
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
bi.lineInfo = line.Parse(debugLine)
|
return debugLine, nil
|
||||||
} else {
|
|
||||||
bi.setLoadError("could not find .debug_line section in binary")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("could not find .debug_line section in binary")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(4)
|
debugLineBytes, err := getDebugLineInfoPE(peFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(2)
|
||||||
go bi.parseDebugFramePE(peFile, wg)
|
go bi.parseDebugFramePE(peFile, wg)
|
||||||
go bi.obtainGoSymbolsPE(peFile, wg)
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||||
go bi.parseDebugLineInfoPE(peFile, wg)
|
|
||||||
go bi.loadDebugInfoMaps(wg)
|
|
||||||
|
|
||||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||||
// to G struct per:
|
// 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
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||||
for _, s := range f.Symbols {
|
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
|
return textStart, symtab, pclntab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) parseDebugLineInfoPE(exe *pe.File, wg *sync.WaitGroup) {
|
func getDebugLineInfoPE(exe *pe.File) ([]byte, error) {
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
if sec := exe.Section(".debug_line"); sec != nil {
|
if sec := exe.Section(".debug_line"); sec != nil {
|
||||||
debugLine, err := sec.Data()
|
debugLine, err := sec.Data()
|
||||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||||
bi.setLoadError("could not get .debug_line section: %v", err)
|
return nil, fmt.Errorf("could not get .debug_line section: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||||
debugLine = debugLine[:sec.VirtualSize]
|
debugLine = debugLine[:sec.VirtualSize]
|
||||||
}
|
}
|
||||||
bi.lineInfo = line.Parse(debugLine)
|
return debugLine, nil
|
||||||
} else {
|
|
||||||
bi.setLoadError("could not find .debug_line section in binary")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("could not find .debug_line section in binary")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MACH-O ////////////////////////////////////////////////////////////
|
// MACH-O ////////////////////////////////////////////////////////////
|
||||||
@ -480,11 +502,14 @@ func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(4)
|
debugLineBytes, err := getDebugLineInfoMacho(exe)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(2)
|
||||||
go bi.parseDebugFrameMacho(exe, wg)
|
go bi.parseDebugFrameMacho(exe, wg)
|
||||||
go bi.obtainGoSymbolsMacho(exe, wg)
|
go bi.loadDebugInfoMaps(debugLineBytes, wg)
|
||||||
go bi.parseDebugLineInfoMacho(exe, wg)
|
|
||||||
go bi.loadDebugInfoMaps(wg)
|
|
||||||
bi.gStructOffset = 0x8a0
|
bi.gStructOffset = 0x8a0
|
||||||
return nil
|
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) {
|
func getDebugLineInfoMacho(exe *macho.File) ([]byte, error) {
|
||||||
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()
|
|
||||||
|
|
||||||
if sec := exe.Section("__debug_line"); sec != nil {
|
if sec := exe.Section("__debug_line"); sec != nil {
|
||||||
debugLine, err := exe.Section("__debug_line").Data()
|
debugLine, err := exe.Section("__debug_line").Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get __debug_line section: %v", err)
|
return nil, fmt.Errorf("could not get __debug_line section: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
bi.lineInfo = line.Parse(debugLine)
|
return debugLine, nil
|
||||||
} else {
|
|
||||||
bi.setLoadError("could not find __debug_line section in binary")
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
return nil, errors.New("could not find __debug_line section in binary")
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"golang.org/x/arch/x86/x86asm"
|
"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
|
// 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
|
// 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()
|
var mem MemoryReadWriter = p.CurrentThread()
|
||||||
breakpoints := p.Breakpoints()
|
breakpoints := p.Breakpoints()
|
||||||
bi := p.BinInfo()
|
bi := p.BinInfo()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/dwarf"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -11,11 +10,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type functionDebugInfo struct {
|
|
||||||
lowpc, highpc uint64
|
|
||||||
offset dwarf.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
var NotExecutableErr = errors.New("not an executable file")
|
var NotExecutableErr = errors.New("not an executable file")
|
||||||
var NotRecordedErr = errors.New("not a recording")
|
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.
|
// FindFileLocation returns the PC for a given file:line.
|
||||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
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 {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -53,7 +47,7 @@ func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
|||||||
// https://github.com/derekparker/delve/issues/170
|
// https://github.com/derekparker/delve/issues/170
|
||||||
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||||
bi := p.BinInfo()
|
bi := p.BinInfo()
|
||||||
origfn := bi.goSymTable.LookupFunc(funcName)
|
origfn := bi.LookupFunc[funcName]
|
||||||
if origfn == nil {
|
if origfn == nil {
|
||||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
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 {
|
if firstLine {
|
||||||
return FirstPCAfterPrologue(p, origfn, false)
|
return FirstPCAfterPrologue(p, origfn, false)
|
||||||
} else if lineOffset > 0 {
|
} else if lineOffset > 0 {
|
||||||
filename, lineno, _ := bi.goSymTable.PCToLine(origfn.Entry)
|
filename, lineno := origfn.cu.lineInfo.PCToLine(origfn.Entry)
|
||||||
breakAddr, _, err := bi.goSymTable.LineToPC(filename, lineno+lineOffset)
|
breakAddr, _, err := bi.LineToPC(filename, lineno+lineOffset)
|
||||||
return breakAddr, err
|
return breakAddr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,6 @@ type Stackframe struct {
|
|||||||
CFA int64
|
CFA int64
|
||||||
// High address of the stack.
|
// High address of the stack.
|
||||||
StackHi uint64
|
StackHi uint64
|
||||||
// Description of the stack frame.
|
|
||||||
FDE *frame.FrameDescriptionEntry
|
|
||||||
// Return address for this stack frame (as read from the stack frame itself).
|
// Return address for this stack frame (as read from the stack frame itself).
|
||||||
Ret uint64
|
Ret uint64
|
||||||
// Address to the memory location containing the return address
|
// 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 {
|
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
|
var stackBarrierPC uint64
|
||||||
if stackBarrierFunc != nil && stkbar != nil {
|
if stackBarrierFunc != nil && stkbar != nil {
|
||||||
stackBarrierPC = stackBarrierFunc.Entry
|
stackBarrierPC = stackBarrierFunc.Entry
|
||||||
fn := bi.goSymTable.PCToFunc(pc)
|
fn := bi.PCToFunc(pc)
|
||||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
if fn != nil && fn.Name == runtimeStackBarrier {
|
||||||
// We caught the goroutine as it's executing the stack barrier, we must
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
it.err = err
|
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 {
|
if !top {
|
||||||
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
|
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
|
||||||
r.Call.PC = r.Current.PC
|
r.Call.PC = r.Current.PC
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/token"
|
"go/token"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -42,7 +42,7 @@ type Location struct {
|
|||||||
PC uint64
|
PC uint64
|
||||||
File string
|
File string
|
||||||
Line int
|
Line int
|
||||||
Fn *gosym.Func
|
Fn *Function
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThreadBlockedError is returned when the thread
|
// ThreadBlockedError is returned when the thread
|
||||||
@ -104,6 +104,10 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if topframe.Current.Fn == nil {
|
||||||
|
return fmt.Errorf("no source for pc %#x", topframe.Current.PC)
|
||||||
|
}
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
defer func() {
|
defer func() {
|
||||||
if !success {
|
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 {
|
if err != nil && stepInto {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -203,7 +207,7 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add breakpoints on all the lines in the current function
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -211,14 +215,14 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
if !csource {
|
if !csource {
|
||||||
var covered bool
|
var covered bool
|
||||||
for i := range pcs {
|
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
|
covered = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !covered {
|
if !covered {
|
||||||
fn := dbp.BinInfo().goSymTable.PCToFunc(topframe.Ret)
|
fn := dbp.BinInfo().PCToFunc(topframe.Ret)
|
||||||
if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
|
if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||||
|
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -155,62 +157,128 @@ func (bi *BinaryInfo) loadPackageMap() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type sortFunctionsDebugInfoByLowpc []functionDebugInfo
|
type functionsDebugInfoByEntry []Function
|
||||||
|
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Len() int { return len(v) }
|
func (v functionsDebugInfoByEntry) Len() int { return len(v) }
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Less(i, j int) bool { return v[i].lowpc < v[j].lowpc }
|
func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry }
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) {
|
func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
||||||
temp := v[i]
|
|
||||||
v[i] = v[j]
|
|
||||||
v[j] = temp
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
defer wg.Done()
|
||||||
bi.types = make(map[string]dwarf.Offset)
|
bi.types = make(map[string]dwarf.Offset)
|
||||||
bi.packageVars = make(map[string]dwarf.Offset)
|
bi.packageVars = make(map[string]dwarf.Offset)
|
||||||
bi.functions = []functionDebugInfo{}
|
bi.Functions = []Function{}
|
||||||
|
bi.compileUnits = []*compileUnit{}
|
||||||
reader := bi.DwarfReader()
|
reader := bi.DwarfReader()
|
||||||
|
var cu *compileUnit = nil
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch entry.Tag {
|
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:
|
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 name, ok := entry.Val(dwarf.AttrName).(string); ok {
|
||||||
|
if !cu.isgo {
|
||||||
|
name = "C." + name
|
||||||
|
}
|
||||||
if _, exists := bi.types[name]; !exists {
|
if _, exists := bi.types[name]; !exists {
|
||||||
bi.types[name] = entry.Offset
|
bi.types[name] = entry.Offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reader.SkipChildren()
|
reader.SkipChildren()
|
||||||
|
|
||||||
case dwarf.TagVariable:
|
case dwarf.TagVariable:
|
||||||
if n, ok := entry.Val(dwarf.AttrName).(string); ok {
|
if n, ok := entry.Val(dwarf.AttrName).(string); ok {
|
||||||
|
if !cu.isgo {
|
||||||
|
n = "C." + n
|
||||||
|
}
|
||||||
bi.packageVars[n] = entry.Offset
|
bi.packageVars[n] = entry.Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
case dwarf.TagSubprogram:
|
case dwarf.TagSubprogram:
|
||||||
lowpc, ok1 := entry.Val(dwarf.AttrLowpc).(uint64)
|
ok1 := false
|
||||||
highpc, ok2 := entry.Val(dwarf.AttrHighpc).(uint64)
|
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 {
|
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()
|
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) {
|
func uniq(s []string) []string {
|
||||||
i := sort.Search(len(bi.functions), func(i int) bool {
|
if len(s) <= 0 {
|
||||||
fn := bi.functions[i]
|
return s
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0, errors.New("unable to find function context")
|
src, dst := 1, 1
|
||||||
|
for src < len(s) {
|
||||||
|
if s[src] != s[dst-1] {
|
||||||
|
s[dst] = s[src]
|
||||||
|
dst++
|
||||||
|
}
|
||||||
|
src++
|
||||||
|
}
|
||||||
|
return s[:dst]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
|
func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
|
||||||
|
@ -511,7 +511,7 @@ func (g *G) UserCurrent() Location {
|
|||||||
// Go returns the location of the 'go' statement
|
// Go returns the location of the 'go' statement
|
||||||
// that spawned this goroutine.
|
// that spawned this goroutine.
|
||||||
func (g *G) Go() Location {
|
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}
|
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))
|
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 {
|
if fn == nil {
|
||||||
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
|
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
|
||||||
return
|
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
|
// Fetches all variables of a specific type in the current function scope
|
||||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Variable, error) {
|
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Variable, error) {
|
||||||
off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
|
fn := scope.BinInfo.PCToFunc(scope.PC)
|
||||||
if err != nil {
|
if fn == nil {
|
||||||
return nil, err
|
return nil, errors.New("unable to find function context")
|
||||||
}
|
}
|
||||||
|
|
||||||
var vars []*Variable
|
var vars []*Variable
|
||||||
var depths []int
|
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
|
hasScopes := false
|
||||||
for varReader.Next() {
|
for varReader.Next() {
|
||||||
entry := varReader.Entry()
|
entry := varReader.Entry()
|
||||||
|
@ -2,7 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/gosym"
|
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/printer"
|
"go/printer"
|
||||||
"go/token"
|
"go/token"
|
||||||
@ -187,16 +186,20 @@ func ConvertVar(v *proc.Variable) *Variable {
|
|||||||
|
|
||||||
// ConvertFunction converts from gosym.Func to
|
// ConvertFunction converts from gosym.Func to
|
||||||
// api.Function.
|
// api.Function.
|
||||||
func ConvertFunction(fn *gosym.Func) *Function {
|
func ConvertFunction(fn *proc.Function) *Function {
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return 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{
|
return &Function{
|
||||||
Name: fn.Name,
|
Name: fn.Name,
|
||||||
Type: fn.Type,
|
Type: 0,
|
||||||
Value: fn.Value,
|
Value: fn.Entry,
|
||||||
GoType: fn.GoType,
|
GoType: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package debugger
|
package debugger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
@ -305,7 +304,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
|||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
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)) {
|
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||||
fileName = symFile
|
fileName = symFile
|
||||||
break
|
break
|
||||||
@ -637,7 +636,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
files := []string{}
|
files := []string{}
|
||||||
for f := range d.target.BinInfo().Sources() {
|
for _, f := range d.target.BinInfo().Sources {
|
||||||
if regex.Match([]byte(f)) {
|
if regex.Match([]byte(f)) {
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
@ -650,7 +649,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
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) {
|
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||||
@ -677,7 +676,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
|||||||
return r, nil
|
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)
|
regex, err := regexp.Compile(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
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{}
|
funcs := []string{}
|
||||||
for _, f := range allFuncs {
|
for _, f := range allFuncs {
|
||||||
if f.Sym != nil && regex.Match([]byte(f.Name)) {
|
if regex.Match([]byte(f.Name)) {
|
||||||
funcs = append(funcs, f.Name)
|
funcs = append(funcs, f.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package debugger
|
package debugger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/gosym"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -216,7 +215,7 @@ func stripReceiverDecoration(in string) string {
|
|||||||
return in[2 : len(in)-1]
|
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() {
|
if spec.BaseName != sym.BaseName() {
|
||||||
return false
|
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) {
|
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)
|
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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) {
|
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||||
limit := maxFindLocationCandidates
|
limit := maxFindLocationCandidates
|
||||||
var candidateFiles []string
|
var candidateFiles []string
|
||||||
for file := range d.target.BinInfo().Sources() {
|
for _, file := range d.target.BinInfo().Sources {
|
||||||
if loc.FileMatch(file) {
|
if loc.FileMatch(file) {
|
||||||
candidateFiles = append(candidateFiles, file)
|
candidateFiles = append(candidateFiles, file)
|
||||||
if len(candidateFiles) >= limit {
|
if len(candidateFiles) >= limit {
|
||||||
@ -342,11 +341,8 @@ func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
|
|||||||
|
|
||||||
var candidateFuncs []string
|
var candidateFuncs []string
|
||||||
if loc.FuncBase != nil {
|
if loc.FuncBase != nil {
|
||||||
for _, f := range d.target.BinInfo().Funcs() {
|
for _, f := range d.target.BinInfo().Functions {
|
||||||
if f.Sym == nil {
|
if !loc.FuncBase.Match(f) {
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !loc.FuncBase.Match(f.Sym) {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if loc.Base == f.Name {
|
if loc.Base == f.Name {
|
||||||
|
Loading…
Reference in New Issue
Block a user