1
2
3
4
5 package logopt
6
7 import (
8 "internal/testenv"
9 "os"
10 "path/filepath"
11 "runtime"
12 "strings"
13 "testing"
14 )
15
16 const srcCode = `package x
17 type pair struct {a,b int}
18 func bar(y *pair) *int {
19 return &y.b
20 }
21 var a []int
22 func foo(w, z *pair) *int {
23 if *bar(w) > 0 {
24 return bar(z)
25 }
26 if a[1] > 0 {
27 a = a[:2]
28 }
29 return &a[0]
30 }
31
32 // address taking prevents closure inlining
33 func n() int {
34 foo := func() int { return 1 }
35 bar := &foo
36 x := (*bar)() + foo()
37 return x
38 }
39 `
40
41 func want(t *testing.T, out string, desired string) {
42
43
44 s := strings.ReplaceAll(desired, string(os.PathSeparator), "/")
45 if !strings.Contains(out, s) {
46 t.Errorf("did not see phrase %s in \n%s", s, out)
47 }
48 }
49
50 func wantN(t *testing.T, out string, desired string, n int) {
51 if strings.Count(out, desired) != n {
52 t.Errorf("expected exactly %d occurrences of %s in \n%s", n, desired, out)
53 }
54 }
55
56 func TestPathStuff(t *testing.T) {
57 sep := string(filepath.Separator)
58 if path, whine := parseLogPath("file:///c:foo"); path != "c:foo" || whine != "" {
59 t.Errorf("path='%s', whine='%s'", path, whine)
60 }
61 if path, whine := parseLogPath("file:///foo"); path != sep+"foo" || whine != "" {
62 t.Errorf("path='%s', whine='%s'", path, whine)
63 }
64 if path, whine := parseLogPath("foo"); path != "" || whine == "" {
65 t.Errorf("path='%s', whine='%s'", path, whine)
66 }
67 if sep == "\\" {
68 if path, whine := parseLogPath("C:/foo"); path != "C:\\foo" || whine != "" {
69 t.Errorf("path='%s', whine='%s'", path, whine)
70 }
71 if path, whine := parseLogPath("c:foo"); path != "" || whine == "" {
72 t.Errorf("path='%s', whine='%s'", path, whine)
73 }
74 if path, whine := parseLogPath("/foo"); path != "" || whine == "" {
75 t.Errorf("path='%s', whine='%s'", path, whine)
76 }
77 } else {
78 if path, whine := parseLogPath("/foo"); path != sep+"foo" || whine != "" {
79 t.Errorf("path='%s', whine='%s'", path, whine)
80 }
81 }
82 }
83
84 func TestLogOpt(t *testing.T) {
85 t.Parallel()
86
87 testenv.MustHaveGoBuild(t)
88
89 dir := fixSlash(t.TempDir())
90 src := filepath.Join(dir, "file.go")
91 if err := os.WriteFile(src, []byte(srcCode), 0644); err != nil {
92 t.Fatal(err)
93 }
94
95 outfile := filepath.Join(dir, "file.o")
96
97 t.Run("JSON_fails", func(t *testing.T) {
98
99 out, err := testLogOpt(t, "-json=foo", src, outfile)
100 if err == nil {
101 t.Error("-json=foo succeeded unexpectedly")
102 }
103 want(t, out, "option should be")
104 want(t, out, "number")
105
106
107 out, err = testLogOpt(t, "-json=9,foo", src, outfile)
108 if err == nil {
109 t.Error("-json=0,foo succeeded unexpectedly")
110 }
111 want(t, out, "version must be")
112
113 })
114
115
116 normalize := func(out []byte, d, t string) string {
117 s := string(out)
118 s = strings.ReplaceAll(s, d, t)
119 s = strings.ReplaceAll(s, string(os.PathSeparator), "/")
120 return s
121 }
122
123
124
125 t.Run("Copy", func(t *testing.T) {
126 const copyCode = `package x
127 func s128a1(x *[128]int8) [128]int8 {
128 return *x
129 }
130 func s127a1(x *[127]int8) [127]int8 {
131 return *x
132 }
133 func s16a8(x *[16]int64) [16]int64 {
134 return *x
135 }
136 func s15a8(x *[15]int64) [15]int64 {
137 return *x
138 }
139 `
140 copy := filepath.Join(dir, "copy.go")
141 if err := os.WriteFile(copy, []byte(copyCode), 0644); err != nil {
142 t.Fatal(err)
143 }
144 outcopy := filepath.Join(dir, "copy.o")
145
146
147 arches := []string{runtime.GOARCH}
148 goos0 := runtime.GOOS
149 if runtime.GOARCH == "amd64" {
150 arches = []string{"arm", "arm64", "386", "amd64", "mips", "mips64", "loong64", "ppc64le", "riscv64", "s390x", "wasm"}
151 goos0 = "linux"
152 }
153
154 for _, arch := range arches {
155 t.Run(arch, func(t *testing.T) {
156 goos := goos0
157 if arch == "wasm" {
158 goos = "js"
159 }
160 _, err := testCopy(t, dir, arch, goos, copy, outcopy)
161 if err != nil {
162 t.Error("-json=0,file://log/opt should have succeeded")
163 }
164 logged, err := os.ReadFile(filepath.Join(dir, "log", "opt", "x", "copy.json"))
165 if err != nil {
166 t.Error("-json=0,file://log/opt missing expected log file")
167 }
168 slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
169 t.Logf("%s", slogged)
170 want(t, slogged, `{"range":{"start":{"line":3,"character":2},"end":{"line":3,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
171 want(t, slogged, `{"range":{"start":{"line":9,"character":2},"end":{"line":9,"character":2}},"severity":3,"code":"copy","source":"go compiler","message":"128 bytes"}`)
172 wantN(t, slogged, `"code":"copy"`, 2)
173 })
174 }
175 })
176
177
178
179 if runtime.GOARCH != "amd64" {
180 return
181 }
182
183 t.Run("Success", func(t *testing.T) {
184
185
186
187 _, err := testLogOptDir(t, dir, "-json=0,file://log/opt", src, outfile)
188 if err != nil {
189 t.Error("-json=0,file://log/opt should have succeeded")
190 }
191 logged, err := os.ReadFile(filepath.Join(dir, "log", "opt", "x", "file.json"))
192 if err != nil {
193 t.Error("-json=0,file://log/opt missing expected log file")
194 }
195
196 slogged := normalize(logged, string(uriIfy(dir)), string(uriIfy("tmpdir")))
197 t.Logf("%s", slogged)
198
199 want(t, slogged, `{"range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}},"severity":3,"code":"nilcheck","source":"go compiler","message":"",`+
200 `"relatedInformation":[{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"}]}`)
201 want(t, slogged, `{"range":{"start":{"line":11,"character":6},"end":{"line":11,"character":6}},"severity":3,"code":"isInBounds","source":"go compiler","message":""}`)
202 want(t, slogged, `{"range":{"start":{"line":7,"character":6},"end":{"line":7,"character":6}},"severity":3,"code":"canInlineFunction","source":"go compiler","message":"cost: 35"}`)
203
204 want(t, slogged, `{"range":{"start":{"line":7,"character":13},"end":{"line":7,"character":13}},"severity":3,"code":"leak","source":"go compiler","message":"parameter z leaks to ~r0 with derefs=0",`+
205 `"relatedInformation":[`+
206 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: y = z:"},`+
207 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y := z (assign-pair)"},`+
208 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: flow: ~r0 = y:"},`+
209 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
210 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from y.b (dot of pointer)"},`+
211 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":11},"end":{"line":4,"character":11}}},"message":"inlineLoc"},`+
212 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from \u0026y.b (address-of)"},`+
213 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":4,"character":9},"end":{"line":4,"character":9}}},"message":"inlineLoc"},`+
214 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":13},"end":{"line":9,"character":13}}},"message":"escflow: from ~r0 = \u0026y.b (assign-pair)"},`+
215 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: flow: ~r0 = ~r0:"},`+
216 `{"location":{"uri":"file://tmpdir/file.go","range":{"start":{"line":9,"character":3},"end":{"line":9,"character":3}}},"message":"escflow: from return ~r0 (return)"}]}`)
217 })
218 }
219
220 func testLogOpt(t *testing.T, flag, src, outfile string) (string, error) {
221 run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=p", flag, "-o", outfile, src}
222 t.Log(run)
223 cmd := testenv.Command(t, run[0], run[1:]...)
224 out, err := cmd.CombinedOutput()
225 t.Logf("%s", out)
226 return string(out), err
227 }
228
229 func testLogOptDir(t *testing.T, dir, flag, src, outfile string) (string, error) {
230
231 run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=x", flag, "-o", outfile, src}
232 t.Log(run)
233 cmd := testenv.Command(t, run[0], run[1:]...)
234 cmd.Dir = dir
235 out, err := cmd.CombinedOutput()
236 t.Logf("%s", out)
237 return string(out), err
238 }
239
240 func testCopy(t *testing.T, dir, goarch, goos, src, outfile string) (string, error) {
241
242 run := []string{testenv.GoToolPath(t), "tool", "compile", "-p=x", "-json=0,file://log/opt", "-o", outfile, src}
243 t.Log(run)
244 cmd := testenv.Command(t, run[0], run[1:]...)
245 cmd.Dir = dir
246 cmd.Env = append(os.Environ(), "GOARCH="+goarch, "GOOS="+goos)
247 out, err := cmd.CombinedOutput()
248 t.Logf("%s", out)
249 return string(out), err
250 }
251
View as plain text