github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/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": 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 dotest(t) 200 defer endtest() 201 202 f, tab := crack(pclinetestBinary, t) 203 defer f.Close() 204 text := f.Section(".text") 205 textdat, err := text.Data() 206 if err != nil { 207 t.Fatalf("reading .text: %v", err) 208 } 209 210 // Test PCToLine 211 sym := tab.LookupFunc("main.linefrompc") 212 wantLine := 0 213 for pc := sym.Entry; pc < sym.End; pc++ { 214 off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g 215 if textdat[off] == 255 { 216 break 217 } 218 wantLine += int(textdat[off]) 219 t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) 220 file, line, fn := tab.PCToLine(pc) 221 if fn == nil { 222 t.Errorf("failed to get line of PC %#x", pc) 223 } else if !strings.HasSuffix(file, "pclinetest.s") || line != wantLine || fn != sym { 224 t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.s", wantLine, sym.Name) 225 } 226 } 227 228 // Test LineToPC 229 sym = tab.LookupFunc("main.pcfromline") 230 lookupline := -1 231 wantLine = 0 232 off := uint64(0) // TODO(rsc): should not need off; bug in 8g 233 for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { 234 file, line, fn := tab.PCToLine(pc) 235 off = pc - text.Addr 236 if textdat[off] == 255 { 237 break 238 } 239 wantLine += int(textdat[off]) 240 if line != wantLine { 241 t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) 242 off = pc + 1 - text.Addr 243 continue 244 } 245 if lookupline == -1 { 246 lookupline = line 247 } 248 for ; lookupline <= line; lookupline++ { 249 pc2, fn2, err := tab.LineToPC(file, lookupline) 250 if lookupline != line { 251 // Should be nothing on this line 252 if err == nil { 253 t.Errorf("expected no PC at line %d, got %#x (%s)", lookupline, pc2, fn2.Name) 254 } 255 } else if err != nil { 256 t.Errorf("failed to get PC of line %d: %s", lookupline, err) 257 } else if pc != pc2 { 258 t.Errorf("expected PC %#x (%s) at line %d, got PC %#x (%s)", pc, fn.Name, line, pc2, fn2.Name) 259 } 260 } 261 off = pc + 1 - text.Addr 262 } 263 }