aboutsummaryrefslogtreecommitdiff
path: root/src/debug/dwarf
diff options
context:
space:
mode:
authorAustin Clements <austin@google.com>2015-03-06 13:26:04 -0500
committerAustin Clements <austin@google.com>2015-03-10 02:32:48 +0000
commitfdd49d2be02fa62ecbf547e4224663b562077425 (patch)
tree278f9f6a674c09409a6259c72afd09b2de6e2b70 /src/debug/dwarf
parent339300926774c9e4868a04d1362c98b19706c7d1 (diff)
downloadgo-fdd49d2be02fa62ecbf547e4224663b562077425.tar.xz
debug/dwarf: add unit tests for line table reader
This adds simple ELF test binaries generated by gcc and clang and compares the line tables returned by the line table reader against tables based on the output of readelf. The binaries were generated with # gcc --version | head -n1 gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 # gcc -g -o line-gcc.elf line*.c # 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*.c Change-Id: Id210fdc1d007ac9719e8f5dc845f2b94eed12234 Reviewed-on: https://go-review.googlesource.com/7070 Reviewed-by: Nigel Tao <nigeltao@golang.org> Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/debug/dwarf')
-rw-r--r--src/debug/dwarf/line_test.go229
-rwxr-xr-xsrc/debug/dwarf/testdata/line-clang.elfbin0 -> 10271 bytes
-rwxr-xr-xsrc/debug/dwarf/testdata/line-gcc.elfbin0 -> 10113 bytes
-rw-r--r--src/debug/dwarf/testdata/line1.c9
-rw-r--r--src/debug/dwarf/testdata/line1.h7
-rw-r--r--src/debug/dwarf/testdata/line2.c6
6 files changed, 251 insertions, 0 deletions
diff --git a/src/debug/dwarf/line_test.go b/src/debug/dwarf/line_test.go
new file mode 100644
index 0000000000..4104b5d49b
--- /dev/null
+++ b/src/debug/dwarf/line_test.go
@@ -0,0 +1,229 @@
+// 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.
+
+package dwarf_test
+
+import (
+ . "debug/dwarf"
+ "io"
+ "testing"
+)
+
+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"}
+)
+
+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},
+
+ {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 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 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 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.Fatal("dr.Next:", err)
+ } else if ent == nil {
+ break
+ }
+
+ if ent.Tag != TagCompileUnit {
+ dr.SkipChildren()
+ continue
+ }
+
+ // Decode CU's line table.
+ lr, err := d.LineReader(ent)
+ if err != nil {
+ t.Fatal("d.LineReader:", err)
+ } else if lr == nil {
+ continue
+ }
+
+ 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)
+ }
+}
diff --git a/src/debug/dwarf/testdata/line-clang.elf b/src/debug/dwarf/testdata/line-clang.elf
new file mode 100755
index 0000000000..b63cc781c4
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-clang.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line-gcc.elf b/src/debug/dwarf/testdata/line-gcc.elf
new file mode 100755
index 0000000000..50500a8eec
--- /dev/null
+++ b/src/debug/dwarf/testdata/line-gcc.elf
Binary files differ
diff --git a/src/debug/dwarf/testdata/line1.c b/src/debug/dwarf/testdata/line1.c
new file mode 100644
index 0000000000..f35864776c
--- /dev/null
+++ b/src/debug/dwarf/testdata/line1.c
@@ -0,0 +1,9 @@
+#include "line1.h"
+
+void f2();
+
+int main()
+{
+ f1();
+ f2();
+}
diff --git a/src/debug/dwarf/testdata/line1.h b/src/debug/dwarf/testdata/line1.h
new file mode 100644
index 0000000000..974d4c8817
--- /dev/null
+++ b/src/debug/dwarf/testdata/line1.h
@@ -0,0 +1,7 @@
+static void f1()
+{
+ char buf[10];
+ int i;
+ for(i = 0; i < 10; i++)
+ buf[i] = 1;
+}
diff --git a/src/debug/dwarf/testdata/line2.c b/src/debug/dwarf/testdata/line2.c
new file mode 100644
index 0000000000..38d89983cb
--- /dev/null
+++ b/src/debug/dwarf/testdata/line2.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void f2()
+{
+ printf("hello\n");
+}