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
}
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")
{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) {
{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) {
{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) {
}
}
-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()
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 {
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.
}
}
+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 {
if len(a) != len(b) {
return false