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