1 // Copyright 2009 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.
22 pclinetestBinary
string
25 func dotest(t
*testing
.T
) {
26 testenv
.MustHaveGoBuild(t
)
27 // For now, only works on amd64 platforms.
28 if runtime
.GOARCH
!= "amd64" {
29 t
.Skipf("skipping on non-AMD64 system %s", runtime
.GOARCH
)
32 pclineTempDir
, err
= ioutil
.TempDir("", "pclinetest")
36 // This command builds pclinetest from pclinetest.asm;
37 // the resulting binary looks like it was built from pclinetest.s,
38 // but we have renamed it to keep it away from the go tool.
39 pclinetestBinary
= filepath
.Join(pclineTempDir
, "pclinetest")
40 cmd
:= exec
.Command(testenv
.GoToolPath(t
), "tool", "asm", "-o", pclinetestBinary
+".o", "pclinetest.asm")
41 cmd
.Stdout
= os
.Stdout
42 cmd
.Stderr
= os
.Stderr
43 if err
:= cmd
.Run(); err
!= nil {
47 // stamp .o file as being 'package main' so that go tool link will accept it
48 data
, err
:= ioutil
.ReadFile(pclinetestBinary
+ ".o")
52 i
:= bytes
.IndexByte(data
, '\n')
56 data
= append(append(data
[:i
:i
], "\nmain"...), data
[i
:]...)
57 if err
:= ioutil
.WriteFile(pclinetestBinary
+".o", data
, 0666); err
!= nil {
61 cmd
= exec
.Command(testenv
.GoToolPath(t
), "tool", "link", "-H", "linux",
62 "-o", pclinetestBinary
, pclinetestBinary
+".o")
63 cmd
.Stdout
= os
.Stdout
64 cmd
.Stderr
= os
.Stderr
65 if err
:= cmd
.Run(); err
!= nil {
71 if pclineTempDir
!= "" {
72 os
.RemoveAll(pclineTempDir
)
78 // skipIfNotELF skips the test if we are not running on an ELF system.
79 // These tests open and examine the test binary, and use elf.Open to do so.
80 func skipIfNotELF(t
*testing
.T
) {
82 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
85 t
.Skipf("skipping on non-ELF system %s", runtime
.GOOS
)
89 func getTable(t
*testing
.T
) *Table
{
90 f
, tab
:= crack(os
.Args
[0], t
)
95 func crack(file
string, t
*testing
.T
) (*elf
.File
, *Table
) {
97 f
, err
:= elf
.Open(file
)
101 return parse(file
, f
, t
)
104 func parse(file
string, f
*elf
.File
, t
*testing
.T
) (*elf
.File
, *Table
) {
105 s
:= f
.Section(".gosymtab")
107 t
.Skip("no .gosymtab section")
109 symdat
, err
:= s
.Data()
112 t
.Fatalf("reading %s gosymtab: %v", file
, err
)
114 pclndat
, err
:= f
.Section(".gopclntab").Data()
117 t
.Fatalf("reading %s gopclntab: %v", file
, err
)
120 pcln
:= NewLineTable(pclndat
, f
.Section(".text").Addr
)
121 tab
, err
:= NewTable(symdat
, pcln
)
124 t
.Fatalf("parsing %s gosymtab: %v", file
, err
)
130 func TestLineFromAline(t
*testing
.T
) {
134 if tab
.go12line
!= nil {
135 // aline's don't exist in the Go 1.2 table.
136 t
.Skip("not relevant to Go 1.2 symbol table")
139 // Find the sym package
140 pkg
:= tab
.LookupFunc("debug/gosym.TestLineFromAline").Obj
145 // Walk every absolute line and ensure that we hit every
146 // source line monotonically
147 lastline
:= make(map[string]int)
149 for i
:= 0; i
< 10000; i
++ {
150 path
, line
:= pkg
.lineFromAline(i
)
151 // Check for end of object
157 } else if final
!= -1 {
158 t
.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final
, i
, path
, line
)
160 // It's okay to see files multiple times (e.g., sys.a)
165 // Check that the is the next line in path
166 ll
, ok
:= lastline
[path
]
168 t
.Errorf("file %s starts on line %d", path
, line
)
169 } else if line
!= ll
+1 {
170 t
.Fatalf("expected next line of file %s to be %d, got %d", path
, ll
+1, line
)
172 lastline
[path
] = line
175 t
.Errorf("never reached end of object")
179 func TestLineAline(t
*testing
.T
) {
183 if tab
.go12line
!= nil {
184 // aline's don't exist in the Go 1.2 table.
185 t
.Skip("not relevant to Go 1.2 symbol table")
188 for _
, o
:= range tab
.Files
{
189 // A source file can appear multiple times in a
190 // object. alineFromLine will always return alines in
191 // the first file, so track which lines we've seen.
192 found
:= make(map[string]int)
193 for i
:= 0; i
< 1000; i
++ {
194 path
, line
:= o
.lineFromAline(i
)
199 // cgo files are full of 'Z' symbols, which we don't handle
200 if len(path
) > 4 && path
[len(path
)-4:] == ".cgo" {
204 if minline
, ok
:= found
[path
]; path
!= "" && ok
{
206 // We've already covered this file
212 a
, err
:= o
.alineFromLine(path
, line
)
214 t
.Errorf("absolute line %d in object %s maps to %s:%d, but mapping that back gives error %s", i
, o
.Paths
[0].Name
, path
, line
, err
)
216 t
.Errorf("absolute line %d in object %s maps to %s:%d, which maps back to absolute line %d\n", i
, o
.Paths
[0].Name
, path
, line
, a
)
222 func TestPCLine(t
*testing
.T
) {
226 f
, tab
:= crack(pclinetestBinary
, t
)
228 text
:= f
.Section(".text")
229 textdat
, err
:= text
.Data()
231 t
.Fatalf("reading .text: %v", err
)
235 sym
:= tab
.LookupFunc("linefrompc")
237 for pc
:= sym
.Entry
; pc
< sym
.End
; pc
++ {
238 off
:= pc
- text
.Addr
// TODO(rsc): should not need off; bug in 8g
239 if textdat
[off
] == 255 {
242 wantLine
+= int(textdat
[off
])
243 t
.Logf("off is %d %#x (max %d)", off
, textdat
[off
], sym
.End
-pc
)
244 file
, line
, fn
:= tab
.PCToLine(pc
)
246 t
.Errorf("failed to get line of PC %#x", pc
)
247 } else if !strings
.HasSuffix(file
, "pclinetest.asm") || line
!= wantLine || fn
!= sym
{
248 t
.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc
, file
, line
, fn
.Name
, "pclinetest.asm", wantLine
, sym
.Name
)
253 sym
= tab
.LookupFunc("pcfromline")
256 off
:= uint64(0) // TODO(rsc): should not need off; bug in 8g
257 for pc
:= sym
.Value
; pc
< sym
.End
; pc
+= 2 + uint64(textdat
[off
]) {
258 file
, line
, fn
:= tab
.PCToLine(pc
)
260 if textdat
[off
] == 255 {
263 wantLine
+= int(textdat
[off
])
264 if line
!= wantLine
{
265 t
.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine
, pc
, line
)
266 off
= pc
+ 1 - text
.Addr
269 if lookupline
== -1 {
272 for ; lookupline
<= line
; lookupline
++ {
273 pc2
, fn2
, err
:= tab
.LineToPC(file
, lookupline
)
274 if lookupline
!= line
{
275 // Should be nothing on this line
277 t
.Errorf("expected no PC at line %d, got %#x (%s)", lookupline
, pc2
, fn2
.Name
)
279 } else if err
!= nil {
280 t
.Errorf("failed to get PC of line %d: %s", lookupline
, err
)
281 } else if pc
!= pc2
{
282 t
.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc
, fn
.Name
, line
, pc2
, fn2
.Name
)
285 off
= pc
+ 1 - text
.Addr