diff options
Diffstat (limited to 'libgo/go/debug/dwarf/line_test.go')
-rw-r--r-- | libgo/go/debug/dwarf/line_test.go | 232 |
1 files changed, 204 insertions, 28 deletions
diff --git a/libgo/go/debug/dwarf/line_test.go b/libgo/go/debug/dwarf/line_test.go index 2476a6faf53..4104b5d49b8 100644 --- a/libgo/go/debug/dwarf/line_test.go +++ b/libgo/go/debug/dwarf/line_test.go @@ -1,4 +1,4 @@ -// Copyright 2012 The Go Authors. All rights reserved. +// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. @@ -6,48 +6,224 @@ package dwarf_test import ( . "debug/dwarf" - "path/filepath" + "io" "testing" ) -type lineTest struct { - pc uint64 - file string - line int -} +var ( + file1C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.c"} + file1H = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.h"} + file2C = &LineFile{Name: "/home/austin/go.dev/src/debug/dwarf/testdata/line2.c"} +) -var elfLineTests = [...]lineTest{ - {0x4004c4, "typedef.c", 83}, - {0x4004c8, "typedef.c", 84}, - {0x4004ca, "typedef.c", 84}, - {0x4003e0, "", 0}, -} +func TestLineELFGCC(t *testing.T) { + // Generated by: + // # gcc --version | head -n1 + // gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 + // # gcc -g -o line-gcc.elf line*.c + + // Line table based on readelf --debug-dump=rawline,decodedline + want := []LineEntry{ + {Address: 0x40059d, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x4005a5, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x4005b4, File: file1H, Line: 5, IsStmt: true}, + {Address: 0x4005bd, File: file1H, Line: 6, IsStmt: true, Discriminator: 2}, + {Address: 0x4005c7, File: file1H, Line: 5, IsStmt: true, Discriminator: 2}, + {Address: 0x4005cb, File: file1H, Line: 5, IsStmt: false, Discriminator: 1}, + {Address: 0x4005d1, File: file1H, Line: 7, IsStmt: true}, + {Address: 0x4005e7, File: file1C, Line: 6, IsStmt: true}, + {Address: 0x4005eb, File: file1C, Line: 7, IsStmt: true}, + {Address: 0x4005f5, File: file1C, Line: 8, IsStmt: true}, + {Address: 0x4005ff, File: file1C, Line: 9, IsStmt: true}, + {Address: 0x400601, EndSequence: true}, -var machoLineTests = [...]lineTest{ - {0x0, "typedef.c", 83}, + {Address: 0x400601, File: file2C, Line: 4, IsStmt: true}, + {Address: 0x400605, File: file2C, Line: 5, IsStmt: true}, + {Address: 0x40060f, File: file2C, Line: 6, IsStmt: true}, + {Address: 0x400611, EndSequence: true}, + } + + testLineTable(t, want, elfData(t, "testdata/line-gcc.elf")) } -func TestLineElf(t *testing.T) { - testLine(t, elfData(t, "testdata/typedef.elf"), elfLineTests[:], "elf") +func TestLineELFClang(t *testing.T) { + // Generated by: + // # clang --version | head -n1 + // Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4) + // # clang -g -o line-clang.elf line*. + + want := []LineEntry{ + {Address: 0x400530, File: file1C, Line: 6, IsStmt: true}, + {Address: 0x400534, File: file1C, Line: 7, IsStmt: true, PrologueEnd: true}, + {Address: 0x400539, File: file1C, Line: 8, IsStmt: true}, + {Address: 0x400545, File: file1C, Line: 9, IsStmt: true}, + {Address: 0x400550, File: file1H, Line: 2, IsStmt: true}, + {Address: 0x400554, File: file1H, Line: 5, IsStmt: true, PrologueEnd: true}, + {Address: 0x400568, File: file1H, Line: 6, IsStmt: true}, + {Address: 0x400571, File: file1H, Line: 5, IsStmt: true}, + {Address: 0x400581, File: file1H, Line: 7, IsStmt: true}, + {Address: 0x400583, EndSequence: true}, + + {Address: 0x400590, File: file2C, Line: 4, IsStmt: true}, + {Address: 0x4005a0, File: file2C, Line: 5, IsStmt: true, PrologueEnd: true}, + {Address: 0x4005a7, File: file2C, Line: 6, IsStmt: true}, + {Address: 0x4005b0, EndSequence: true}, + } + + testLineTable(t, want, elfData(t, "testdata/line-clang.elf")) } -func TestLineMachO(t *testing.T) { - testLine(t, machoData(t, "testdata/typedef.macho"), machoLineTests[:], "macho") +func TestLineSeek(t *testing.T) { + d := elfData(t, "testdata/line-gcc.elf") + + // Get the line table for the first CU. + cu, err := d.Reader().Next() + if err != nil { + t.Fatal("d.Reader().Next:", err) + } + lr, err := d.LineReader(cu) + if err != nil { + t.Fatal("d.LineReader:", err) + } + + // Read entries forward. + var line LineEntry + var posTable []LineReaderPos + var table []LineEntry + for { + posTable = append(posTable, lr.Tell()) + + err := lr.Next(&line) + if err != nil { + if err == io.EOF { + break + } + t.Fatal("lr.Next:", err) + } + table = append(table, line) + } + + // Test that Reset returns to the first line. + lr.Reset() + if err := lr.Next(&line); err != nil { + t.Fatal("lr.Next after Reset failed:", err) + } else if line != table[0] { + t.Fatal("lr.Next after Reset returned", line, "instead of", table[0]) + } + + // Check that entries match when seeking backward. + for i := len(posTable) - 1; i >= 0; i-- { + lr.Seek(posTable[i]) + err := lr.Next(&line) + if i == len(posTable)-1 { + if err != io.EOF { + t.Fatal("expected io.EOF after seek to end, got", err) + } + } else if err != nil { + t.Fatal("lr.Next after seek to", posTable[i], "failed:", err) + } else if line != table[i] { + t.Fatal("lr.Next after seek to", posTable[i], "returned", line, "instead of", table[i]) + } + } + + // Check that seeking to a PC returns the right line. + if err := lr.SeekPC(table[0].Address-1, &line); err != ErrUnknownPC { + t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", table[0].Address-1, err) + } + for i, testLine := range table { + if testLine.EndSequence { + if err := lr.SeekPC(testLine.Address, &line); err != ErrUnknownPC { + t.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", testLine.Address, err) + } + continue + } + + nextPC := table[i+1].Address + for pc := testLine.Address; pc < nextPC; pc++ { + if err := lr.SeekPC(pc, &line); err != nil { + t.Fatalf("lr.SeekPC to %#x failed: %v", pc, err) + } else if line != testLine { + t.Fatalf("lr.SeekPC to %#x returned %v instead of %v", pc, line, testLine) + } + } + } } -func testLine(t *testing.T, d *Data, tests []lineTest, kind string) { - for _, v := range tests { - file, line, err := d.FileLine(v.pc) +func testLineTable(t *testing.T, want []LineEntry, d *Data) { + // Get line table from d. + var got []LineEntry + dr := d.Reader() + for { + ent, err := dr.Next() if err != nil { - t.Errorf("%s: %v", kind, err) + t.Fatal("dr.Next:", err) + } else if ent == nil { + break + } + + if ent.Tag != TagCompileUnit { + dr.SkipChildren() continue } - if file != "" { - file = filepath.Base(file) + + // Decode CU's line table. + lr, err := d.LineReader(ent) + if err != nil { + t.Fatal("d.LineReader:", err) + } else if lr == nil { + continue } - if file != v.file || line != v.line { - t.Errorf("%s: for %d have %q:%d want %q:%d", - kind, v.pc, file, line, v.file, v.line) + + for { + var line LineEntry + err := lr.Next(&line) + if err != nil { + if err == io.EOF { + break + } + t.Fatal("lr.Next:", err) + } + got = append(got, line) } } + + // Compare line tables. + if !compareLines(got, want) { + t.Log("Line tables do not match. Got:") + dumpLines(t, got) + t.Log("Want:") + dumpLines(t, want) + t.FailNow() + } +} + +func compareLines(a, b []LineEntry) bool { + if len(a) != len(b) { + return false + } + + for i := range a { + al, bl := a[i], b[i] + // If both are EndSequence, then the only other valid + // field is Address. Otherwise, test equality of all + // fields. + if al.EndSequence && bl.EndSequence && al.Address == bl.Address { + continue + } + if al.File.Name != bl.File.Name { + return false + } + al.File = nil + bl.File = nil + if al != bl { + return false + } + } + return true +} + +func dumpLines(t *testing.T, lines []LineEntry) { + for _, l := range lines { + t.Logf(" %+v File:%+v", l, l.File) + } } |