Source file src/runtime/race/output_test.go

     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  
     7  package race_test
     8  
     9  import (
    10  	"fmt"
    11  	"internal/testenv"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"regexp"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  )
    20  
    21  func 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  
    89  var 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", `
    98  package main
    99  import "time"
   100  var xptr *int
   101  var donechan chan bool
   102  func main() {
   103  	done := make(chan bool)
   104  	x := 0
   105  	startRacer(&x, done)
   106  	store(&x, 43)
   107  	<-done
   108  }
   109  func store(x *int, v int) {
   110  	*x = v
   111  }
   112  func startRacer(x *int, done chan bool) {
   113  	xptr = x
   114  	donechan = done
   115  	go racer()
   116  }
   117  func racer() {
   118  	time.Sleep(10*time.Millisecond)
   119  	store(xptr, 42)
   120  	donechan <- true
   121  }
   122  `, []string{`==================
   123  WARNING: DATA RACE
   124  Write 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  
   130  Previous 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  
   136  Goroutine [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  ==================
   142  Found 1 data race\(s\)
   143  exit status 66
   144  `}},
   145  
   146  	{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
   147  package main
   148  func 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.", `
   161  package main
   162  func 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", `
   177  package main
   178  func 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  ==================
   190  exit status 66
   191  `}},
   192  
   193  	{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
   194  package main_test
   195  import "testing"
   196  func 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
   213  FAIL`}},
   214  
   215  	{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
   216  package main
   217  func 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", `
   234  package main
   235  
   236  var x int
   237  var c chan int
   238  func main() {
   239  	c = make(chan int)
   240  	go f()
   241  	x = 1
   242  	<-c
   243  }
   244  
   245  func f() {
   246  	g(c)
   247  }
   248  
   249  func g(c chan int) {
   250  	h(c)
   251  }
   252  
   253  func h(c chan int) {
   254  	c <- x
   255  }
   256  `, []string{`==================
   257  WARNING: DATA RACE
   258  Read 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  
   266  Previous write at 0x[0-9,a-f]+ by main goroutine:
   267    main\.main\(\)
   268        .+/main\.go:9 \+0x[0-9,a-f]+
   269  
   270  Goroutine [0-9] \(running\) created at:
   271    main\.main\(\)
   272        .+/main\.go:8 \+0x[0-9,a-f]+
   273  ==================
   274  Found 1 data race\(s\)
   275  exit status 66
   276  `}},
   277  
   278  	// Test for https://golang.org/issue/17190
   279  	{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
   280  package main
   281  
   282  /*
   283  #include <pthread.h>
   284  typedef struct cb {
   285          int foo;
   286  } cb;
   287  extern void goCallback();
   288  static inline void *threadFunc(void *p) {
   289  	goCallback();
   290  	return 0;
   291  }
   292  static inline void startThread(cb* c) {
   293  	pthread_t th;
   294  	pthread_create(&th, 0, threadFunc, 0);
   295  }
   296  */
   297  import "C"
   298  
   299  var done chan bool
   300  var racy int
   301  
   302  //export goCallback
   303  func goCallback() {
   304  	racy++
   305  	done <- true
   306  }
   307  
   308  func main() {
   309  	done = make(chan bool)
   310  	var c C.cb
   311  	C.startThread(&c)
   312  	racy++
   313  	<- done
   314  }
   315  `, []string{`==================
   316  WARNING: DATA RACE
   317  Read at 0x[0-9,a-f]+ by main goroutine:
   318    main\.main\(\)
   319        .*/main\.go:34 \+0x[0-9,a-f]+
   320  
   321  Previous 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  
   329  Goroutine [0-9] \(running\) created at:
   330    runtime\.newextram\(\)
   331        .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
   332  ==================`,
   333  		`==================
   334  WARNING: DATA RACE
   335  Read at 0x[0-9,a-f]+ by .*:
   336    main\..*
   337        .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
   338  
   339  Previous write at 0x[0-9,a-f]+ by .*:
   340    main\..*
   341        .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
   342  
   343  Goroutine [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", `
   348  package main_test
   349  import "testing"
   350  func 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  
   362  func TestPass(t *testing.T) {
   363  }
   364  `, []string{`
   365  ==================
   366  --- FAIL: TestFail \([0-9.]+s\)
   367  .*testing.go:.*: race detected during execution of test
   368  FAIL`}},
   369  	{"mutex", "run", "", "atexit_sleep_ms=0", `
   370  package main
   371  import (
   372  	"sync"
   373  	"fmt"
   374  )
   375  func 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", `
   402  package main
   403  import (
   404  	"sync"
   405  	"time"
   406  )
   407  func 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{`==================
   429  WARNING: DATA RACE
   430  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   431    main\.main\.func2\(\)
   432        .*/main\.go:21 \+0x[0-9,a-f]+
   433  
   434  Previous write at 0x[0-9,a-f]+ by main goroutine:
   435    main\.main\(\)
   436        .*/main\.go:23 \+0x[0-9,a-f]+
   437  
   438  Goroutine [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", `
   445  package main
   446  import "sync"
   447  var wg sync.WaitGroup
   448  var x int
   449  func main() {
   450  	f := (*T).f
   451  	wg.Add(2)
   452  	go f(new(T))
   453  	f(new(T))
   454  	wg.Wait()
   455  }
   456  type T struct{}
   457  func (t T) f() {
   458  	x = 42
   459  	wg.Done()
   460  }
   461  `, []string{`==================
   462  WARNING: DATA RACE
   463  Write 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  
   471  Previous 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  

View as plain text