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/nistec/fiat" {
107 continue
108 }
109 if e.Val(dwarf.AttrStmtList) == nil {
110 continue
111 }
112 lrdr, err := dw.LineReader(e)
113 must(err)
114
115 var le dwarf.LineEntry
116
117 for {
118 err := lrdr.Next(&le)
119 if err == io.EOF {
120 break
121 }
122 must(err)
123 fl := Line{le.File.Name, le.Line}
124 lines[fl] = lines[fl] || le.IsStmt
125 }
126 }
127
128 nonStmtLines := []Line{}
129 for line, isstmt := range lines {
130 if !isstmt {
131 nonStmtLines = append(nonStmtLines, line)
132 }
133 }
134
135 var m int
136 if runtime.GOARCH == "amd64" {
137 m = 1
138 } else if runtime.GOARCH == "riscv64" {
139 m = 3
140 } else {
141 m = 2
142 }
143
144 if len(nonStmtLines)*100 > m*len(lines) {
145 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))
146 }
147 t.Logf("Saw %d out of %d lines without statement marks", len(nonStmtLines), len(lines))
148 if testing.Verbose() {
149 slices.SortFunc(nonStmtLines, func(a, b Line) int {
150 if a.File != b.File {
151 return strings.Compare(a.File, b.File)
152 }
153 return cmp.Compare(a.Line, b.Line)
154 })
155 for _, l := range nonStmtLines {
156 t.Logf("%s:%d has no DWARF is_stmt mark\n", l.File, l.Line)
157 }
158 }
159 t.Logf("total=%d, nostmt=%d\n", len(lines), len(nonStmtLines))
160 }
161
View as plain text