PR target/66870
[official-gcc.git] / libgo / go / debug / gosym / pclntab_test.go
blob35502e8c39551b835a755a8454aecf228e495405
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.
5 package gosym
7 import (
8 "debug/elf"
9 "fmt"
10 "io/ioutil"
11 "os"
12 "os/exec"
13 "path/filepath"
14 "runtime"
15 "strings"
16 "testing"
19 var (
20 pclineTempDir string
21 pclinetestBinary string
24 func dotest(self bool) bool {
25 // For now, only works on amd64 platforms.
26 if runtime.GOARCH != "amd64" {
27 return false
29 // Self test reads test binary; only works on Linux.
30 if self && runtime.GOOS != "linux" {
31 return false
33 // Command below expects "sh", so Unix.
34 if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
35 return false
37 if pclinetestBinary != "" {
38 return true
40 var err error
41 pclineTempDir, err = ioutil.TempDir("", "pclinetest")
42 if err != nil {
43 panic(err)
45 if strings.Contains(pclineTempDir, " ") {
46 panic("unexpected space in tempdir")
48 // This command builds pclinetest from pclinetest.asm;
49 // the resulting binary looks like it was built from pclinetest.s,
50 // but we have renamed it to keep it away from the go tool.
51 pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
52 command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
53 pclinetestBinary, pclinetestBinary, pclinetestBinary)
54 cmd := exec.Command("sh", "-c", command)
55 cmd.Stdout = os.Stdout
56 cmd.Stderr = os.Stderr
57 if err := cmd.Run(); err != nil {
58 panic(err)
60 return true
63 func endtest() {
64 if pclineTempDir != "" {
65 os.RemoveAll(pclineTempDir)
66 pclineTempDir = ""
67 pclinetestBinary = ""
71 func getTable(t *testing.T) *Table {
72 f, tab := crack(os.Args[0], t)
73 f.Close()
74 return tab
77 func crack(file string, t *testing.T) (*elf.File, *Table) {
78 // Open self
79 f, err := elf.Open(file)
80 if err != nil {
81 t.Fatal(err)
83 return parse(file, f, t)
86 func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
87 symdat, err := f.Section(".gosymtab").Data()
88 if err != nil {
89 f.Close()
90 t.Fatalf("reading %s gosymtab: %v", file, err)
92 pclndat, err := f.Section(".gopclntab").Data()
93 if err != nil {
94 f.Close()
95 t.Fatalf("reading %s gopclntab: %v", file, err)
98 pcln := NewLineTable(pclndat, f.Section(".text").Addr)
99 tab, err := NewTable(symdat, pcln)
100 if err != nil {
101 f.Close()
102 t.Fatalf("parsing %s gosymtab: %v", file, err)
105 return f, tab
108 var goarch = os.Getenv("O")
110 func TestLineFromAline(t *testing.T) {
111 if !dotest(true) {
112 return
114 defer endtest()
116 tab := getTable(t)
117 if tab.go12line != nil {
118 // aline's don't exist in the Go 1.2 table.
119 t.Skip("not relevant to Go 1.2 symbol table")
122 // Find the sym package
123 pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
124 if pkg == nil {
125 t.Fatalf("nil pkg")
128 // Walk every absolute line and ensure that we hit every
129 // source line monotonically
130 lastline := make(map[string]int)
131 final := -1
132 for i := 0; i < 10000; i++ {
133 path, line := pkg.lineFromAline(i)
134 // Check for end of object
135 if path == "" {
136 if final == -1 {
137 final = i - 1
139 continue
140 } else if final != -1 {
141 t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
143 // It's okay to see files multiple times (e.g., sys.a)
144 if line == 1 {
145 lastline[path] = 1
146 continue
148 // Check that the is the next line in path
149 ll, ok := lastline[path]
150 if !ok {
151 t.Errorf("file %s starts on line %d", path, line)
152 } else if line != ll+1 {
153 t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
155 lastline[path] = line
157 if final == -1 {
158 t.Errorf("never reached end of object")
162 func TestLineAline(t *testing.T) {
163 if !dotest(true) {
164 return
166 defer endtest()
168 tab := getTable(t)
169 if tab.go12line != nil {
170 // aline's don't exist in the Go 1.2 table.
171 t.Skip("not relevant to Go 1.2 symbol table")
174 for _, o := range tab.Files {
175 // A source file can appear multiple times in a
176 // object. alineFromLine will always return alines in
177 // the first file, so track which lines we've seen.
178 found := make(map[string]int)
179 for i := 0; i < 1000; i++ {
180 path, line := o.lineFromAline(i)
181 if path == "" {
182 break
185 // cgo files are full of 'Z' symbols, which we don't handle
186 if len(path) > 4 && path[len(path)-4:] == ".cgo" {
187 continue
190 if minline, ok := found[path]; path != "" && ok {
191 if minline >= line {
192 // We've already covered this file
193 continue
196 found[path] = line
198 a, err := o.alineFromLine(path, line)
199 if err != nil {
200 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)
201 } else if a != i {
202 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)
208 func TestPCLine(t *testing.T) {
209 if !dotest(false) {
210 return
212 defer endtest()
214 f, tab := crack(pclinetestBinary, t)
215 text := f.Section(".text")
216 textdat, err := text.Data()
217 if err != nil {
218 t.Fatalf("reading .text: %v", err)
221 // Test PCToLine
222 sym := tab.LookupFunc("linefrompc")
223 wantLine := 0
224 for pc := sym.Entry; pc < sym.End; pc++ {
225 off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
226 if textdat[off] == 255 {
227 break
229 wantLine += int(textdat[off])
230 t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc)
231 file, line, fn := tab.PCToLine(pc)
232 if fn == nil {
233 t.Errorf("failed to get line of PC %#x", pc)
234 } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym {
235 t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name)
239 // Test LineToPC
240 sym = tab.LookupFunc("pcfromline")
241 lookupline := -1
242 wantLine = 0
243 off := uint64(0) // TODO(rsc): should not need off; bug in 8g
244 for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
245 file, line, fn := tab.PCToLine(pc)
246 off = pc - text.Addr
247 if textdat[off] == 255 {
248 break
250 wantLine += int(textdat[off])
251 if line != wantLine {
252 t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
253 off = pc + 1 - text.Addr
254 continue
256 if lookupline == -1 {
257 lookupline = line
259 for ; lookupline <= line; lookupline++ {
260 pc2, fn2, err := tab.LineToPC(file, lookupline)
261 if lookupline != line {
262 // Should be nothing on this line
263 if err == nil {
264 t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
266 } else if err != nil {
267 t.Errorf("failed to get PC of line %d: %s", lookupline, err)
268 } else if pc != pc2 {
269 t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
272 off = pc + 1 - text.Addr