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