]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/debug/gosym/pclntab_test.go
Add Go frontend, libgo library, and Go testsuite.
[thirdparty/gcc.git] / libgo / go / debug / gosym / pclntab_test.go
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.
4
5 package gosym
6
7 import (
8 "debug/elf"
9 "os"
10 "testing"
11 "syscall"
12 )
13
14 func dotest() bool {
15 // For now, only works on ELF platforms.
16 return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
17 }
18
19 func getTable(t *testing.T) *Table {
20 f, tab := crack(os.Args[0], t)
21 f.Close()
22 return tab
23 }
24
25 func crack(file string, t *testing.T) (*elf.File, *Table) {
26 // Open self
27 f, err := elf.Open(file)
28 if err != nil {
29 t.Fatal(err)
30 }
31 return parse(file, f, t)
32 }
33
34 func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
35 symdat, err := f.Section(".gosymtab").Data()
36 if err != nil {
37 f.Close()
38 t.Fatalf("reading %s gosymtab: %v", file, err)
39 }
40 pclndat, err := f.Section(".gopclntab").Data()
41 if err != nil {
42 f.Close()
43 t.Fatalf("reading %s gopclntab: %v", file, err)
44 }
45
46 pcln := NewLineTable(pclndat, f.Section(".text").Addr)
47 tab, err := NewTable(symdat, pcln)
48 if err != nil {
49 f.Close()
50 t.Fatalf("parsing %s gosymtab: %v", file, err)
51 }
52
53 return f, tab
54 }
55
56 var goarch = os.Getenv("O")
57
58 func TestLineFromAline(t *testing.T) {
59 if !dotest() {
60 return
61 }
62
63 tab := getTable(t)
64
65 // Find the sym package
66 pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
67 if pkg == nil {
68 t.Fatalf("nil pkg")
69 }
70
71 // Walk every absolute line and ensure that we hit every
72 // source line monotonically
73 lastline := make(map[string]int)
74 final := -1
75 for i := 0; i < 10000; i++ {
76 path, line := pkg.lineFromAline(i)
77 // Check for end of object
78 if path == "" {
79 if final == -1 {
80 final = i - 1
81 }
82 continue
83 } else if final != -1 {
84 t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
85 }
86 // It's okay to see files multiple times (e.g., sys.a)
87 if line == 1 {
88 lastline[path] = 1
89 continue
90 }
91 // Check that the is the next line in path
92 ll, ok := lastline[path]
93 if !ok {
94 t.Errorf("file %s starts on line %d", path, line)
95 } else if line != ll+1 {
96 t.Errorf("expected next line of file %s to be %d, got %d", path, ll+1, line)
97 }
98 lastline[path] = line
99 }
100 if final == -1 {
101 t.Errorf("never reached end of object")
102 }
103 }
104
105 func TestLineAline(t *testing.T) {
106 if !dotest() {
107 return
108 }
109
110 tab := getTable(t)
111
112 for _, o := range tab.Files {
113 // A source file can appear multiple times in a
114 // object. alineFromLine will always return alines in
115 // the first file, so track which lines we've seen.
116 found := make(map[string]int)
117 for i := 0; i < 1000; i++ {
118 path, line := o.lineFromAline(i)
119 if path == "" {
120 break
121 }
122
123 // cgo files are full of 'Z' symbols, which we don't handle
124 if len(path) > 4 && path[len(path)-4:] == ".cgo" {
125 continue
126 }
127
128 if minline, ok := found[path]; path != "" && ok {
129 if minline >= line {
130 // We've already covered this file
131 continue
132 }
133 }
134 found[path] = line
135
136 a, err := o.alineFromLine(path, line)
137 if err != nil {
138 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)
139 } else if a != i {
140 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)
141 }
142 }
143 }
144 }
145
146 // gotest: if [ "$(uname)-$(uname -m)" = Linux-x86_64 -a "$GOARCH" = amd64 ]; then
147 // gotest: mkdir -p _test && $AS pclinetest.s && $LD -E main -o _test/pclinetest pclinetest.$O
148 // gotest: fi
149 func TestPCLine(t *testing.T) {
150 if !dotest() {
151 return
152 }
153
154 f, tab := crack("_test/pclinetest", t)
155 text := f.Section(".text")
156 textdat, err := text.Data()
157 if err != nil {
158 t.Fatalf("reading .text: %v", err)
159 }
160
161 // Test PCToLine
162 sym := tab.LookupFunc("linefrompc")
163 wantLine := 0
164 for pc := sym.Entry; pc < sym.End; pc++ {
165 file, line, fn := tab.PCToLine(pc)
166 off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
167 wantLine += int(textdat[off])
168 if fn == nil {
169 t.Errorf("failed to get line of PC %#x", pc)
170 } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym {
171 t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name)
172 }
173 }
174
175 // Test LineToPC
176 sym = tab.LookupFunc("pcfromline")
177 lookupline := -1
178 wantLine = 0
179 off := uint64(0) // TODO(rsc): should not need off; bug in 8g
180 for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
181 file, line, fn := tab.PCToLine(pc)
182 off = pc - text.Addr
183 wantLine += int(textdat[off])
184 if line != wantLine {
185 t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
186 off = pc + 1 - text.Addr
187 continue
188 }
189 if lookupline == -1 {
190 lookupline = line
191 }
192 for ; lookupline <= line; lookupline++ {
193 pc2, fn2, err := tab.LineToPC(file, lookupline)
194 if lookupline != line {
195 // Should be nothing on this line
196 if err == nil {
197 t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
198 }
199 } else if err != nil {
200 t.Errorf("failed to get PC of line %d: %s", lookupline, err)
201 } else if pc != pc2 {
202 t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
203 }
204 }
205 off = pc + 1 - text.Addr
206 }
207 }