diff options
| author | Ian Lance Taylor <iant@golang.org> | 2016-03-16 14:15:54 -0700 |
|---|---|---|
| committer | Ian Lance Taylor <iant@golang.org> | 2016-03-22 14:06:09 +0000 |
| commit | d1b8871f13203cd99d5e7d686170f0e266760084 (patch) | |
| tree | 7458f93ff93d8c7b849eab786932a1682657337b /src/debug/dwarf/entry.go | |
| parent | 77f4b773e72b0840a1ce0b314cba44dff9fbaf31 (diff) | |
| download | go-d1b8871f13203cd99d5e7d686170f0e266760084.tar.xz | |
debug/dwarf: add Reader.SeekPC and Data.Ranges
These new methods help find the compilation unit to pass to the
LineReader method in order to find the line information for a PC.
The Ranges method also helps identify the specific function for a PC,
needed to determine the function name.
This uses the .debug.ranges section if necessary, and changes the object
file format packages to pass in the section contents if available.
Change-Id: I5ebc3d27faaf1a126ffb17a1e6027efdf64af836
Reviewed-on: https://go-review.googlesource.com/20769
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/debug/dwarf/entry.go')
| -rw-r--r-- | src/debug/dwarf/entry.go | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go index 6f72005e72..80bf14cb22 100644 --- a/src/debug/dwarf/entry.go +++ b/src/debug/dwarf/entry.go @@ -636,3 +636,123 @@ func (r *Reader) clone() typeReader { func (r *Reader) offset() Offset { return r.b.off } + +// SeekPC returns the Entry for the compilation unit that includes pc, +// and positions the reader to read the children of that unit. If pc +// is not covered by any unit, SeekPC returns ErrUnknownPC and the +// position of the reader is undefined. +// +// Because compilation units can describe multiple regions of the +// executable, in the worst case SeekPC must search through all the +// ranges in all the compilation units. Each call to SeekPC starts the +// search at the compilation unit of the last call, so in general +// looking up a series of PCs will be faster if they are sorted. If +// the caller wishes to do repeated fast PC lookups, it should build +// an appropriate index using the Ranges method. +func (r *Reader) SeekPC(pc uint64) (*Entry, error) { + unit := r.unit + for i := 0; i < len(r.d.unit); i++ { + if unit >= len(r.d.unit) { + unit = 0 + } + r.err = nil + r.lastChildren = false + r.unit = unit + u := &r.d.unit[unit] + r.b = makeBuf(r.d, u, "info", u.off, u.data) + e, err := r.Next() + if err != nil { + return nil, err + } + ranges, err := r.d.Ranges(e) + if err != nil { + return nil, err + } + for _, pcs := range ranges { + if pcs[0] <= pc && pc < pcs[1] { + return e, nil + } + } + unit++ + } + return nil, ErrUnknownPC +} + +// Ranges returns the PC ranges covered by e, a slice of [low,high) pairs. +// Only some entry types, such as TagCompileUnit or TagSubprogram, have PC +// ranges; for others, this will return nil with no error. +func (d *Data) Ranges(e *Entry) ([][2]uint64, error) { + var ret [][2]uint64 + + low, lowOK := e.Val(AttrLowpc).(uint64) + + var high uint64 + var highOK bool + highField := e.AttrField(AttrHighpc) + if highField != nil { + switch highField.Class { + case ClassAddress: + high, highOK = highField.Val.(uint64) + case ClassConstant: + off, ok := highField.Val.(int64) + if ok { + high = low + uint64(off) + highOK = true + } + } + } + + if lowOK && highOK { + ret = append(ret, [2]uint64{low, high}) + } + + ranges, rangesOK := e.Val(AttrRanges).(int64) + if rangesOK && d.ranges != nil { + // The initial base address is the lowpc attribute + // of the enclosing compilation unit. + // Although DWARF specifies the lowpc attribute, + // comments in gdb/dwarf2read.c say that some versions + // of GCC use the entrypc attribute, so we check that too. + var cu *Entry + if e.Tag == TagCompileUnit { + cu = e + } else { + i := d.offsetToUnit(e.Offset) + if i == -1 { + return nil, errors.New("no unit for entry") + } + u := &d.unit[i] + b := makeBuf(d, u, "info", u.off, u.data) + cu = b.entry(u.atable, u.base) + if b.err != nil { + return nil, b.err + } + } + + var base uint64 + if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK { + base = cuEntry + } else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK { + base = cuLow + } + + u := &d.unit[d.offsetToUnit(e.Offset)] + buf := makeBuf(d, u, "ranges", Offset(ranges), d.ranges[ranges:]) + for len(buf.data) > 0 { + low = buf.addr() + high = buf.addr() + + if low == 0 && high == 0 { + break + } + + if low == ^uint64(0)>>uint((8-u.addrsize())*8) { + base = high + } else { + ret = append(ret, [2]uint64{base + low, base + high}) + } + } + } + + return ret, nil +} |
