github.com/guyezi/gofrontend@v0.0.0-20200228202240-7a62a49e62c0/libgo/go/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 "internal/testenv" 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(t *testing.T) { 25 testenv.MustHaveGoBuild(t) 26 // For now, only works on amd64 platforms. 27 if runtime.GOARCH != "amd64" { 28 t.Skipf("skipping on non-AMD64 system %s", runtime.GOARCH) 29 } 30 var err error 31 pclineTempDir, err = ioutil.TempDir("", "pclinetest") 32 if err != nil { 33 t.Fatal(err) 34 } 35 pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") 36 cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", pclinetestBinary) 37 cmd.Dir = "testdata" 38 cmd.Env = append(os.Environ(), "GOOS=linux") 39 cmd.Stdout = os.Stdout 40 cmd.Stderr = os.Stderr 41 if err := cmd.Run(); err != nil { 42 t.Fatal(err) 43 } 44 } 45 46 func endtest() { 47 if pclineTempDir != "" { 48 os.RemoveAll(pclineTempDir) 49 pclineTempDir = "" 50 pclinetestBinary = "" 51 } 52 } 53 54 // skipIfNotELF skips the test if we are not running on an ELF system. 55 // These tests open and examine the test binary, and use elf.Open to do so. 56 func skipIfNotELF(t *testing.T) { 57 switch runtime.GOOS { 58 case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos": 59 // OK. 60 default: 61 t.Skipf("skipping on non-ELF system %s", runtime.GOOS) 62 } 63 } 64 65 func getTable(t *testing.T) *Table { 66 f, tab := crack(os.Args[0], t) 67 f.Close() 68 return tab 69 } 70 71 func crack(file string, t *testing.T) (*elf.File, *Table) { 72 // Open self 73 f, err := elf.Open(file) 74 if err != nil { 75 t.Fatal(err) 76 } 77 return parse(file, f, t) 78 } 79 80 func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { 81 s := f.Section(".gosymtab") 82 if s == nil { 83 t.Skip("no .gosymtab section") 84 } 85 symdat, err := s.Data() 86 if err != nil { 87 f.Close() 88 t.Fatalf("reading %s gosymtab: %v", file, err) 89 } 90 pclndat, err := f.Section(".gopclntab").Data() 91 if err != nil { 92 f.Close() 93 t.Fatalf("reading %s gopclntab: %v", file, err) 94 } 95 96 pcln := NewLineTable(pclndat, f.Section(".text").Addr) 97 tab, err := NewTable(symdat, pcln) 98 if err != nil { 99 f.Close() 100 t.Fatalf("parsing %s gosymtab: %v", file, err) 101 } 102 103 return f, tab 104 } 105 106 func TestLineFromAline(t *testing.T) { 107 skipIfNotELF(t) 108 109 tab := getTable(t) 110 if tab.go12line != nil { 111 // aline's don't exist in the Go 1.2 table. 112 t.Skip("not relevant to Go 1.2 symbol table") 113 } 114 115 // Find the sym package 116 pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj 117 if pkg == nil { 118 t.Fatalf("nil pkg") 119 } 120 121 // Walk every absolute line and ensure that we hit every 122 // source line monotonically 123 lastline := make(map[string]int) 124 final := -1 125 for i := 0; i < 10000; i++ { 126 path, line := pkg.lineFromAline(i) 127 // Check for end of object 128 if path == "" { 129 if final == -1 { 130 final = i - 1 131 } 132 continue 133 } else if final != -1 { 134 t.Fatalf("reached end of package at absolute line %d, but absolute line %d mapped to %s:%d", final, i, path, line) 135 } 136 // It's okay to see files multiple times (e.g., sys.a) 137 if line == 1 { 138 lastline[path] = 1 139 continue 140 } 141 // Check that the is the next line in path 142 ll, ok := lastline[path] 143 if !ok { 144 t.Errorf("file %s starts on line %d", path, line) 145 } else if line != ll+1 { 146 t.Fatalf("expected next line of file %s to be %d, got %d", path, ll+1, line) 147 } 148 lastline[path] = line 149 } 150 if final == -1 { 151 t.Errorf("never reached end of object") 152 } 153 } 154 155 func TestLineAline(t *testing.T) { 156 skipIfNotELF(t) 157 158 tab := getTable(t) 159 if tab.go12line != nil { 160 // aline's don't exist in the Go 1.2 table. 161 t.Skip("not relevant to Go 1.2 symbol table") 162 } 163 164 for _, o := range tab.Files { 165 // A source file can appear multiple times in a 166 // object. alineFromLine will always return alines in 167 // the first file, so track which lines we've seen. 168 found := make(map[string]int) 169 for i := 0; i < 1000; i++ { 170 path, line := o.lineFromAline(i) 171 if path == "" { 172 break 173 } 174 175 // cgo files are full of 'Z' symbols, which we don't handle 176 if len(path) > 4 && path[len(path)-4:] == ".cgo" { 177 continue 178 } 179 180 if minline, ok := found[path]; path != "" && ok { 181 if minline >= line { 182 // We've already covered this file 183 continue 184 } 185 } 186 found[path] = line 187 188 a, err := o.alineFromLine(path, line) 189 if err != nil { 190 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) 191 } else if a != i { 192 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) 193 } 194 } 195 } 196 } 197 198 func TestPCLine(t *testing.T) { 199 if testing.Short() { 200 t.Skip("skipping in -short mode") 201 } 202 dotest(t) 203 defer endtest() 204 205 f, tab := crack(pclinetestBinary, t) 206 defer f.Close() 207 text := f.Section(".text") 208 textdat, err := text.Data() 209 if err != nil { 210 t.Fatalf("reading .text: %v", err) 211 } 212 213 // Test PCToLine 214 sym := tab.LookupFunc("main.linefrompc") 215 wantLine := 0 216 for pc := sym.Entry; pc < sym.End; pc++ { 217 off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g 218 if textdat[off] == 255 { 219 break 220 } 221 wantLine += int(textdat[off]) 222 t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) 223 file, line, fn := tab.PCToLine(pc) 224 if fn == nil { 225 t.Errorf("failed to get line of PC %#x", pc) 226 } else if !strings.HasSuffix(file, "pclinetest.s") || line != wantLine || fn != sym { 227 t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.s", wantLine, sym.Name) 228 } 229 } 230 231 // Test LineToPC 232 sym = tab.LookupFunc("main.pcfromline") 233 lookupline := -1 234 wantLine = 0 235 off := uint64(0) // TODO(rsc): should not need off; bug in 8g 236 for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { 237 file, line, fn := tab.PCToLine(pc) 238 off = pc - text.Addr 239 if textdat[off] == 255 { 240 break 241 } 242 wantLine += int(textdat[off]) 243 if line != wantLine { 244 t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) 245 off = pc + 1 - text.Addr 246 continue 247 } 248 if lookupline == -1 { 249 lookupline = line 250 } 251 for ; lookupline <= line; lookupline++ { 252 pc2, fn2, err := tab.LineToPC(file, lookupline) 253 if lookupline != line { 254 // Should be nothing on this line 255 if err == nil { 256 t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name) 257 } 258 } else if err != nil { 259 t.Errorf("failed to get PC of line %d: %s", lookupline, err) 260 } else if pc != pc2 { 261 t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name) 262 } 263 } 264 off = pc + 1 - text.Addr 265 } 266 }