20acba612f2f015dd23a4aec081b1cf92dc47d2c
[platform/upstream/gcc48.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         "fmt"
10         "io/ioutil"
11         "os"
12         "os/exec"
13         "path/filepath"
14         "runtime"
15         "strings"
16         "testing"
17 )
18
19 var (
20         pclineTempDir    string
21         pclinetestBinary string
22 )
23
24 func dotest() bool {
25         // For now, only works on ELF platforms.
26         if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
27                 return false
28         }
29         if pclinetestBinary != "" {
30                 return true
31         }
32         var err error
33         pclineTempDir, err = ioutil.TempDir("", "pclinetest")
34         if err != nil {
35                 panic(err)
36         }
37         if strings.Contains(pclineTempDir, " ") {
38                 panic("unexpected space in tempdir")
39         }
40         // This command builds pclinetest from pclinetest.asm;
41         // the resulting binary looks like it was built from pclinetest.s,
42         // but we have renamed it to keep it away from the go tool.
43         pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
44         command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
45                 pclinetestBinary, pclinetestBinary, pclinetestBinary)
46         cmd := exec.Command("sh", "-c", command)
47         cmd.Stdout = os.Stdout
48         cmd.Stderr = os.Stderr
49         if err := cmd.Run(); err != nil {
50                 panic(err)
51         }
52         return true
53 }
54
55 func endtest() {
56         if pclineTempDir != "" {
57                 os.RemoveAll(pclineTempDir)
58                 pclineTempDir = ""
59                 pclinetestBinary = ""
60         }
61 }
62
63 func getTable(t *testing.T) *Table {
64         f, tab := crack(os.Args[0], t)
65         f.Close()
66         return tab
67 }
68
69 func crack(file string, t *testing.T) (*elf.File, *Table) {
70         // Open self
71         f, err := elf.Open(file)
72         if err != nil {
73                 t.Fatal(err)
74         }
75         return parse(file, f, t)
76 }
77
78 func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
79         symdat, err := f.Section(".gosymtab").Data()
80         if err != nil {
81                 f.Close()
82                 t.Fatalf("reading %s gosymtab: %v", file, err)
83         }
84         pclndat, err := f.Section(".gopclntab").Data()
85         if err != nil {
86                 f.Close()
87                 t.Fatalf("reading %s gopclntab: %v", file, err)
88         }
89
90         pcln := NewLineTable(pclndat, f.Section(".text").Addr)
91         tab, err := NewTable(symdat, pcln)
92         if err != nil {
93                 f.Close()
94                 t.Fatalf("parsing %s gosymtab: %v", file, err)
95         }
96
97         return f, tab
98 }
99
100 var goarch = os.Getenv("O")
101
102 func TestLineFromAline(t *testing.T) {
103         if !dotest() {
104                 return
105         }
106         defer endtest()
107
108         tab := getTable(t)
109
110         // Find the sym package
111         pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
112         if pkg == nil {
113                 t.Fatalf("nil pkg")
114         }
115
116         // Walk every absolute line and ensure that we hit every
117         // source line monotonically
118         lastline := make(map[string]int)
119         final := -1
120         for i := 0; i < 10000; i++ {
121                 path, line := pkg.lineFromAline(i)
122                 // Check for end of object
123                 if path == "" {
124                         if final == -1 {
125                                 final = i - 1
126                         }
127                         continue
128                 } else if final != -1 {
129                         t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line)
130                 }
131                 // It's okay to see files multiple times (e.g., sys.a)
132                 if line == 1 {
133                         lastline[path] = 1
134                         continue
135                 }
136                 // Check that the is the next line in path
137                 ll, ok := lastline[path]
138                 if !ok {
139                         t.Errorf("file %s starts on line %d", path, line)
140                 } else if line != ll+1 {
141                         t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line)
142                 }
143                 lastline[path] = line
144         }
145         if final == -1 {
146                 t.Errorf("never reached end of object")
147         }
148 }
149
150 func TestLineAline(t *testing.T) {
151         if !dotest() {
152                 return
153         }
154         defer endtest()
155
156         tab := getTable(t)
157
158         for _, o := range tab.Files {
159                 // A source file can appear multiple times in a
160                 // object.  alineFromLine will always return alines in
161                 // the first file, so track which lines we've seen.
162                 found := make(map[string]int)
163                 for i := 0; i < 1000; i++ {
164                         path, line := o.lineFromAline(i)
165                         if path == "" {
166                                 break
167                         }
168
169                         // cgo files are full of 'Z' symbols, which we don't handle
170                         if len(path) > 4 && path[len(path)-4:] == ".cgo" {
171                                 continue
172                         }
173
174                         if minline, ok := found[path]; path != "" && ok {
175                                 if minline >= line {
176                                         // We've already covered this file
177                                         continue
178                                 }
179                         }
180                         found[path] = line
181
182                         a, err := o.alineFromLine(path, line)
183                         if err != nil {
184                                 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)
185                         } else if a != i {
186                                 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)
187                         }
188                 }
189         }
190 }
191
192 func TestPCLine(t *testing.T) {
193         if !dotest() {
194                 return
195         }
196         defer endtest()
197
198         f, tab := crack(pclinetestBinary, t)
199         text := f.Section(".text")
200         textdat, err := text.Data()
201         if err != nil {
202                 t.Fatalf("reading .text: %v", err)
203         }
204
205         // Test PCToLine
206         sym := tab.LookupFunc("linefrompc")
207         wantLine := 0
208         for pc := sym.Entry; pc < sym.End; pc++ {
209                 file, line, fn := tab.PCToLine(pc)
210                 off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
211                 wantLine += int(textdat[off])
212                 t.Logf("off is %d", off)
213                 if fn == nil {
214                         t.Errorf("failed to get line of PC %#x", pc)
215                 } else if !strings.HasSuffix(file, "pclinetest.asm") {
216                         t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name)
217                 } else if line != wantLine || fn != sym {
218                         t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name)
219                 }
220         }
221
222         // Test LineToPC
223         sym = tab.LookupFunc("pcfromline")
224         lookupline := -1
225         wantLine = 0
226         off := uint64(0) // TODO(rsc): should not need off; bug in 8g
227         for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) {
228                 file, line, fn := tab.PCToLine(pc)
229                 off = pc - text.Addr
230                 wantLine += int(textdat[off])
231                 if line != wantLine {
232                         t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line)
233                         off = pc + 1 - text.Addr
234                         continue
235                 }
236                 if lookupline == -1 {
237                         lookupline = line
238                 }
239                 for ; lookupline <= line; lookupline++ {
240                         pc2, fn2, err := tab.LineToPC(file, lookupline)
241                         if lookupline != line {
242                                 // Should be nothing on this line
243                                 if err == nil {
244                                         t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
245                                 }
246                         } else if err != nil {
247                                 t.Errorf("failed to get PC of line %d: %s", lookupline, err)
248                         } else if pc != pc2 {
249                                 t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name)
250                         }
251                 }
252                 off = pc + 1 - text.Addr
253         }
254 }