From 861556c5d7b83a75a66549ca4c7199e343f5eb40 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 21 Aug 2019 16:21:06 -0400 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Ian Lance Taylor --- src/debug/dwarf/line.go | 29 ++++++++++++++++++ src/debug/dwarf/line_test.go | 58 ++++++++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/src/debug/dwarf/line.go b/src/debug/dwarf/line.go index 4fc1896dbc0..1cd9dd98cf7 100644 --- a/src/debug/dwarf/line.go +++ b/src/debug/dwarf/line.go @@ -441,6 +441,19 @@ func (r *LineReader) readFileEntry() (bool, error) { mtime := 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}) return false, nil } @@ -692,6 +705,22 @@ func (r *LineReader) resetState() { 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 // seek PC is not covered by any entry in the line table. var ErrUnknownPC = errors.New("ErrUnknownPC") diff --git a/src/debug/dwarf/line_test.go b/src/debug/dwarf/line_test.go index 11a254464a2..1fd9b19b035 100644 --- a/src/debug/dwarf/line_test.go +++ b/src/debug/dwarf/line_test.go @@ -43,8 +43,9 @@ func TestLineELFGCC(t *testing.T) { {Address: 0x40060f, File: file2C, Line: 6, IsStmt: 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) { @@ -83,8 +84,9 @@ func TestLineGCCWindows(t *testing.T) { {Address: 0x401595, File: file2C, Line: 6, IsStmt: 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) { @@ -110,8 +112,9 @@ func TestLineELFClang(t *testing.T) { {Address: 0x4005a7, File: file2C, Line: 6, IsStmt: 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) { @@ -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. var got []LineEntry dr := d.Reader() @@ -207,6 +210,12 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) { 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. lr, err := d.LineReader(ent) if err != nil { @@ -225,12 +234,23 @@ func testLineTable(t *testing.T, want []LineEntry, d *Data) { t.Fatal("lr.Next:", err) } // Ignore sources from the Windows build environment. - if strings.HasPrefix(line.File.Name, "C:\\crossdev\\") || - strings.HasPrefix(line.File.Name, "C:/crossdev/") { + if ignore { continue } 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. @@ -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 := "" + if f != nil { + name = f.Name + } + t.Logf(" %d %s", i, name) + } +} + func compareLines(a, b []LineEntry) bool { if len(a) != len(b) { return false