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