diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 586b9addfde2d4ca3ddff5d3b19cef2d2f0aa0d6..1f9d217d7978c088f79c4d6f52cd8a23968a478f 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,6 +1,7 @@ { "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" diff --git a/dwarf/debug/dwarf/symbol.go b/dwarf/debug/dwarf/symbol.go deleted file mode 100644 index 6ddc836266b00ab5c30821a75ed52ee50bf64690..0000000000000000000000000000000000000000 --- a/dwarf/debug/dwarf/symbol.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2014 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 - -// This file provides simple methods to access the symbol table by name and address. - -import "fmt" - -// lookupEntry returns the 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 - } - if nameAttr.(string) == name { - return entry, nil - } - } - return nil, fmt.Errorf("DWARF entry for %q not found", name) -} - -// LookupEntry returns the Entry for the named symbol. -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 -} - -// 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 -} - -// EntryLocation returns the address of the object referred to by the given Entry. -func (d *Data) EntryLocation(e *Entry) (uint64, error) { - loc, _ := e.Val(AttrLocation).([]byte) - if len(loc) == 0 { - return 0, fmt.Errorf("DWARF entry has no Location attribute") - } - // TODO: implement the DWARF Location bytecode. What we have here only - // recognizes a program with a single literal opAddr bytecode. - if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize { - switch asize { - case 1: - return uint64(loc[1]), nil - case 2: - return uint64(d.order.Uint16(loc[1:])), nil - case 4: - return uint64(d.order.Uint32(loc[1:])), nil - case 8: - return d.order.Uint64(loc[1:]), nil - } - } - return 0, fmt.Errorf("DWARF entry has an unimplemented Location op") -} - -// EntryTypeOffset returns the offset in the given Entry's type attribute. -func (d *Data) EntryTypeOffset(e *Entry) (Offset, error) { - v := e.Val(AttrType) - if v == nil { - return 0, fmt.Errorf("DWARF entry has no Type attribute") - } - off, ok := v.(Offset) - if !ok { - return 0, fmt.Errorf("DWARF entry has an invalid Type attribute") - } - 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") - } - 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 - } - return nil, 0, fmt.Errorf("PC %#x not found", pc) -} diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go index a45a4d83976b4d49bec084d1e27ce975ddf6923a..339c278ac5c8a99f4d1eb8509ba2315ed016e15a 100755 --- a/dwarf/reader/reader.go +++ b/dwarf/reader/reader.go @@ -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" ) diff --git a/proc/eval.go b/proc/eval.go index 4c943dfb2c7f31100786dfd925d6b9dbe163646c..d7393f379336b5e8379149c417b8d37b518dd987 100644 --- a/proc/eval.go +++ b/proc/eval.go @@ -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, }, diff --git a/proc/proc.go b/proc/proc.go index 885825fa1faca3669ebcedf1765792ec06b35362..15bacf7f22bce0d1f186d92b9266425f545911ea 100644 --- a/proc/proc.go +++ b/proc/proc.go @@ -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" diff --git a/proc/proc_darwin.go b/proc/proc_darwin.go index 5df450bec122faa1db12b2328c067ad046c3762e..0e596d53d4318c63e91306d24016d81f725a1102 100644 --- a/proc/proc_darwin.go +++ b/proc/proc_darwin.go @@ -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" diff --git a/proc/proc_linux.go b/proc/proc_linux.go index 6ea8af45c9133b295edf34e41a3252f3120ba517..041318b78ccb1b6c278a68791a19a82bbe104720 100644 --- a/proc/proc_linux.go +++ b/proc/proc_linux.go @@ -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" ) diff --git a/proc/proc_windows.go b/proc/proc_windows.go index 34fea50345e792f36e31eab53f9d19d26f0fffaa..46f39a0fc17537c86b9660c2d8b5a8ca8fd8b51b 100644 --- a/proc/proc_windows.go +++ b/proc/proc_windows.go @@ -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" ) diff --git a/proc/threads.go b/proc/threads.go index 81a34f1603eea8a424f4cce16e3d86465de6cb0c..8c9749daabb99b42df354f0cfc61317f4f9248a8 100644 --- a/proc/threads.go +++ b/proc/threads.go @@ -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 diff --git a/proc/types.go b/proc/types.go index 1432aaa7dc58e487536d7ae2ac83e253813d10ea..27cf77ed184c914061b5e0eae762a9b3972e6cf2 100644 --- a/proc/types.go +++ b/proc/types.go @@ -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) { diff --git a/proc/variables.go b/proc/variables.go index 03483fdc34f7c451ad1f062b609382d3c6d6a8af..37647276126046855fae522c16fb1328d0ee5ef6 100644 --- a/proc/variables.go +++ b/proc/variables.go @@ -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 } diff --git a/service/api/conversions.go b/service/api/conversions.go index cc6bd8e9488c2cae29eaa71210a22def4f562275..2362181aee41b347bcd764a6fd0d7e0af43691a7 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -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" diff --git a/dwarf/debug/LICENSE b/vendor/golang.org/x/debug/LICENSE similarity index 100% rename from dwarf/debug/LICENSE rename to vendor/golang.org/x/debug/LICENSE diff --git a/dwarf/debug/dwarf/buf.go b/vendor/golang.org/x/debug/dwarf/buf.go similarity index 92% rename from dwarf/debug/dwarf/buf.go rename to vendor/golang.org/x/debug/dwarf/buf.go index a98d28393e46d27161363568e12d80fc5f4035dc..73fcc9abb762594d47a74391aede1f77eda1ace3 100644 --- a/dwarf/debug/dwarf/buf.go +++ b/vendor/golang.org/x/debug/dwarf/buf.go @@ -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 diff --git a/vendor/golang.org/x/debug/dwarf/cache.go b/vendor/golang.org/x/debug/dwarf/cache.go new file mode 100644 index 0000000000000000000000000000000000000000..cf795e707576ff5f7b93438d4d7402aeabcc209a --- /dev/null +++ b/vendor/golang.org/x/debug/dwarf/cache.go @@ -0,0 +1,249 @@ +// 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 +} diff --git a/dwarf/debug/dwarf/const.go b/vendor/golang.org/x/debug/dwarf/const.go similarity index 100% rename from dwarf/debug/dwarf/const.go rename to vendor/golang.org/x/debug/dwarf/const.go diff --git a/dwarf/debug/dwarf/entry.go b/vendor/golang.org/x/debug/dwarf/entry.go similarity index 100% rename from dwarf/debug/dwarf/entry.go rename to vendor/golang.org/x/debug/dwarf/entry.go diff --git a/dwarf/debug/dwarf/frame.go b/vendor/golang.org/x/debug/dwarf/frame.go similarity index 100% rename from dwarf/debug/dwarf/frame.go rename to vendor/golang.org/x/debug/dwarf/frame.go diff --git a/dwarf/debug/dwarf/line.go b/vendor/golang.org/x/debug/dwarf/line.go similarity index 80% rename from dwarf/debug/dwarf/line.go rename to vendor/golang.org/x/debug/dwarf/line.go index f035ef9b4a65b5f19406342dcbfda26f452985eb..2f47739d599c3386d6830c999908aafd03061a53 100644 --- a/dwarf/debug/dwarf/line.go +++ b/vendor/golang.org/x/debug/dwarf/line.go @@ -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 } diff --git a/dwarf/debug/dwarf/open.go b/vendor/golang.org/x/debug/dwarf/open.go similarity index 80% rename from dwarf/debug/dwarf/open.go rename to vendor/golang.org/x/debug/dwarf/open.go index c1b3f37aca9618ebbf5aa1f07eb691abeba9cfaa..c727e8b04d09bb1a928b334ed12ac62b415f2d4b 100644 --- a/dwarf/debug/dwarf/open.go +++ b/vendor/golang.org/x/debug/dwarf/open.go @@ -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 } diff --git a/vendor/golang.org/x/debug/dwarf/symbol.go b/vendor/golang.org/x/debug/dwarf/symbol.go new file mode 100644 index 0000000000000000000000000000000000000000..52d682902b868db67c65c8bc1870221180ec2faa --- /dev/null +++ b/vendor/golang.org/x/debug/dwarf/symbol.go @@ -0,0 +1,119 @@ +// Copyright 2014 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 + +// This file provides simple methods to access the symbol table by name and address. + +import ( + "fmt" + "regexp" + "sort" +) + +// 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) { + 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 + } + } + 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 result, nil +} + +// LookupEntry returns the Entry for the named symbol. +func (d *Data) LookupEntry(name string) (*Entry, error) { + return d.lookupEntry(name, 0) +} + +// LookupFunction returns the entry for a function. +func (d *Data) LookupFunction(name string) (*Entry, error) { + return d.lookupEntry(name, TagSubprogram) +} + +// 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. +func (d *Data) EntryLocation(e *Entry) (uint64, error) { + loc, _ := e.Val(AttrLocation).([]byte) + if len(loc) == 0 { + return 0, fmt.Errorf("DWARF entry has no Location attribute") + } + // TODO: implement the DWARF Location bytecode. What we have here only + // recognizes a program with a single literal opAddr bytecode. + if asize := d.unit[0].asize; loc[0] == opAddr && len(loc) == 1+asize { + switch asize { + case 1: + return uint64(loc[1]), nil + case 2: + return uint64(d.order.Uint16(loc[1:])), nil + case 4: + return uint64(d.order.Uint32(loc[1:])), nil + case 8: + return d.order.Uint64(loc[1:]), nil + } + } + 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) + if v == nil { + return 0, fmt.Errorf("DWARF entry has no Type attribute") + } + off, ok := v.(Offset) + if !ok { + return 0, fmt.Errorf("DWARF entry has an invalid Type attribute") + } + return off, nil +} + +// 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") + } + 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 p[i].entry, p[i].pc, nil +} diff --git a/dwarf/debug/dwarf/type.go b/vendor/golang.org/x/debug/dwarf/type.go similarity index 95% rename from dwarf/debug/dwarf/type.go rename to vendor/golang.org/x/debug/dwarf/type.go index 8cf4ac84adc51e88b687552f986d1dfeaa012b8d..ae1a3f35412f58f82120cd150dbfb55f3e29690c 100644 --- a/dwarf/debug/dwarf/type.go +++ b/vendor/golang.org/x/debug/dwarf/type.go @@ -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 ] + f.ByteOffset = int64(b.uint()) + b.assertEmpty() + case opConsts: + // Handle opcode sequence [DW_OP_consts 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 diff --git a/dwarf/debug/dwarf/typeunit.go b/vendor/golang.org/x/debug/dwarf/typeunit.go similarity index 100% rename from dwarf/debug/dwarf/typeunit.go rename to vendor/golang.org/x/debug/dwarf/typeunit.go diff --git a/dwarf/debug/dwarf/unit.go b/vendor/golang.org/x/debug/dwarf/unit.go similarity index 100% rename from dwarf/debug/dwarf/unit.go rename to vendor/golang.org/x/debug/dwarf/unit.go diff --git a/dwarf/debug/elf/elf.go b/vendor/golang.org/x/debug/elf/elf.go similarity index 100% rename from dwarf/debug/elf/elf.go rename to vendor/golang.org/x/debug/elf/elf.go diff --git a/dwarf/debug/elf/file.go b/vendor/golang.org/x/debug/elf/file.go similarity index 99% rename from dwarf/debug/elf/file.go rename to vendor/golang.org/x/debug/elf/file.go index da469f9443fc281a7e1d6035f7b6603ef833ebf9..69465505ff64490dddf34a4a9c81f68c468e9257 100644 --- a/dwarf/debug/elf/file.go +++ b/vendor/golang.org/x/debug/elf/file.go @@ -13,7 +13,7 @@ import ( "io" "os" - "github.com/derekparker/delve/dwarf/debug/dwarf" + "golang.org/x/debug/dwarf" ) // TODO: error reporting detail diff --git a/dwarf/debug/macho/fat.go b/vendor/golang.org/x/debug/macho/fat.go similarity index 100% rename from dwarf/debug/macho/fat.go rename to vendor/golang.org/x/debug/macho/fat.go diff --git a/dwarf/debug/macho/file.go b/vendor/golang.org/x/debug/macho/file.go similarity index 99% rename from dwarf/debug/macho/file.go rename to vendor/golang.org/x/debug/macho/file.go index ecb8247fba8be35e51816c647d27c0ae73d8dc01..7f59901af03ddacad8ce6105801c39953a893ae7 100644 --- a/dwarf/debug/macho/file.go +++ b/vendor/golang.org/x/debug/macho/file.go @@ -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. diff --git a/dwarf/debug/macho/macho.go b/vendor/golang.org/x/debug/macho/macho.go similarity index 100% rename from dwarf/debug/macho/macho.go rename to vendor/golang.org/x/debug/macho/macho.go