From d1b8871f13203cd99d5e7d686170f0e266760084 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 16 Mar 2016 14:15:54 -0700 Subject: [PATCH] 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 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/debug/dwarf/entry.go | 120 ++++++++++++++++++++++++++++ src/debug/dwarf/entry_test.go | 101 +++++++++++++++++++++++ src/debug/dwarf/testdata/ranges.c | 25 ++++++ src/debug/dwarf/testdata/ranges.elf | Bin 0 -> 10348 bytes src/debug/elf/file.go | 4 +- src/debug/macho/file.go | 6 +- src/debug/pe/file.go | 6 +- 7 files changed, 254 insertions(+), 8 deletions(-) create mode 100644 src/debug/dwarf/testdata/ranges.c create mode 100755 src/debug/dwarf/testdata/ranges.elf 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 +} diff --git a/src/debug/dwarf/entry_test.go b/src/debug/dwarf/entry_test.go index 8bd2d2a8ad..58a5d570be 100644 --- a/src/debug/dwarf/entry_test.go +++ b/src/debug/dwarf/entry_test.go @@ -6,6 +6,7 @@ package dwarf_test import ( . "debug/dwarf" + "reflect" "testing" ) @@ -34,3 +35,103 @@ func TestSplit(t *testing.T) { t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown) } } + +// wantRange maps from a PC to the ranges of the compilation unit +// containing that PC. +type wantRange struct { + pc uint64 + ranges [][2]uint64 +} + +func TestReaderSeek(t *testing.T) { + want := []wantRange{ + {0x40059d, [][2]uint64{{0x40059d, 0x400601}}}, + {0x400600, [][2]uint64{{0x40059d, 0x400601}}}, + {0x400601, [][2]uint64{{0x400601, 0x400611}}}, + {0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test + {0x10, nil}, + {0x400611, nil}, + } + testRanges(t, "testdata/line-gcc.elf", want) +} + +func TestRangesSection(t *testing.T) { + want := []wantRange{ + {0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, + {0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, + {0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, + {0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}}, + {0x400408, nil}, + {0x400449, nil}, + {0x4003ff, nil}, + } + testRanges(t, "testdata/ranges.elf", want) +} + +func testRanges(t *testing.T, name string, want []wantRange) { + d := elfData(t, name) + r := d.Reader() + for _, w := range want { + entry, err := r.SeekPC(w.pc) + if err != nil { + if w.ranges != nil { + t.Errorf("%s: missing Entry for %#x", name, w.pc) + } + if err != ErrUnknownPC { + t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err) + } + continue + } + + ranges, err := d.Ranges(entry) + if err != nil { + t.Errorf("%s: %v", name, err) + continue + } + if !reflect.DeepEqual(ranges, w.ranges) { + t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges) + } + } +} + +func TestReaderRanges(t *testing.T) { + d := elfData(t, "testdata/line-gcc.elf") + + subprograms := []struct { + name string + ranges [][2]uint64 + }{ + {"f1", [][2]uint64{{0x40059d, 0x4005e7}}}, + {"main", [][2]uint64{{0x4005e7, 0x400601}}}, + {"f2", [][2]uint64{{0x400601, 0x400611}}}, + } + + r := d.Reader() + i := 0 + for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() { + if entry.Tag != TagSubprogram { + continue + } + + if i > len(subprograms) { + t.Fatalf("too many subprograms (expected at most %d)", i) + } + + if got := entry.Val(AttrName).(string); got != subprograms[i].name { + t.Errorf("subprogram %d name is %s, expected %s", i, got, subprograms[i].name) + } + ranges, err := d.Ranges(entry) + if err != nil { + t.Errorf("subprogram %d: %v", i, err) + continue + } + if !reflect.DeepEqual(ranges, subprograms[i].ranges) { + t.Errorf("subprogram %d ranges are %x, expected %x", i, ranges, subprograms[i].ranges) + } + i++ + } + + if i < len(subprograms) { + t.Errorf("saw only %d subprograms, expected %d", i, len(subprograms)) + } +} diff --git a/src/debug/dwarf/testdata/ranges.c b/src/debug/dwarf/testdata/ranges.c new file mode 100644 index 0000000000..2f208e591c --- /dev/null +++ b/src/debug/dwarf/testdata/ranges.c @@ -0,0 +1,25 @@ +// gcc -g -O2 -freorder-blocks-and-partition + +const char *arr[10000]; +const char *hot = "hot"; +const char *cold = "cold"; + +__attribute__((noinline)) +void fn(int path) { + int i; + + if (path) { + for (i = 0; i < sizeof arr / sizeof arr[0]; i++) { + arr[i] = hot; + } + } else { + for (i = 0; i < sizeof arr / sizeof arr[0]; i++) { + arr[i] = cold; + } + } +} + +int main(int argc, char *argv[]) { + fn(argc); + return 0; +} diff --git a/src/debug/dwarf/testdata/ranges.elf b/src/debug/dwarf/testdata/ranges.elf new file mode 100755 index 0000000000000000000000000000000000000000..7f54138cffb3bc61a8e537a5becdfc913a41da3a GIT binary patch literal 10348 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 NlkaiGDCNwF{{TVI*H!=k literal 0 HcmV?d00001 diff --git a/src/debug/elf/file.go b/src/debug/elf/file.go index 17bf76b29e..72796d535f 100644 --- a/src/debug/elf/file.go +++ b/src/debug/elf/file.go @@ -958,7 +958,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the ones the debug/dwarf package uses. // Don't bother loading others. - var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil} + var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} for i, s := range f.Sections { suffix := "" switch { @@ -979,7 +979,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { dat[suffix] = b } - d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, nil, dat["str"]) + d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) if err != nil { return nil, err } diff --git a/src/debug/macho/file.go b/src/debug/macho/file.go index 063a6f5ff8..223346f10d 100644 --- a/src/debug/macho/file.go +++ b/src/debug/macho/file.go @@ -474,7 +474,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the ones the debug/dwarf package uses. // Don't bother loading others. - var names = [...]string{"abbrev", "info", "line", "str"} + var names = [...]string{"abbrev", "info", "line", "ranges", "str"} var dat [len(names)][]byte for i, name := range names { name = "__debug_" + name @@ -489,8 +489,8 @@ func (f *File) DWARF() (*dwarf.Data, error) { dat[i] = b } - abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3] - return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str) + abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] + return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) } // ImportedSymbols returns the names of all symbols diff --git a/src/debug/pe/file.go b/src/debug/pe/file.go index 6adb039fd9..1acc368e1b 100644 --- a/src/debug/pe/file.go +++ b/src/debug/pe/file.go @@ -298,7 +298,7 @@ func (f *File) DWARF() (*dwarf.Data, error) { // There are many other DWARF sections, but these // are the ones the debug/dwarf package uses. // Don't bother loading others. - var names = [...]string{"abbrev", "info", "line", "str"} + var names = [...]string{"abbrev", "info", "line", "ranges", "str"} var dat [len(names)][]byte for i, name := range names { name = ".debug_" + name @@ -316,8 +316,8 @@ func (f *File) DWARF() (*dwarf.Data, error) { dat[i] = b } - abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3] - return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str) + abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4] + return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) } // ImportedSymbols returns the names of all symbols -- 2.50.0