1
0
mirror of https://github.com/golang/go synced 2024-11-19 07:34:44 -07:00

debug/dwarf: expose file name table from line table reader

Currently, the line table reader keeps the file name table internal.
However, there are various attributes like AttrDeclFile and
AttrCallFile whose value is an index into this table. Hence, in order
to interpret these attributes, we need access to the file name table.

This CL adds a method to LineReader that exposes the file table of the
current compilation unit in order to allow consumers to interpret
attributes that index into this table.

Change-Id: I6b64b815f23b3b0695036ddabe1a67c3954867dd
Reviewed-on: https://go-review.googlesource.com/c/go/+/192699
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Austin Clements 2019-08-21 16:21:06 -04:00
parent bc40294d47
commit 861556c5d7
2 changed files with 81 additions and 6 deletions

View File

@ -441,6 +441,19 @@ func (r *LineReader) readFileEntry() (bool, error) {
mtime := r.buf.uint() mtime := r.buf.uint()
length := int(r.buf.uint()) length := int(r.buf.uint())
// If this is a dynamically added path and the cursor was
// backed up, we may have already added this entry. Avoid
// updating existing line table entries in this case. This
// avoids an allocation and potential racy access to the slice
// backing store if the user called Files.
if len(r.fileEntries) < cap(r.fileEntries) {
fe := r.fileEntries[:len(r.fileEntries)+1]
if fe[len(fe)-1] != nil {
// We already processed this addition.
r.fileEntries = fe
return false, nil
}
}
r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length}) r.fileEntries = append(r.fileEntries, &LineFile{name, mtime, length})
return false, nil return false, nil
} }
@ -692,6 +705,22 @@ func (r *LineReader) resetState() {
r.updateFile() r.updateFile()
} }
// Files returns the file name table of this compilation unit as of
// the current position in the line table. The file name table may be
// referenced from attributes in this compilation unit such as
// AttrDeclFile.
//
// Entry 0 is always nil, since file index 0 represents "no file".
//
// The file name table of a compilation unit is not fixed. Files
// returns the file table as of the current position in the line
// table. This may contain more entries than the file table at an
// earlier position in the line table, though existing entries never
// change.
func (r *LineReader) Files() []*LineFile {
return r.fileEntries
}
// ErrUnknownPC is the error returned by LineReader.ScanPC when the // ErrUnknownPC is the error returned by LineReader.ScanPC when the
// seek PC is not covered by any entry in the line table. // seek PC is not covered by any entry in the line table.
var ErrUnknownPC = errors.New("ErrUnknownPC") var ErrUnknownPC = errors.New("ErrUnknownPC")

View File

@ -43,8 +43,9 @@ func TestLineELFGCC(t *testing.T) {
{Address: 0x40060f, File: file2C, Line: 6, IsStmt: true}, {Address: 0x40060f, File: file2C, Line: 6, IsStmt: true},
{Address: 0x400611, EndSequence: true}, {Address: 0x400611, EndSequence: true},
} }
files := [][]*LineFile{{nil, file1H, file1C}, {nil, file2C}}
testLineTable(t, want, elfData(t, "testdata/line-gcc.elf")) testLineTable(t, want, files, elfData(t, "testdata/line-gcc.elf"))
} }
func TestLineGCCWindows(t *testing.T) { func TestLineGCCWindows(t *testing.T) {
@ -83,8 +84,9 @@ func TestLineGCCWindows(t *testing.T) {
{Address: 0x401595, File: file2C, Line: 6, IsStmt: true}, {Address: 0x401595, File: file2C, Line: 6, IsStmt: true},
{Address: 0x40159b, EndSequence: true}, {Address: 0x40159b, EndSequence: true},
} }
files := [][]*LineFile{{nil, file1H, file1C}, {nil, file2C}}
testLineTable(t, want, peData(t, "testdata/line-gcc-win.bin")) testLineTable(t, want, files, peData(t, "testdata/line-gcc-win.bin"))
} }
func TestLineELFClang(t *testing.T) { func TestLineELFClang(t *testing.T) {
@ -110,8 +112,9 @@ func TestLineELFClang(t *testing.T) {
{Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true}, {Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true},
{Address: 0x4005b0, EndSequence: true}, {Address: 0x4005b0, EndSequence: true},
} }
files := [][]*LineFile{{nil, file1C, file1H}, {nil, file2C}}
testLineTable(t, want, elfData(t, "testdata/line-clang.elf")) testLineTable(t, want, files, elfData(t, "testdata/line-clang.elf"))
} }
func TestLineSeek(t *testing.T) { func TestLineSeek(t *testing.T) {
@ -190,7 +193,7 @@ func TestLineSeek(t *testing.T) {
} }
} }
func testLineTable(t *testing.T, want []LineEntry, d *Data) { func testLineTable(t *testing.T, want []LineEntry, files [][]*LineFile, d *Data) {
// Get line table from d. // Get line table from d.
var got []LineEntry var got []LineEntry
dr := d.Reader() dr := d.Reader()
@ -207,6 +210,12 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) {
continue continue
} }
// Ignore system compilation units (this happens in
// the Windows binary). We'll still decode the line
// table, but won't check it.
name := ent.Val(AttrName).(string)
ignore := strings.HasPrefix(name, "C:/crossdev/") || strings.HasPrefix(name, "../../")
// Decode CU's line table. // Decode CU's line table.
lr, err := d.LineReader(ent) lr, err := d.LineReader(ent)
if err != nil { if err != nil {
@ -225,12 +234,23 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) {
t.Fatal("lr.Next:", err) t.Fatal("lr.Next:", err)
} }
// Ignore sources from the Windows build environment. // Ignore sources from the Windows build environment.
if strings.HasPrefix(line.File.Name, "C:\\crossdev\\") || if ignore {
strings.HasPrefix(line.File.Name, "C:/crossdev/") {
continue continue
} }
got = append(got, line) got = append(got, line)
} }
// Check file table.
if !ignore {
if !compareFiles(files[0], lr.Files()) {
t.Log("File tables do not match. Got:")
dumpFiles(t, lr.Files())
t.Log("Want:")
dumpFiles(t, files[0])
t.Fail()
}
files = files[1:]
}
} }
// Compare line tables. // Compare line tables.
@ -243,6 +263,32 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) {
} }
} }
func compareFiles(a, b []*LineFile) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] == nil && b[i] == nil {
continue
}
if a[i] != nil && b[i] != nil && a[i].Name == b[i].Name {
continue
}
return false
}
return true
}
func dumpFiles(t *testing.T, files []*LineFile) {
for i, f := range files {
name := "<nil>"
if f != nil {
name = f.Name
}
t.Logf(" %d %s", i, name)
}
}
func compareLines(a, b []LineEntry) bool { func compareLines(a, b []LineEntry) bool {
if len(a) != len(b) { if len(a) != len(b) {
return false return false