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