1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build race
6
7package race_test
8
9import (
10	"fmt"
11	"internal/testenv"
12	"os"
13	"os/exec"
14	"path/filepath"
15	"regexp"
16	"runtime"
17	"strings"
18	"testing"
19)
20
21func TestOutput(t *testing.T) {
22	pkgdir := t.TempDir()
23	out, err := exec.Command(testenv.GoToolPath(t), "install", "-race", "-pkgdir="+pkgdir, "testing").CombinedOutput()
24	if err != nil {
25		t.Fatalf("go install -race: %v\n%s", err, out)
26	}
27
28	for _, test := range tests {
29		if test.goos != "" && test.goos != runtime.GOOS {
30			t.Logf("test %v runs only on %v, skipping: ", test.name, test.goos)
31			continue
32		}
33		dir := t.TempDir()
34		source := "main.go"
35		if test.run == "test" {
36			source = "main_test.go"
37		}
38		src := filepath.Join(dir, source)
39		f, err := os.Create(src)
40		if err != nil {
41			t.Fatalf("failed to create file: %v", err)
42		}
43		_, err = f.WriteString(test.source)
44		if err != nil {
45			f.Close()
46			t.Fatalf("failed to write: %v", err)
47		}
48		if err := f.Close(); err != nil {
49			t.Fatalf("failed to close file: %v", err)
50		}
51
52		cmd := exec.Command(testenv.GoToolPath(t), test.run, "-race", "-pkgdir="+pkgdir, src)
53		// GODEBUG spoils program output, GOMAXPROCS makes it flaky.
54		for _, env := range os.Environ() {
55			if strings.HasPrefix(env, "GODEBUG=") ||
56				strings.HasPrefix(env, "GOMAXPROCS=") ||
57				strings.HasPrefix(env, "GORACE=") {
58				continue
59			}
60			cmd.Env = append(cmd.Env, env)
61		}
62		cmd.Env = append(cmd.Env,
63			"GOMAXPROCS=1", // see comment in race_test.go
64			"GORACE="+test.gorace,
65		)
66		got, _ := cmd.CombinedOutput()
67		matched := false
68		for _, re := range test.re {
69			if regexp.MustCompile(re).MatchString(string(got)) {
70				matched = true
71				break
72			}
73		}
74		if !matched {
75			exp := fmt.Sprintf("expect:\n%v\n", test.re[0])
76			if len(test.re) > 1 {
77				exp = fmt.Sprintf("expected one of %d patterns:\n",
78					len(test.re))
79				for k, re := range test.re {
80					exp += fmt.Sprintf("pattern %d:\n%v\n", k, re)
81				}
82			}
83			t.Fatalf("failed test case %v, %sgot:\n%s",
84				test.name, exp, got)
85		}
86	}
87}
88
89var tests = []struct {
90	name   string
91	run    string
92	goos   string
93	gorace string
94	source string
95	re     []string
96}{
97	{"simple", "run", "", "atexit_sleep_ms=0", `
98package main
99import "time"
100var xptr *int
101var donechan chan bool
102func main() {
103	done := make(chan bool)
104	x := 0
105	startRacer(&x, done)
106	store(&x, 43)
107	<-done
108}
109func store(x *int, v int) {
110	*x = v
111}
112func startRacer(x *int, done chan bool) {
113	xptr = x
114	donechan = done
115	go racer()
116}
117func racer() {
118	time.Sleep(10*time.Millisecond)
119	store(xptr, 42)
120	donechan <- true
121}
122`, []string{`==================
123WARNING: DATA RACE
124Write at 0x[0-9,a-f]+ by goroutine [0-9]:
125  main\.store\(\)
126      .+/main\.go:14 \+0x[0-9,a-f]+
127  main\.racer\(\)
128      .+/main\.go:23 \+0x[0-9,a-f]+
129
130Previous write at 0x[0-9,a-f]+ by main goroutine:
131  main\.store\(\)
132      .+/main\.go:14 \+0x[0-9,a-f]+
133  main\.main\(\)
134      .+/main\.go:10 \+0x[0-9,a-f]+
135
136Goroutine [0-9] \(running\) created at:
137  main\.startRacer\(\)
138      .+/main\.go:19 \+0x[0-9,a-f]+
139  main\.main\(\)
140      .+/main\.go:9 \+0x[0-9,a-f]+
141==================
142Found 1 data race\(s\)
143exit status 66
144`}},
145
146	{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
147package main
148func main() {
149	done := make(chan bool)
150	x := 0; _ = x
151	go func() {
152		x = 42
153		done <- true
154	}()
155	x = 43
156	<-done
157}
158`, []string{`exit status 13`}},
159
160	{"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
161package main
162func main() {
163	done := make(chan bool)
164	x := 0; _ = x
165	go func() {
166		x = 42
167		done <- true
168	}()
169	x = 43
170	<-done
171}
172`, []string{`
173      go:7 \+0x[0-9,a-f]+
174`}},
175
176	{"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", `
177package main
178func main() {
179	done := make(chan bool)
180	x := 0; _ = x
181	go func() {
182		x = 42
183		done <- true
184	}()
185	x = 43
186	<-done
187}
188`, []string{`
189==================
190exit status 66
191`}},
192
193	{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
194package main_test
195import "testing"
196func TestFail(t *testing.T) {
197	done := make(chan bool)
198	x := 0
199	_ = x
200	go func() {
201		x = 42
202		done <- true
203	}()
204	x = 43
205	<-done
206	t.Log(t.Failed())
207}
208`, []string{`
209==================
210--- FAIL: TestFail \([0-9.]+s\)
211.*testing.go:.*: race detected during execution of test
212.*main_test.go:14: true
213FAIL`}},
214
215	{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
216package main
217func main() {
218	done := make(chan string)
219	data := make([]byte, 10)
220	go func() {
221		done <- string(data)
222	}()
223	data[0] = 1
224	<-done
225}
226`, []string{`
227  runtime\.slicebytetostring\(\)
228      .*/runtime/string\.go:.*
229  main\.main\.func1\(\)
230      .*/main.go:7`}},
231
232	// Test for https://golang.org/issue/33309
233	{"midstack_inlining_traceback", "run", "linux", "atexit_sleep_ms=0", `
234package main
235
236var x int
237var c chan int
238func main() {
239	c = make(chan int)
240	go f()
241	x = 1
242	<-c
243}
244
245func f() {
246	g(c)
247}
248
249func g(c chan int) {
250	h(c)
251}
252
253func h(c chan int) {
254	c <- x
255}
256`, []string{`==================
257WARNING: DATA RACE
258Read at 0x[0-9,a-f]+ by goroutine [0-9]:
259  main\.h\(\)
260      .+/main\.go:22 \+0x[0-9,a-f]+
261  main\.g\(\)
262      .+/main\.go:18 \+0x[0-9,a-f]+
263  main\.f\(\)
264      .+/main\.go:14 \+0x[0-9,a-f]+
265
266Previous write at 0x[0-9,a-f]+ by main goroutine:
267  main\.main\(\)
268      .+/main\.go:9 \+0x[0-9,a-f]+
269
270Goroutine [0-9] \(running\) created at:
271  main\.main\(\)
272      .+/main\.go:8 \+0x[0-9,a-f]+
273==================
274Found 1 data race\(s\)
275exit status 66
276`}},
277
278	// Test for https://golang.org/issue/17190
279	{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
280package main
281
282/*
283#include <pthread.h>
284typedef struct cb {
285        int foo;
286} cb;
287extern void goCallback();
288static inline void *threadFunc(void *p) {
289	goCallback();
290	return 0;
291}
292static inline void startThread(cb* c) {
293	pthread_t th;
294	pthread_create(&th, 0, threadFunc, 0);
295}
296*/
297import "C"
298
299var done chan bool
300var racy int
301
302//export goCallback
303func goCallback() {
304	racy++
305	done <- true
306}
307
308func main() {
309	done = make(chan bool)
310	var c C.cb
311	C.startThread(&c)
312	racy++
313	<- done
314}
315`, []string{`==================
316WARNING: DATA RACE
317Read at 0x[0-9,a-f]+ by main goroutine:
318  main\.main\(\)
319      .*/main\.go:34 \+0x[0-9,a-f]+
320
321Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
322  main\.goCallback\(\)
323      .*/main\.go:27 \+0x[0-9,a-f]+
324  _cgoexp_[0-9a-z]+_goCallback\(\)
325      .*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
326  _cgoexp_[0-9a-z]+_goCallback\(\)
327      <autogenerated>:1 \+0x[0-9,a-f]+
328
329Goroutine [0-9] \(running\) created at:
330  runtime\.newextram\(\)
331      .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
332==================`,
333		`==================
334WARNING: DATA RACE
335Read at 0x[0-9,a-f]+ by .*:
336  main\..*
337      .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
338
339Previous write at 0x[0-9,a-f]+ by .*:
340  main\..*
341      .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
342
343Goroutine [0-9] \(running\) created at:
344  runtime\.newextram\(\)
345      .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
346==================`}},
347	{"second_test_passes", "test", "", "atexit_sleep_ms=0", `
348package main_test
349import "testing"
350func TestFail(t *testing.T) {
351	done := make(chan bool)
352	x := 0
353	_ = x
354	go func() {
355		x = 42
356		done <- true
357	}()
358	x = 43
359	<-done
360}
361
362func TestPass(t *testing.T) {
363}
364`, []string{`
365==================
366--- FAIL: TestFail \([0-9.]+s\)
367.*testing.go:.*: race detected during execution of test
368FAIL`}},
369	{"mutex", "run", "", "atexit_sleep_ms=0", `
370package main
371import (
372	"sync"
373	"fmt"
374)
375func main() {
376	c := make(chan bool, 1)
377	threads := 1
378	iterations := 20000
379	data := 0
380	var wg sync.WaitGroup
381	for i := 0; i < threads; i++ {
382		wg.Add(1)
383		go func() {
384			defer wg.Done()
385			for i := 0; i < iterations; i++ {
386				c <- true
387				data += 1
388				<- c
389			}
390		}()
391	}
392	for i := 0; i < iterations; i++ {
393		c <- true
394		data += 1
395		<- c
396	}
397	wg.Wait()
398	if (data == iterations*(threads+1)) { fmt.Println("pass") }
399}`, []string{`pass`}},
400	// Test for https://github.com/golang/go/issues/37355
401	{"chanmm", "run", "", "atexit_sleep_ms=0", `
402package main
403import (
404	"sync"
405	"time"
406)
407func main() {
408	c := make(chan bool, 1)
409	var data uint64
410	var wg sync.WaitGroup
411	wg.Add(2)
412	c <- true
413	go func() {
414		defer wg.Done()
415		c <- true
416	}()
417	go func() {
418		defer wg.Done()
419		time.Sleep(time.Second)
420		<-c
421		data = 2
422	}()
423	data = 1
424	<-c
425	wg.Wait()
426	_ = data
427}
428`, []string{`==================
429WARNING: DATA RACE
430Write at 0x[0-9,a-f]+ by goroutine [0-9]:
431  main\.main\.func2\(\)
432      .*/main\.go:21 \+0x[0-9,a-f]+
433
434Previous write at 0x[0-9,a-f]+ by main goroutine:
435  main\.main\(\)
436      .*/main\.go:23 \+0x[0-9,a-f]+
437
438Goroutine [0-9] \(running\) created at:
439  main\.main\(\)
440      .*/main.go:[0-9]+ \+0x[0-9,a-f]+
441==================`}},
442	// Test symbolizing wrappers. Both (*T).f and main.gowrap1 are wrappers.
443	// go.dev/issue/60245
444	{"wrappersym", "run", "", "atexit_sleep_ms=0", `
445package main
446import "sync"
447var wg sync.WaitGroup
448var x int
449func main() {
450	f := (*T).f
451	wg.Add(2)
452	go f(new(T))
453	f(new(T))
454	wg.Wait()
455}
456type T struct{}
457func (t T) f() {
458	x = 42
459	wg.Done()
460}
461`, []string{`==================
462WARNING: DATA RACE
463Write at 0x[0-9,a-f]+ by goroutine [0-9]:
464  main\.T\.f\(\)
465      .*/main.go:15 \+0x[0-9,a-f]+
466  main\.\(\*T\)\.f\(\)
467      <autogenerated>:1 \+0x[0-9,a-f]+
468  main\.main\.gowrap1\(\)
469      .*/main.go:9 \+0x[0-9,a-f]+
470
471Previous write at 0x[0-9,a-f]+ by main goroutine:
472  main\.T\.f\(\)
473      .*/main.go:15 \+0x[0-9,a-f]+
474  main\.\(\*T\)\.f\(\)
475      <autogenerated>:1 \+0x[0-9,a-f]+
476  main\.main\(\)
477      .*/main.go:10 \+0x[0-9,a-f]+
478
479`}},
480}
481