github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/debug/gosym/pclntab_test.go (about) 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 }