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.
21 pclinetestBinary string
25 // For now, only works on ELF platforms.
26 if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
29 if pclinetestBinary != "" {
33 pclineTempDir, err = ioutil.TempDir("", "pclinetest")
37 if strings.Contains(pclineTempDir, " ") {
38 panic("unexpected space in tempdir")
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 {
56 if pclineTempDir != "" {
57 os.RemoveAll(pclineTempDir)
63 func getTable(t *testing.T) *Table {
64 f, tab := crack(os.Args[0], t)
69 func crack(file string, t *testing.T) (*elf.File, *Table) {
71 f, err := elf.Open(file)
75 return parse(file, f, t)
78 func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) {
79 symdat, err := f.Section(".gosymtab").Data()
82 t.Fatalf("reading %s gosymtab: %v", file, err)
84 pclndat, err := f.Section(".gopclntab").Data()
87 t.Fatalf("reading %s gopclntab: %v", file, err)
90 pcln := NewLineTable(pclndat, f.Section(".text").Addr)
91 tab, err := NewTable(symdat, pcln)
94 t.Fatalf("parsing %s gosymtab: %v", file, err)
100 var goarch = os.Getenv("O")
102 func TestLineFromAline(t *testing.T) {
110 // Find the sym package
111 pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj
116 // Walk every absolute line and ensure that we hit every
117 // source line monotonically
118 lastline := make(map[string]int)
120 for i := 0; i < 10000; i++ {
121 path, line := pkg.lineFromAline(i)
122 // Check for end of object
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)
131 // It's okay to see files multiple times (e.g., sys.a)
136 // Check that the is the next line in path
137 ll, ok := lastline[path]
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)
143 lastline[path] = line
146 t.Errorf("never reached end of object")
150 func TestLineAline(t *testing.T) {
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)
169 // cgo files are full of 'Z' symbols, which we don't handle
170 if len(path) > 4 && path[len(path)-4:] == ".cgo" {
174 if minline, ok := found[path]; path != "" && ok {
176 // We've already covered this file
182 a, err := o.alineFromLine(path, line)
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)
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)
192 func TestPCLine(t *testing.T) {
198 f, tab := crack(pclinetestBinary, t)
199 text := f.Section(".text")
200 textdat, err := text.Data()
202 t.Fatalf("reading .text: %v", err)
206 sym := tab.LookupFunc("linefrompc")
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)
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)
223 sym = tab.LookupFunc("pcfromline")
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)
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
236 if lookupline == -1 {
239 for ; lookupline <= line; lookupline++ {
240 pc2, fn2, err := tab.LineToPC(file, lookupline)
241 if lookupline != line {
242 // Should be nothing on this line
244 t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name)
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)
252 off = pc + 1 - text.Addr