提交 372869c9 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

proc: Back to using vendored golang.org/x/debug/ (#585)

Patch https://go-review.googlesource.com/23085 has been merged so we
can go back to using golang.org/x/debug/.
上级 da910cc3
{
"ImportPath": "github.com/derekparker/delve",
"GoVersion": "go1.5",
"GodepVersion": "v74",
"Packages": [
"github.com/derekparker/delve/cmd/dlv",
"github.com/derekparker/delve/cmd/dlv/cmds",
......@@ -16,7 +17,7 @@
"github.com/derekparker/delve/service",
"github.com/derekparker/delve/service/api",
"github.com/derekparker/delve/service/debugger",
"github.com/derekparker/delve/service/rpc",
"github.com/derekparker/delve/service/rpccommon",
"github.com/derekparker/delve/service/test",
"github.com/derekparker/delve/terminal",
"github.com/derekparker/delve/version"
......@@ -69,6 +70,18 @@
"ImportPath": "github.com/spf13/pflag",
"Rev": "b084184666e02084b8ccb9b704bf0d79c466eb1d"
},
{
"ImportPath": "golang.org/x/debug/dwarf",
"Rev": "fb508927b491eca48a708e9d000fdb7afa53c32b"
},
{
"ImportPath": "golang.org/x/debug/elf",
"Rev": "fb508927b491eca48a708e9d000fdb7afa53c32b"
},
{
"ImportPath": "golang.org/x/debug/macho",
"Rev": "fb508927b491eca48a708e9d000fdb7afa53c32b"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "eb2c74142fd19a79b3f237334c7384d5167b1b46"
......
......@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/op"
)
......
......@@ -11,7 +11,7 @@ import (
"go/token"
"reflect"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/reader"
)
......@@ -494,7 +494,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
return nil, err
}
if xv.Children[0].DwarfType.String() != typ.String() {
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.String(), xv.Children[0].TypeString(), typ)
return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
}
return &xv.Children[0], nil
}
......@@ -633,7 +633,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true
typename := "*" + xev.DwarfType.String()
typename := "*" + xev.DwarfType.Common().Name
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv.Children = []Variable{*xev}
rv.loaded = true
......@@ -1102,7 +1102,7 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
ByteSize: 24,
Name: "",
},
StructName: fmt.Sprintf("[]%s", v.fieldType),
StructName: fmt.Sprintf("[]%s", v.fieldType.Common().Name),
Kind: "struct",
Field: nil,
},
......
......@@ -14,7 +14,7 @@ import (
"strings"
"sync"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
"github.com/derekparker/delve/dwarf/reader"
......
......@@ -15,7 +15,7 @@ import (
"sync"
"unsafe"
"github.com/derekparker/delve/dwarf/debug/macho"
"golang.org/x/debug/macho"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
......
......@@ -16,7 +16,7 @@ import (
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/dwarf/debug/elf"
"golang.org/x/debug/elf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
)
......
......@@ -15,7 +15,7 @@ import (
sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/frame"
"github.com/derekparker/delve/dwarf/line"
)
......
......@@ -10,7 +10,7 @@ import (
"reflect"
"runtime"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
)
// Thread represents a single thread in the traced process
......
......@@ -9,7 +9,7 @@ import (
"strings"
"sync"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
)
// Do not call this function directly it isn't able to deal correctly with package paths
......@@ -22,7 +22,7 @@ func (dbp *Process) findType(name string) (dwarf.Type, error) {
}
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
}
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
......
......@@ -12,7 +12,7 @@ import (
"strings"
"unsafe"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/op"
"github.com/derekparker/delve/dwarf/reader"
)
......@@ -282,7 +282,7 @@ func (v *Variable) TypeString() string {
return "nil"
}
if v.DwarfType != nil {
return v.DwarfType.String()
return v.DwarfType.Common().Name
}
return v.Kind.String()
}
......@@ -626,10 +626,10 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
// not a regular struct member
for _, field := range t.Field {
isEmbeddedStructMember :=
(field.Type.String() == field.Name) ||
(field.Type.Common().Name == field.Name) ||
(len(field.Name) > 1 &&
field.Name[0] == '*' &&
field.Type.String()[1:] == field.Name[1:])
field.Type.Common().Name[1:] == field.Name[1:])
if !isEmbeddedStructMember {
continue
}
......
......@@ -9,7 +9,7 @@ import (
"reflect"
"strconv"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/proc"
)
......@@ -88,6 +88,9 @@ func prettyTypeName(typ dwarf.Type) string {
if typ == nil {
return ""
}
if typ.Common().Name != "" {
return typ.Common().Name
}
r := typ.String()
if r == "*void" {
return "unsafe.Pointer"
......
......@@ -8,6 +8,7 @@ package dwarf
import (
"encoding/binary"
"fmt"
"strconv"
)
......@@ -173,6 +174,17 @@ func (b *buf) addr() uint64 {
return 0
}
// assertEmpty checks that everything has been read from b.
func (b *buf) assertEmpty() {
if len(b.data) == 0 {
return
}
if len(b.data) > 5 {
b.error(fmt.Sprintf("unexpected extra data: %x...", b.data[0:5]))
}
b.error(fmt.Sprintf("unexpected extra data: %x", b.data))
}
func (b *buf) error(s string) {
if b.err == nil {
b.data = nil
......
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package dwarf
import (
"sort"
)
// pcToFuncEntries maps PC ranges to function entries.
//
// Each element contains a *Entry for a function and its corresponding start PC.
// If we know the address one past the last instruction of a function, and it is
// not equal to the start address of the next function, we mark that with
// another element containing that address and a nil entry. The elements are
// sorted by PC. Among elements with the same PC, those with non-nil *Entry
// are put earlier.
type pcToFuncEntries []pcToFuncEntry
type pcToFuncEntry struct {
pc uint64
entry *Entry
}
func (p pcToFuncEntries) Len() int { return len(p) }
func (p pcToFuncEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p pcToFuncEntries) Less(i, j int) bool {
if p[i].pc != p[j].pc {
return p[i].pc < p[j].pc
}
return p[i].entry != nil && p[j].entry == nil
}
// nameCache maps each symbol name to a linked list of the entries with that name.
type nameCache map[string]*nameCacheEntry
type nameCacheEntry struct {
entry *Entry
link *nameCacheEntry
}
// pcToLineEntries maps PCs to line numbers.
//
// It is a slice of (PC, line, file number) triples, sorted by PC. The file
// number is an index into the source files slice.
// If (PC1, line1, file1) and (PC2, line2, file2) are two consecutive elements,
// then the span of addresses [PC1, PC2) belongs to (line1, file1). If an
// element's file number is zero, it only marks the end of a span.
//
// TODO: could save memory by changing pcToLineEntries and lineToPCEntries to use
// interval trees containing references into .debug_line.
type pcToLineEntries []pcToLineEntry
type pcToLineEntry struct {
pc uint64
line uint64
file uint64
}
func (p pcToLineEntries) Len() int { return len(p) }
func (p pcToLineEntries) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p pcToLineEntries) Less(i, j int) bool {
if p[i].pc != p[j].pc {
return p[i].pc < p[j].pc
}
return p[i].file > p[j].file
}
// byFileLine is used temporarily while building lineToPCEntries.
type byFileLine []pcToLineEntry
func (b byFileLine) Len() int { return len(b) }
func (b byFileLine) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b byFileLine) Less(i, j int) bool {
if b[i].file != b[j].file {
return b[i].file < b[j].file
}
return b[i].line < b[j].line
}
// lineToPCEntries maps line numbers to breakpoint addresses.
//
// The slice contains, for each source file in Data, a slice of (line, PC)
// pairs, sorted by line. Note that there may be more than one PC for a line.
type lineToPCEntries [][]lineToPCEntry
type lineToPCEntry struct {
line uint64
pc uint64
}
func (d *Data) buildLineToPCCache(pclfs pcToLineEntries) {
// TODO: only include lines where is_stmt is true
sort.Sort(byFileLine(pclfs))
// Make a slice of (line, PC) pairs for each (non-zero) file.
var (
c = make(lineToPCEntries, len(d.sourceFiles))
curSlice []lineToPCEntry
)
for i, pclf := range pclfs {
if pclf.file == 0 {
// This entry indicated the end of an instruction sequence, not a breakpoint.
continue
}
curSlice = append(curSlice, lineToPCEntry{line: pclf.line, pc: pclf.pc})
if i+1 == len(pclfs) || pclf.file != pclfs[i+1].file {
// curSlice now contains all of the entries for pclf.file.
if pclf.file > 0 && pclf.file < uint64(len(c)) {
c[pclf.file] = curSlice
}
curSlice = nil
}
}
d.lineToPCEntries = c
}
func (d *Data) buildPCToLineCache(cache pcToLineEntries) {
// Sort cache by PC (in increasing order), then by file number (in decreasing order).
sort.Sort(cache)
// Build a copy without redundant entries.
var out pcToLineEntries
for i, pclf := range cache {
if i > 0 && pclf.pc == cache[i-1].pc {
// This entry is for the same PC as the previous entry.
continue
}
if i > 0 && pclf.file == cache[i-1].file && pclf.line == cache[i-1].line {
// This entry is for the same file and line as the previous entry.
continue
}
out = append(out, pclf)
}
d.pcToLineEntries = out
}
// buildLineCaches constructs d.sourceFiles, d.lineToPCEntries, d.pcToLineEntries.
func (d *Data) buildLineCaches() {
if len(d.line) == 0 {
return
}
var m lineMachine
// Assume the address_size in the first unit applies to the whole program.
// TODO: we could handle executables containing code for multiple address
// sizes using DW_AT_stmt_list attributes.
if len(d.unit) == 0 {
return
}
buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
if err := m.parseHeader(&buf); err != nil {
return
}
for _, f := range m.header.file {
d.sourceFiles = append(d.sourceFiles, f.name)
}
var cache pcToLineEntries
fn := func(m *lineMachine) bool {
if m.endSequence {
cache = append(cache, pcToLineEntry{
pc: m.address,
line: 0,
file: 0,
})
} else {
cache = append(cache, pcToLineEntry{
pc: m.address,
line: m.line,
file: m.file,
})
}
return true
}
m.evalCompilationUnit(&buf, fn)
d.buildLineToPCCache(cache)
d.buildPCToLineCache(cache)
}
// buildInfoCaches initializes nameCache and pcToFuncEntries by walking the
// top-level entries under each compile unit. It swallows any errors in parsing.
func (d *Data) buildInfoCaches() {
// TODO: record errors somewhere?
d.nameCache = make(map[string]*nameCacheEntry)
var pcToFuncEntries pcToFuncEntries
r := d.Reader()
loop:
for {
entry, err := r.Next()
if entry == nil || err != nil {
break loop
}
if entry.Tag != TagCompileUnit /* DW_TAG_compile_unit */ {
r.SkipChildren()
continue
}
for {
entry, err := r.Next()
if entry == nil || err != nil {
break loop
}
if entry.Tag == 0 {
// End of children of current compile unit.
break
}
r.SkipChildren()
// Update name-to-entry cache.
if name, ok := entry.Val(AttrName).(string); ok {
d.nameCache[name] = &nameCacheEntry{entry: entry, link: d.nameCache[name]}
}
// If this entry is a function, update PC-to-containing-function cache.
if entry.Tag != TagSubprogram /* DW_TAG_subprogram */ {
continue
}
// DW_AT_low_pc, if present, is the address of the first instruction of
// the function.
lowpc, ok := entry.Val(AttrLowpc).(uint64)
if !ok {
continue
}
pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{lowpc, entry})
// DW_AT_high_pc, if present (TODO: and of class address) is the address
// one past the last instruction of the function.
highpc, ok := entry.Val(AttrHighpc).(uint64)
if !ok {
continue
}
pcToFuncEntries = append(pcToFuncEntries, pcToFuncEntry{highpc, nil})
}
}
// Sort elements by PC. If there are multiple elements with the same PC,
// those with non-nil *Entry are placed earlier.
sort.Sort(pcToFuncEntries)
// Copy only the first element for each PC to out.
n := 0
for i, ce := range pcToFuncEntries {
if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
n++
}
}
out := make([]pcToFuncEntry, 0, n)
for i, ce := range pcToFuncEntries {
if i == 0 || ce.pc != pcToFuncEntries[i-1].pc {
out = append(out, ce)
}
}
d.pcToFuncEntries = out
}
......@@ -11,91 +11,40 @@ package dwarf
import (
"fmt"
"sort"
"strings"
)
// PCToLine returns the file and line number corresponding to the PC value.
// It returns an error if a correspondence cannot be found.
func (d *Data) PCToLine(pc uint64) (file string, line uint64, err error) {
if len(d.line) == 0 {
c := d.pcToLineEntries
if len(c) == 0 {
return "", 0, fmt.Errorf("PCToLine: no line table")
}
var m lineMachine
// Assume the first info unit is the same as us. Extremely likely. TODO?
if len(d.unit) == 0 {
return "", 0, fmt.Errorf("no info section")
}
buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
if err = m.parseHeader(&buf); err != nil {
return "", 0, err
}
state := pcSearchState{pc: pc, newSequence: true}
if err = m.evalCompilationUnit(&buf, state.findPC); err != nil {
return "", 0, err
}
if !state.found {
i := sort.Search(len(c), func(i int) bool { return c[i].pc > pc }) - 1
// c[i] is now the entry in pcToLineEntries with the largest pc that is not
// larger than the query pc.
// The search has failed if:
// - All pcs in c were larger than the query pc (i == -1).
// - c[i] marked the end of a sequence of instructions (c[i].file == 0).
// - c[i] is the last element of c, and isn't the end of a sequence of
// instructions, and the search pc is much larger than c[i].pc. In this
// case, we don't know the range of the last instruction, but the search
// pc is probably past it.
if i == -1 || c[i].file == 0 || (i+1 == len(c) && pc-c[i].pc > 1024) {
return "", 0, fmt.Errorf("no source line defined for PC %#x", pc)
}
if state.lastFile >= uint64(len(m.header.file)) {
if c[i].file >= uint64(len(d.sourceFiles)) {
return "", 0, fmt.Errorf("invalid file number in DWARF data")
}
return m.header.file[state.lastFile].name, state.lastLine, nil
}
// pcSearchState holds the state for the search PCToLine does.
type pcSearchState struct {
pc uint64 // pc we are searching for.
// lastPC, lastFile, and lastLine are the PC, file number and line that were
// output most recently by the line machine.
lastPC uint64
lastFile uint64
lastLine uint64
// found indicates that the above values correspond to the PC we're looking for.
found bool
// newSequence indicates that we are starting a new sequence of instructions,
// and so last{PC,File,Line} are not valid.
newSequence bool
return d.sourceFiles[c[i].file], c[i].line, nil
}
// findPC will execute for every line in the state machine, until we find state.pc.
// It returns a bool indicating whether to continue searching.
func (state *pcSearchState) findPC(m *lineMachine) bool {
if !state.newSequence && state.lastPC < state.pc && state.pc < m.address {
// The PC we are looking for is between the previous PC and the current PC,
// so lastFile and lastLine are its source location.
state.found = true
return false
}
if m.endSequence {
state.newSequence = true
return true
}
state.newSequence = false
state.lastPC, state.lastFile, state.lastLine = m.address, m.file, m.line
if m.address == state.pc {
// lastFile and lastLine are the source location of pc.
state.found = true
return false
}
return true
}
// LineToPCs returns the PCs corresponding to the file and line number.
// LineToBreakpointPCs returns the PCs that should be used as breakpoints
// corresponding to the given file and line number.
// It returns an empty slice if no PCs were found.
func (d *Data) LineToPCs(file string, line uint64) ([]uint64, error) {
if len(d.line) == 0 {
return nil, fmt.Errorf("LineToPCs: no line table")
}
if len(d.unit) == 0 {
return nil, fmt.Errorf("LineToPCs: no info section")
}
buf := makeBuf(d, &d.unit[0], "line", 0, d.line)
var m lineMachine
if err := m.parseHeader(&buf); err != nil {
return nil, err
}
func (d *Data) LineToBreakpointPCs(file string, line uint64) ([]uint64, error) {
compDir := d.compilationDirectory()
// Find the closest match in the executable for the specified file.
......@@ -103,46 +52,43 @@ func (d *Data) LineToPCs(file string, line uint64) ([]uint64, error) {
// at the end of the name. If there is a tie, we prefer files that are
// under the compilation directory. If there is still a tie, we choose
// the file with the shortest name.
// TODO: handle duplicate file names in the DWARF?
var bestFile struct {
fileNum uint64 // Index of the file in the DWARF data.
components int // Number of matching path components.
length int // Length of the filename.
underComp bool // File is under the compilation directory.
}
for num, f := range m.header.file {
c := matchingPathComponentSuffixSize(f.name, file)
underComp := strings.HasPrefix(f.name, compDir)
for filenum, filename := range d.sourceFiles {
c := matchingPathComponentSuffixSize(filename, file)
underComp := strings.HasPrefix(filename, compDir)
better := false
if c != bestFile.components {
better = c > bestFile.components
} else if underComp != bestFile.underComp {
better = underComp
} else {
better = len(f.name) < bestFile.length
better = len(filename) < bestFile.length
}
if better {
bestFile.fileNum = uint64(num)
bestFile.fileNum = uint64(filenum)
bestFile.components = c
bestFile.length = len(f.name)
bestFile.length = len(filename)
bestFile.underComp = underComp
}
}
if bestFile.components == 0 {
return nil, fmt.Errorf("couldn't find file %s", file)
return nil, fmt.Errorf("couldn't find file %q", file)
}
// pcs will contain the PCs for every line machine output with the correct line
// and file number.
var pcs []uint64
// accumulatePCs will execute for every line machine output.
accumulatePCs := func(m *lineMachine) (cont bool) {
if m.line == line && m.file == bestFile.fileNum {
pcs = append(pcs, m.address)
}
return true
}
if err := m.evalCompilationUnit(&buf, accumulatePCs); err != nil {
return nil, err
c := d.lineToPCEntries[bestFile.fileNum]
// c contains all (pc, line) pairs for the appropriate file.
start := sort.Search(len(c), func(i int) bool { return c[i].line >= line })
end := sort.Search(len(c), func(i int) bool { return c[i].line > line })
// c[i].line == line for all i in the range [start, end).
pcs := make([]uint64, 0, end-start)
for i := start; i < end; i++ {
pcs = append(pcs, c[i].pc)
}
return pcs, nil
}
......
......@@ -23,11 +23,16 @@ type Data struct {
str []byte
// parsed data
abbrevCache map[uint32]abbrevTable
order binary.ByteOrder
typeCache map[Offset]Type
typeSigs map[uint64]*typeUnit
unit []unit
abbrevCache map[uint32]abbrevTable
order binary.ByteOrder
typeCache map[Offset]Type
typeSigs map[uint64]*typeUnit
unit []unit
sourceFiles []string // source files listed in .debug_line.
nameCache // map from name to top-level entries in .debug_info.
pcToFuncEntries // cache of .debug_info data for function bounds.
pcToLineEntries // cache of .debug_line data, used for efficient PC-to-line mapping.
lineToPCEntries // cache of .debug_line data, used for efficient line-to-[]PC mapping.
}
// New returns a new Data object initialized from the given parameters.
......@@ -75,6 +80,8 @@ func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Dat
return nil, err
}
d.unit = u
d.buildInfoCaches()
d.buildLineCaches()
return d, nil
}
......
......@@ -6,33 +6,36 @@ package dwarf
// This file provides simple methods to access the symbol table by name and address.
import "fmt"
import (
"fmt"
"regexp"
"sort"
)
// lookupEntry returns the Entry for the name. If tag is non-zero, only entries
// with that tag are considered.
// lookupEntry returns the first Entry for the name.
// If tag is non-zero, only entries with that tag are considered.
func (d *Data) lookupEntry(name string, tag Tag) (*Entry, error) {
r := d.Reader()
for {
entry, err := r.Next()
if err != nil {
return nil, err
}
if entry == nil {
// TODO: why don't we get an error here?
break
}
if tag != 0 && tag != entry.Tag {
continue
}
nameAttr := entry.Val(AttrName)
if nameAttr == nil {
continue
x, ok := d.nameCache[name]
if !ok {
return nil, fmt.Errorf("DWARF entry for %q not found", name)
}
for ; x != nil; x = x.link {
if tag == 0 || x.entry.Tag == tag {
return x.entry, nil
}
if nameAttr.(string) == name {
return entry, nil
}
return nil, fmt.Errorf("no DWARF entry for %q with tag %s", name, tag)
}
// LookupMatchingSymbols returns the names of all top-level entries matching
// the given regular expression.
func (d *Data) LookupMatchingSymbols(nameRE *regexp.Regexp) (result []string, err error) {
for name := range d.nameCache {
if nameRE.MatchString(name) {
result = append(result, name)
}
}
return nil, fmt.Errorf("DWARF entry for %q not found", name)
return result, nil
}
// LookupEntry returns the Entry for the named symbol.
......@@ -40,38 +43,14 @@ func (d *Data) LookupEntry(name string) (*Entry, error) {
return d.lookupEntry(name, 0)
}
// LookupFunction returns the address of the named symbol, a function.
func (d *Data) LookupFunction(name string) (uint64, error) {
entry, err := d.lookupEntry(name, TagSubprogram)
if err != nil {
return 0, err
}
addrAttr := entry.Val(AttrLowpc)
if addrAttr == nil {
return 0, fmt.Errorf("symbol %q has no LowPC attribute", name)
}
addr, ok := addrAttr.(uint64)
if !ok {
return 0, fmt.Errorf("symbol %q has non-uint64 LowPC attribute", name)
}
return addr, nil
// LookupFunction returns the entry for a function.
func (d *Data) LookupFunction(name string) (*Entry, error) {
return d.lookupEntry(name, TagSubprogram)
}
// TODO: should LookupVariable handle both globals and locals? Locals don't
// necessarily have a fixed address. They may be in a register, or otherwise
// move around.
// LookupVariable returns the location of a named symbol, a variable.
func (d *Data) LookupVariable(name string) (uint64, error) {
entry, err := d.lookupEntry(name, TagVariable)
if err != nil {
return 0, fmt.Errorf("variable %s: %s", name, err)
}
loc, err := d.EntryLocation(entry)
if err != nil {
return 0, fmt.Errorf("variable %s: %s", name, err)
}
return loc, nil
// LookupVariable returns the entry for a (global) variable.
func (d *Data) LookupVariable(name string) (*Entry, error) {
return d.lookupEntry(name, TagVariable)
}
// EntryLocation returns the address of the object referred to by the given Entry.
......@@ -97,6 +76,15 @@ func (d *Data) EntryLocation(e *Entry) (uint64, error) {
return 0, fmt.Errorf("DWARF entry has an unimplemented Location op")
}
// EntryType returns the Type for an Entry.
func (d *Data) EntryType(e *Entry) (Type, error) {
off, err := d.EntryTypeOffset(e)
if err != nil {
return nil, err
}
return d.Type(off)
}
// EntryTypeOffset returns the offset in the given Entry's type attribute.
func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) {
v := e.Val(AttrType)
......@@ -110,46 +98,22 @@ func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) {
return off, nil
}
// LookupPC returns the name of a symbol at the specified PC.
func (d *Data) LookupPC(pc uint64) (string, error) {
entry, _, err := d.EntryForPC(pc)
if err != nil {
return "", err
}
nameAttr := entry.Val(AttrName)
if nameAttr == nil {
// TODO: this shouldn't be possible.
return "", fmt.Errorf("LookupPC: TODO")
// PCToFunction returns the entry and address for the function containing the
// specified PC.
func (d *Data) PCToFunction(pc uint64) (entry *Entry, lowpc uint64, err error) {
p := d.pcToFuncEntries
if len(p) == 0 {
return nil, 0, fmt.Errorf("no function addresses loaded")
}
name, ok := nameAttr.(string)
if !ok {
return "", fmt.Errorf("name for PC %#x is not a string", pc)
}
return name, nil
}
// EntryForPC returns the entry and address for a symbol at the specified PC.
func (d *Data) EntryForPC(pc uint64) (entry *Entry, lowpc uint64, err error) {
// TODO: do something better than a linear scan?
r := d.Reader()
for {
entry, err := r.Next()
if err != nil {
return nil, 0, err
}
if entry == nil {
// TODO: why don't we get an error here.
break
}
if entry.Tag != TagSubprogram {
continue
}
lowpc, lok := entry.Val(AttrLowpc).(uint64)
highpc, hok := entry.Val(AttrHighpc).(uint64)
if !lok || !hok || pc < lowpc || highpc <= pc {
continue
}
return entry, lowpc, nil
i := sort.Search(len(p), func(i int) bool { return p[i].pc > pc }) - 1
// The search failed if:
// - pc was before the start of any function.
// - The largest function bound not larger than pc was the end of a function,
// not the start of one.
// - The largest function bound not larger than pc was the start of a function
// that we don't know the end of, and the PC is much larger than the start.
if i == -1 || p[i].entry == nil || (i+1 == len(p) && pc-p[i].pc >= 1<<20) {
return nil, 0, fmt.Errorf("no function at %x", pc)
}
return nil, 0, fmt.Errorf("PC %#x not found", pc)
return p[i].entry, p[i].pc, nil
}
......@@ -9,6 +9,7 @@
package dwarf
import (
"fmt"
"reflect"
"strconv"
)
......@@ -138,12 +139,7 @@ type PtrType struct {
Type Type
}
func (t *PtrType) String() string {
if t.Name != "" {
return t.Name
}
return "*" + t.Type.String()
}
func (t *PtrType) String() string { return "*" + t.Type.String() }
// A StructType represents a struct, union, or C++ class type.
type StructType struct {
......@@ -165,9 +161,6 @@ type StructField struct {
}
func (t *StructType) String() string {
if t.Name != "" {
return t.Name
}
if t.StructName != "" {
return t.Kind + " " + t.StructName
}
......@@ -451,6 +444,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
// TagSubrangeType or TagEnumerationType giving one dimension.
// dimensions are in left to right order.
t := new(ArrayType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
......@@ -615,12 +609,30 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
case []byte:
// TODO: Should have original compilation
// unit here, not unknownFormat.
if len(loc) == 0 {
// Empty exprloc. f.ByteOffset=0.
break
}
b := makeBuf(d, unknownFormat{}, "location", 0, loc)
if x := b.uint8(); x != opPlusUconst {
err = DecodeError{name, kid.Offset, "unexpected opcode 0x" + strconv.FormatUint(uint64(x), 16)}
op := b.uint8()
switch op {
case opPlusUconst:
// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
f.ByteOffset = int64(b.uint())
b.assertEmpty()
case opConsts:
// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
f.ByteOffset = b.int()
op = b.uint8()
if op != opPlus {
err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
goto Error
}
b.assertEmpty()
default:
err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
goto Error
}
f.ByteOffset = int64(b.uint())
if b.err != nil {
err = b.err
goto Error
......@@ -662,6 +674,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
// Attributes:
// AttrType: subtype
t := new(QualType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
......@@ -690,6 +703,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
t.Name, _ = e.Val(AttrName).(string)
t.EnumName, _ = e.Val(AttrName).(string)
t.Val = make([]*EnumValue, 0, 8)
for kid := next(); kid != nil; kid = next() {
......@@ -735,6 +749,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
// AttrType: type of parameter
// TagUnspecifiedParameter: final ...
t := new(FuncType)
t.Name, _ = e.Val(AttrName).(string)
t.ReflectKind = getKind(e)
typ = t
typeCache[off] = t
......
......@@ -13,7 +13,7 @@ import (
"io"
"os"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
)
// TODO: error reporting detail
......
......@@ -14,7 +14,7 @@ import (
"io"
"os"
"github.com/derekparker/delve/dwarf/debug/dwarf"
"golang.org/x/debug/dwarf"
)
// A File represents an open Mach-O file.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册