1
2
3
4
5
6
7
8
9
10
11
12 package race_test
13
14 import (
15 "bufio"
16 "bytes"
17 "fmt"
18 "internal/testenv"
19 "io"
20 "log"
21 "math/rand"
22 "os"
23 "os/exec"
24 "path/filepath"
25 "strings"
26 "sync"
27 "sync/atomic"
28 "testing"
29 )
30
31 var (
32 passedTests = 0
33 totalTests = 0
34 falsePos = 0
35 falseNeg = 0
36 failingPos = 0
37 failingNeg = 0
38 failed = false
39 )
40
41 const (
42 visibleLen = 40
43 testPrefix = "=== RUN Test"
44 )
45
46 func TestRace(t *testing.T) {
47 testOutput, err := runTests(t)
48 if err != nil {
49 t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
50 }
51 reader := bufio.NewReader(bytes.NewReader(testOutput))
52
53 funcName := ""
54 var tsanLog []string
55 for {
56 s, err := nextLine(reader)
57 if err != nil {
58 fmt.Printf("%s\n", processLog(funcName, tsanLog))
59 break
60 }
61 if strings.HasPrefix(s, testPrefix) {
62 fmt.Printf("%s\n", processLog(funcName, tsanLog))
63 tsanLog = make([]string, 0, 100)
64 funcName = s[len(testPrefix):]
65 } else {
66 tsanLog = append(tsanLog, s)
67 }
68 }
69
70 if totalTests == 0 {
71 t.Fatalf("failed to parse test output:\n%s", testOutput)
72 }
73 fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
74 passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
75 fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
76 if failed {
77 t.Fail()
78 }
79 }
80
81
82
83
84
85 func nextLine(r *bufio.Reader) (string, error) {
86 s, err := r.ReadString('\n')
87 if err != nil {
88 if err != io.EOF {
89 log.Fatalf("nextLine: expected EOF, received %v", err)
90 }
91 return s, err
92 }
93 return s[:len(s)-1], nil
94 }
95
96
97
98
99
100 func processLog(testName string, tsanLog []string) string {
101 if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
102 return ""
103 }
104 gotRace := false
105 for _, s := range tsanLog {
106 if strings.Contains(s, "DATA RACE") {
107 gotRace = true
108 break
109 }
110 }
111
112 failing := strings.Contains(testName, "Failing")
113 expRace := !strings.HasPrefix(testName, "No")
114 for len(testName) < visibleLen {
115 testName += " "
116 }
117 if expRace == gotRace {
118 passedTests++
119 totalTests++
120 if failing {
121 failed = true
122 failingNeg++
123 }
124 return fmt.Sprintf("%s .", testName)
125 }
126 pos := ""
127 if expRace {
128 falseNeg++
129 } else {
130 falsePos++
131 pos = "+"
132 }
133 if failing {
134 failingPos++
135 } else {
136 failed = true
137 }
138 totalTests++
139 return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
140 }
141
142
143
144
145 func runTests(t *testing.T) ([]byte, error) {
146 tests, err := filepath.Glob("./testdata/*_test.go")
147 if err != nil {
148 return nil, err
149 }
150 args := []string{"test", "-race", "-v"}
151 args = append(args, tests...)
152 cmd := exec.Command(testenv.GoToolPath(t), args...)
153
154
155
156 for _, env := range os.Environ() {
157 if strings.HasPrefix(env, "GOMAXPROCS=") ||
158 strings.HasPrefix(env, "GODEBUG=") ||
159 strings.HasPrefix(env, "GORACE=") {
160 continue
161 }
162 cmd.Env = append(cmd.Env, env)
163 }
164
165
166
167
168
169
170
171
172
173
174 cmd.Env = append(cmd.Env,
175 "GOMAXPROCS=1",
176 "GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
177 )
178
179 out, _ := cmd.CombinedOutput()
180 if bytes.Contains(out, []byte("fatal error:")) {
181
182 return out, fmt.Errorf("runtime fatal error")
183 }
184 return out, nil
185 }
186
187 func TestIssue8102(t *testing.T) {
188
189 type S struct {
190 x any
191 i int
192 }
193 c := make(chan int)
194 a := [2]*int{}
195 for ; ; c <- *a[S{}.i] {
196 if t != nil {
197 break
198 }
199 }
200 }
201
202 func TestIssue9137(t *testing.T) {
203 a := []string{"a"}
204 i := 0
205 a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1]
206 if len(a) != 0 || a[:1][0] != "" {
207 t.Errorf("mangled a: %q %q", a, a[:1])
208 }
209 }
210
211 func BenchmarkSyncLeak(b *testing.B) {
212 const (
213 G = 1000
214 S = 1000
215 H = 10
216 )
217 var wg sync.WaitGroup
218 wg.Add(G)
219 for g := 0; g < G; g++ {
220 go func() {
221 defer wg.Done()
222 hold := make([][]uint32, H)
223 for i := 0; i < b.N; i++ {
224 a := make([]uint32, S)
225 atomic.AddUint32(&a[rand.Intn(len(a))], 1)
226 hold[rand.Intn(len(hold))] = a
227 }
228 _ = hold
229 }()
230 }
231 wg.Wait()
232 }
233
234 func BenchmarkStackLeak(b *testing.B) {
235 done := make(chan bool, 1)
236 for i := 0; i < b.N; i++ {
237 go func() {
238 growStack(rand.Intn(100))
239 done <- true
240 }()
241 <-done
242 }
243 }
244
245 func growStack(i int) {
246 if i == 0 {
247 return
248 }
249 growStack(i - 1)
250 }
251
View as plain text