1 // Copyright 2017 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 main 6 7 import ( 8 "cmd/internal/objfile" 9 "debug/dwarf" 10 "internal/testenv" 11 "io" 12 "io/ioutil" 13 "os" 14 "os/exec" 15 "path" 16 "path/filepath" 17 "runtime" 18 "strings" 19 "testing" 20 ) 21 22 func TestDWARF(t *testing.T) { 23 testenv.MustHaveCGO(t) 24 testenv.MustHaveGoBuild(t) 25 26 if runtime.GOOS == "plan9" { 27 t.Skip("skipping on plan9; no DWARF symbol table in executables") 28 } 29 30 out, err := exec.Command(testenv.GoToolPath(t), "list", "-f", "{{.Stale}}", "cmd/link").CombinedOutput() 31 if err != nil { 32 t.Fatalf("go list: %v\n%s", err, out) 33 } 34 if string(out) != "false\n" { 35 if os.Getenv("GOROOT_FINAL_OLD") != "" { 36 t.Skip("cmd/link is stale, but $GOROOT_FINAL_OLD is set") 37 } 38 t.Fatalf("cmd/link is stale - run go install cmd/link") 39 } 40 41 tmpDir, err := ioutil.TempDir("", "go-link-TestDWARF") 42 if err != nil { 43 t.Fatal("TempDir failed: ", err) 44 } 45 defer os.RemoveAll(tmpDir) 46 47 for _, prog := range []string{"testprog", "testprogcgo"} { 48 t.Run(prog, func(t *testing.T) { 49 exe := filepath.Join(tmpDir, prog+".exe") 50 dir := "../../runtime/testdata/" + prog 51 out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", exe, dir).CombinedOutput() 52 if err != nil { 53 t.Fatalf("go build -o %v %v: %v\n%s", exe, dir, err, out) 54 } 55 56 f, err := objfile.Open(exe) 57 if err != nil { 58 t.Fatal(err) 59 } 60 defer f.Close() 61 62 syms, err := f.Symbols() 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 var addr uint64 68 for _, sym := range syms { 69 if sym.Name == "main.main" { 70 addr = sym.Addr 71 break 72 } 73 } 74 if addr == 0 { 75 t.Fatal("cannot find main.main in symbols") 76 } 77 78 d, err := f.DWARF() 79 if err != nil { 80 t.Fatal(err) 81 } 82 83 // TODO: We'd like to use filepath.Join here. 84 // Also related: golang.org/issue/19784. 85 wantFile := path.Join(prog, "main.go") 86 wantLine := 24 87 r := d.Reader() 88 var line dwarf.LineEntry 89 for { 90 cu, err := r.Next() 91 if err != nil { 92 t.Fatal(err) 93 } 94 if cu == nil { 95 break 96 } 97 if cu.Tag != dwarf.TagCompileUnit { 98 r.SkipChildren() 99 continue 100 } 101 lr, err := d.LineReader(cu) 102 if err != nil { 103 t.Fatal(err) 104 } 105 for { 106 err := lr.Next(&line) 107 if err == io.EOF { 108 break 109 } 110 if err != nil { 111 t.Fatal(err) 112 } 113 if line.Address == addr { 114 if !strings.HasSuffix(line.File.Name, wantFile) || line.Line != wantLine { 115 t.Errorf("%#x is %s:%d, want %s:%d", addr, line.File.Name, line.Line, filepath.Join("...", wantFile), wantLine) 116 } 117 return 118 } 119 } 120 } 121 t.Fatalf("did not find file:line for %#x (main.main)", addr) 122 }) 123 } 124 } 125