mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	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:
		| @ -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,8 +46,20 @@ 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 { | ||||||
|  | 		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 := new(DebugLineInfo) | ||||||
| 	dbl.Lookup = make(map[string]*FileEntry) | 	dbl.Lookup = make(map[string]*FileEntry) | ||||||
|  | 	if compdir != "" { | ||||||
|  | 		dbl.IncludeDirs = append(dbl.IncludeDirs, compdir) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	parseDebugLinePrologue(dbl, buf) | 	parseDebugLinePrologue(dbl, buf) | ||||||
| 	parseIncludeDirs(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)). | 	//   - 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)) | 	dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6)) | ||||||
|  |  | ||||||
| 		lines = append(lines, dbl) | 	return dbl | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return lines |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) { | func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) { | ||||||
| @ -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 |  | ||||||
| 	} | 	} | ||||||
|  | 	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) { | 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 { | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli