1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
15 file1C
= &LineFile
{Name
: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.c"}
16 file1H
= &LineFile
{Name
: "/home/austin/go.dev/src/debug/dwarf/testdata/line1.h"}
17 file2C
= &LineFile
{Name
: "/home/austin/go.dev/src/debug/dwarf/testdata/line2.c"}
20 func TestLineELFGCC(t
*testing
.T
) {
22 // # gcc --version | head -n1
23 // gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
24 // # gcc -g -o line-gcc.elf line*.c
26 // Line table based on readelf --debug-dump=rawline,decodedline
28 {Address
: 0x40059d, File
: file1H
, Line
: 2, IsStmt
: true},
29 {Address
: 0x4005a5, File
: file1H
, Line
: 2, IsStmt
: true},
30 {Address
: 0x4005b4, File
: file1H
, Line
: 5, IsStmt
: true},
31 {Address
: 0x4005bd, File
: file1H
, Line
: 6, IsStmt
: true, Discriminator
: 2},
32 {Address
: 0x4005c7, File
: file1H
, Line
: 5, IsStmt
: true, Discriminator
: 2},
33 {Address
: 0x4005cb, File
: file1H
, Line
: 5, IsStmt
: false, Discriminator
: 1},
34 {Address
: 0x4005d1, File
: file1H
, Line
: 7, IsStmt
: true},
35 {Address
: 0x4005e7, File
: file1C
, Line
: 6, IsStmt
: true},
36 {Address
: 0x4005eb, File
: file1C
, Line
: 7, IsStmt
: true},
37 {Address
: 0x4005f5, File
: file1C
, Line
: 8, IsStmt
: true},
38 {Address
: 0x4005ff, File
: file1C
, Line
: 9, IsStmt
: true},
39 {Address
: 0x400601, EndSequence
: true},
41 {Address
: 0x400601, File
: file2C
, Line
: 4, IsStmt
: true},
42 {Address
: 0x400605, File
: file2C
, Line
: 5, IsStmt
: true},
43 {Address
: 0x40060f, File
: file2C
, Line
: 6, IsStmt
: true},
44 {Address
: 0x400611, EndSequence
: true},
47 testLineTable(t
, want
, elfData(t
, "testdata/line-gcc.elf"))
50 func TestLineGCCWindows(t
*testing
.T
) {
53 // gcc (tdm64-1) 4.9.2
54 // > gcc -g -o line-gcc-win.bin line1.c C:\workdir\go\src\debug\dwarf\testdata\line2.c
56 toWindows
:= func(lf
*LineFile
) *LineFile
{
58 lf2
.Name
= strings
.Replace(lf2
.Name
, "/home/austin/go.dev/", "C:\\workdir\\go\\", -1)
59 lf2
.Name
= strings
.Replace(lf2
.Name
, "/", "\\", -1)
62 file1C
:= toWindows(file1C
)
63 file1H
:= toWindows(file1H
)
64 file2C
:= toWindows(file2C
)
66 // Line table based on objdump --dwarf=rawline,decodedline
68 {Address
: 0x401530, File
: file1H
, Line
: 2, IsStmt
: true},
69 {Address
: 0x401538, File
: file1H
, Line
: 5, IsStmt
: true},
70 {Address
: 0x401541, File
: file1H
, Line
: 6, IsStmt
: true, Discriminator
: 3},
71 {Address
: 0x40154b, File
: file1H
, Line
: 5, IsStmt
: true, Discriminator
: 3},
72 {Address
: 0x40154f, File
: file1H
, Line
: 5, IsStmt
: false, Discriminator
: 1},
73 {Address
: 0x401555, File
: file1H
, Line
: 7, IsStmt
: true},
74 {Address
: 0x40155b, File
: file1C
, Line
: 6, IsStmt
: true},
75 {Address
: 0x401563, File
: file1C
, Line
: 6, IsStmt
: true},
76 {Address
: 0x401568, File
: file1C
, Line
: 7, IsStmt
: true},
77 {Address
: 0x40156d, File
: file1C
, Line
: 8, IsStmt
: true},
78 {Address
: 0x401572, File
: file1C
, Line
: 9, IsStmt
: true},
79 {Address
: 0x401578, EndSequence
: true},
81 {Address
: 0x401580, File
: file2C
, Line
: 4, IsStmt
: true},
82 {Address
: 0x401588, File
: file2C
, Line
: 5, IsStmt
: true},
83 {Address
: 0x401595, File
: file2C
, Line
: 6, IsStmt
: true},
84 {Address
: 0x40159b, EndSequence
: true},
87 testLineTable(t
, want
, peData(t
, "testdata/line-gcc-win.bin"))
90 func TestLineELFClang(t
*testing
.T
) {
92 // # clang --version | head -n1
93 // Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
94 // # clang -g -o line-clang.elf line*.
97 {Address
: 0x400530, File
: file1C
, Line
: 6, IsStmt
: true},
98 {Address
: 0x400534, File
: file1C
, Line
: 7, IsStmt
: true, PrologueEnd
: true},
99 {Address
: 0x400539, File
: file1C
, Line
: 8, IsStmt
: true},
100 {Address
: 0x400545, File
: file1C
, Line
: 9, IsStmt
: true},
101 {Address
: 0x400550, File
: file1H
, Line
: 2, IsStmt
: true},
102 {Address
: 0x400554, File
: file1H
, Line
: 5, IsStmt
: true, PrologueEnd
: true},
103 {Address
: 0x400568, File
: file1H
, Line
: 6, IsStmt
: true},
104 {Address
: 0x400571, File
: file1H
, Line
: 5, IsStmt
: true},
105 {Address
: 0x400581, File
: file1H
, Line
: 7, IsStmt
: true},
106 {Address
: 0x400583, EndSequence
: true},
108 {Address
: 0x400590, File
: file2C
, Line
: 4, IsStmt
: true},
109 {Address
: 0x4005a0, File
: file2C
, Line
: 5, IsStmt
: true, PrologueEnd
: true},
110 {Address
: 0x4005a7, File
: file2C
, Line
: 6, IsStmt
: true},
111 {Address
: 0x4005b0, EndSequence
: true},
114 testLineTable(t
, want
, elfData(t
, "testdata/line-clang.elf"))
117 func TestLineSeek(t
*testing
.T
) {
118 d
:= elfData(t
, "testdata/line-gcc.elf")
120 // Get the line table for the first CU.
121 cu
, err
:= d
.Reader().Next()
123 t
.Fatal("d.Reader().Next:", err
)
125 lr
, err
:= d
.LineReader(cu
)
127 t
.Fatal("d.LineReader:", err
)
130 // Read entries forward.
132 var posTable
[]LineReaderPos
133 var table
[]LineEntry
135 posTable
= append(posTable
, lr
.Tell())
137 err
:= lr
.Next(&line
)
142 t
.Fatal("lr.Next:", err
)
144 table
= append(table
, line
)
147 // Test that Reset returns to the first line.
149 if err
:= lr
.Next(&line
); err
!= nil {
150 t
.Fatal("lr.Next after Reset failed:", err
)
151 } else if line
!= table
[0] {
152 t
.Fatal("lr.Next after Reset returned", line
, "instead of", table
[0])
155 // Check that entries match when seeking backward.
156 for i
:= len(posTable
) - 1; i
>= 0; i
-- {
158 err
:= lr
.Next(&line
)
159 if i
== len(posTable
)-1 {
161 t
.Fatal("expected io.EOF after seek to end, got", err
)
163 } else if err
!= nil {
164 t
.Fatal("lr.Next after seek to", posTable
[i
], "failed:", err
)
165 } else if line
!= table
[i
] {
166 t
.Fatal("lr.Next after seek to", posTable
[i
], "returned", line
, "instead of", table
[i
])
170 // Check that seeking to a PC returns the right line.
171 if err
:= lr
.SeekPC(table
[0].Address
-1, &line
); err
!= ErrUnknownPC
{
172 t
.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", table
[0].Address
-1, err
)
174 for i
, testLine
:= range table
{
175 if testLine
.EndSequence
{
176 if err
:= lr
.SeekPC(testLine
.Address
, &line
); err
!= ErrUnknownPC
{
177 t
.Fatalf("lr.SeekPC to %#x returned %v instead of ErrUnknownPC", testLine
.Address
, err
)
182 nextPC
:= table
[i
+1].Address
183 for pc
:= testLine
.Address
; pc
< nextPC
; pc
++ {
184 if err
:= lr
.SeekPC(pc
, &line
); err
!= nil {
185 t
.Fatalf("lr.SeekPC to %#x failed: %v", pc
, err
)
186 } else if line
!= testLine
{
187 t
.Fatalf("lr.SeekPC to %#x returned %v instead of %v", pc
, line
, testLine
)
193 func testLineTable(t
*testing
.T
, want
[]LineEntry
, d
*Data
) {
194 // Get line table from d.
198 ent
, err
:= dr
.Next()
200 t
.Fatal("dr.Next:", err
)
201 } else if ent
== nil {
205 if ent
.Tag
!= TagCompileUnit
{
210 // Decode CU's line table.
211 lr
, err
:= d
.LineReader(ent
)
213 t
.Fatal("d.LineReader:", err
)
214 } else if lr
== nil {
220 err
:= lr
.Next(&line
)
225 t
.Fatal("lr.Next:", err
)
227 // Ignore sources from the Windows build environment.
228 if strings
.HasPrefix(line
.File
.Name
, "C:\\crossdev\\") ||
229 strings
.HasPrefix(line
.File
.Name
, "C:/crossdev/") {
232 got
= append(got
, line
)
236 // Compare line tables.
237 if !compareLines(got
, want
) {
238 t
.Log("Line tables do not match. Got:")
246 func compareLines(a
, b
[]LineEntry
) bool {
247 if len(a
) != len(b
) {
253 // If both are EndSequence, then the only other valid
254 // field is Address. Otherwise, test equality of all
256 if al
.EndSequence
&& bl
.EndSequence
&& al
.Address
== bl
.Address
{
259 if al
.File
.Name
!= bl
.File
.Name
{
271 func dumpLines(t
*testing
.T
, lines
[]LineEntry
) {
272 for _
, l
:= range lines
{
273 t
.Logf(" %+v File:%+v", l
, l
.File
)
277 type joinTest
struct {
278 dirname
, filename
string
282 var joinTests
= []joinTest
{
287 {"/a/", "b", "/a/b"},
289 {`C:\Windows\`, `System32`, `C:\Windows\System32`},
290 {`C:\Windows\`, ``, `C:\Windows\`},
291 {`C:\`, `Windows`, `C:\Windows`},
292 {`C:\Windows\`, `C:System32`, `C:\Windows\System32`},
293 {`C:\Windows`, `a/b`, `C:\Windows\a/b`},
294 {`\\host\share\`, `foo`, `\\host\share\foo`},
295 {`\\host\share\`, `foo\bar`, `\\host\share\foo\bar`},
296 {`//host/share/`, `foo/bar`, `//host/share/foo/bar`},
298 // The following are "best effort". We shouldn't see relative
299 // base directories in DWARF, but these test that pathJoin
300 // doesn't fail miserably if it sees one.
302 {`C:`, `a\b`, `C:a\b`},
303 {`C:.`, `a`, `C:.\a`},
304 {`C:a`, `b`, `C:a\b`},
307 func TestPathJoin(t
*testing
.T
) {
308 for _
, test
:= range joinTests
{
309 got
:= PathJoin(test
.dirname
, test
.filename
)
310 if test
.path
!= got
{
311 t
.Errorf("pathJoin(%q, %q) = %q, want %q", test
.dirname
, test
.filename
, got
, test
.path
)