1
2
3
4
5 package ssa_test
6
7 import (
8 cmddwarf "cmd/internal/dwarf"
9 "cmd/internal/quoted"
10 "debug/dwarf"
11 "debug/elf"
12 "debug/macho"
13 "debug/pe"
14 "fmt"
15 "internal/platform"
16 "internal/testenv"
17 "internal/xcoff"
18 "io"
19 "os"
20 "runtime"
21 "sort"
22 "testing"
23 )
24
25 func open(path string) (*dwarf.Data, error) {
26 if fh, err := elf.Open(path); err == nil {
27 return fh.DWARF()
28 }
29
30 if fh, err := pe.Open(path); err == nil {
31 return fh.DWARF()
32 }
33
34 if fh, err := macho.Open(path); err == nil {
35 return fh.DWARF()
36 }
37
38 if fh, err := xcoff.Open(path); err == nil {
39 return fh.DWARF()
40 }
41
42 return nil, fmt.Errorf("unrecognized executable format")
43 }
44
45 func must(err error) {
46 if err != nil {
47 panic(err)
48 }
49 }
50
51 type Line struct {
52 File string
53 Line int
54 }
55
56 func TestStmtLines(t *testing.T) {
57 if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
58 t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
59 }
60
61 if runtime.GOOS == "aix" {
62 extld := os.Getenv("CC")
63 if extld == "" {
64 extld = "gcc"
65 }
66 extldArgs, err := quoted.Split(extld)
67 if err != nil {
68 t.Fatal(err)
69 }
70 enabled, err := cmddwarf.IsDWARFEnabledOnAIXLd(extldArgs)
71 if err != nil {
72 t.Fatal(err)
73 }
74 if !enabled {
75 t.Skip("skipping on aix: no DWARF with ld version < 7.2.2 ")
76 }
77 }
78
79
80 dir := t.TempDir()
81 out, err := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags=-w=0", "-o", dir+"/test.exe", "cmd/go").CombinedOutput()
82 if err != nil {
83 t.Fatalf("go build: %v\n%s", err, out)
84 }
85
86 lines := map[Line]bool{}
87 dw, err := open(dir + "/test.exe")
88 must(err)
89 rdr := dw.Reader()
90 rdr.Seek(0)
91 for {
92 e, err := rdr.Next()
93 must(err)
94 if e == nil {
95 break
96 }
97 if e.Tag != dwarf.TagCompileUnit {
98 continue
99 }
100 pkgname, _ := e.Val(dwarf.AttrName).(string)
101 if pkgname == "runtime" {
102 continue
103 }
104 if pkgname == "crypto/internal/nistec/fiat" {
105 continue
106 }
107 if e.Val(dwarf.AttrStmtList) == nil {
108 continue
109 }
110 lrdr, err := dw.LineReader(e)
111 must(err)
112
113 var le dwarf.LineEntry
114
115 for {
116 err := lrdr.Next(&le)
117 if err == io.EOF {
118 break
119 }
120 must(err)
121 fl := Line{le.File.Name, le.Line}
122 lines[fl] = lines[fl] || le.IsStmt
123 }
124 }
125
126 nonStmtLines := []Line{}
127 for line, isstmt := range lines {
128 if !isstmt {
129 nonStmtLines = append(nonStmtLines, line)
130 }
131 }
132
133 var m int
134 if runtime.GOARCH == "amd64" {
135 m = 1
136 } else if runtime.GOARCH == "riscv64" {
137 m = 3
138 } else {
139 m = 2
140 }
141
142 if len(nonStmtLines)*100 > m*len(lines) {
143 t.Errorf("Saw too many (%s, > %d%%) lines without statement marks, total=%d, nostmt=%d ('-run TestStmtLines -v' lists failing lines)\n", runtime.GOARCH, m, len(lines), len(nonStmtLines))
144 }
145 t.Logf("Saw %d out of %d lines without statement marks", len(nonStmtLines), len(lines))
146 if testing.Verbose() {
147 sort.Slice(nonStmtLines, func(i, j int) bool {
148 if nonStmtLines[i].File != nonStmtLines[j].File {
149 return nonStmtLines[i].File < nonStmtLines[j].File
150 }
151 return nonStmtLines[i].Line < nonStmtLines[j].Line
152 })
153 for _, l := range nonStmtLines {
154 t.Logf("%s:%d has no DWARF is_stmt mark\n", l.File, l.Line)
155 }
156 }
157 t.Logf("total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
158 }
159
View as plain text