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