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  		t.Run(test.name, func(t *testing.T) {
    30  			if test.goos != "" && test.goos != runtime.GOOS {
    31  				t.Skipf("runs only on %v", test.goos)
    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: %sgot:\n%s", exp, got)
    84  			}
    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  	// Allow the race output in either order.
   124  	`==================
   125  WARNING: DATA RACE
   126  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   127    main\.store\(\)
   128        .+/main\.go:14 \+0x[0-9,a-f]+
   129    main\.racer\(\)
   130        .+/main\.go:23 \+0x[0-9,a-f]+
   131  
   132  Previous write at 0x[0-9,a-f]+ by main goroutine:
   133    main\.store\(\)
   134        .+/main\.go:14 \+0x[0-9,a-f]+
   135    main\.main\(\)
   136        .+/main\.go:10 \+0x[0-9,a-f]+
   137  
   138  Goroutine [0-9] \(running\) created at:
   139    main\.startRacer\(\)
   140        .+/main\.go:19 \+0x[0-9,a-f]+
   141    main\.main\(\)
   142        .+/main\.go:9 \+0x[0-9,a-f]+
   143  ==================
   144  Found 1 data race\(s\)
   145  exit status 66
   146  `,
   147  	`==================
   148  WARNING: DATA RACE
   149  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   150    main\.store\(\)
   151        .+/main\.go:14 \+0x[0-9,a-f]+
   152    main\.main\(\)
   153        .+/main\.go:10 \+0x[0-9,a-f]+
   154  
   155  Previous write at 0x[0-9,a-f]+ by main goroutine:
   156    main\.store\(\)
   157        .+/main\.go:14 \+0x[0-9,a-f]+
   158    main\.racer\(\)
   159        .+/main\.go:23 \+0x[0-9,a-f]+
   160  
   161  Goroutine [0-9] \(running\) created at:
   162    main\.startRacer\(\)
   163        .+/main\.go:19 \+0x[0-9,a-f]+
   164    main\.main\(\)
   165        .+/main\.go:9 \+0x[0-9,a-f]+
   166  ==================
   167  Found 1 data race\(s\)
   168  exit status 66
   169  `}},
   170  
   171  	{"exitcode", "run", "", "atexit_sleep_ms=0 exitcode=13", `
   172  package main
   173  func main() {
   174  	done := make(chan bool)
   175  	x := 0; _ = x
   176  	go func() {
   177  		x = 42
   178  		done <- true
   179  	}()
   180  	x = 43
   181  	<-done
   182  }
   183  `, []string{`exit status 13`}},
   184  
   185  	{"strip_path_prefix", "run", "", "atexit_sleep_ms=0 strip_path_prefix=/main.", `
   186  package main
   187  func main() {
   188  	done := make(chan bool)
   189  	x := 0; _ = x
   190  	go func() {
   191  		x = 42
   192  		done <- true
   193  	}()
   194  	x = 43
   195  	<-done
   196  }
   197  `, []string{`
   198        go:7 \+0x[0-9,a-f]+
   199  `}},
   200  
   201  	{"halt_on_error", "run", "", "atexit_sleep_ms=0 halt_on_error=1", `
   202  package main
   203  func main() {
   204  	done := make(chan bool)
   205  	x := 0; _ = x
   206  	go func() {
   207  		x = 42
   208  		done <- true
   209  	}()
   210  	x = 43
   211  	<-done
   212  }
   213  `, []string{`
   214  ==================
   215  exit status 66
   216  `}},
   217  
   218  	{"test_fails_on_race", "test", "", "atexit_sleep_ms=0", `
   219  package main_test
   220  import "testing"
   221  func TestFail(t *testing.T) {
   222  	done := make(chan bool)
   223  	x := 0
   224  	_ = x
   225  	go func() {
   226  		x = 42
   227  		done <- true
   228  	}()
   229  	x = 43
   230  	<-done
   231  	t.Log(t.Failed())
   232  }
   233  `, []string{`
   234  ==================
   235  --- FAIL: TestFail \([0-9.]+s\)
   236  .*testing.go:.*: race detected during execution of test
   237  .*main_test.go:14: true
   238  FAIL`}},
   239  
   240  	{"slicebytetostring_pc", "run", "", "atexit_sleep_ms=0", `
   241  package main
   242  func main() {
   243  	done := make(chan string)
   244  	data := make([]byte, 10)
   245  	go func() {
   246  		done <- string(data)
   247  	}()
   248  	data[0] = 1
   249  	<-done
   250  }
   251  `, []string{`
   252    runtime\.slicebytetostring\(\)
   253        .*/runtime/string\.go:.*
   254    main\.main\.func1\(\)
   255        .*/main.go:7`}},
   256  
   257  	// Test for https://golang.org/issue/33309
   258  	{"midstack_inlining_traceback", "run", "linux", "atexit_sleep_ms=0", `
   259  package main
   260  
   261  var x int
   262  var c chan int
   263  func main() {
   264  	c = make(chan int)
   265  	go f()
   266  	x = 1
   267  	<-c
   268  }
   269  
   270  func f() {
   271  	g(c)
   272  }
   273  
   274  func g(c chan int) {
   275  	h(c)
   276  }
   277  
   278  func h(c chan int) {
   279  	c <- x
   280  }
   281  `, []string{
   282  	// Allow the race output in either order.
   283  	`==================
   284  WARNING: DATA RACE
   285  Read at 0x[0-9,a-f]+ by goroutine [0-9]:
   286    main\.h\(\)
   287        .+/main\.go:22 \+0x[0-9,a-f]+
   288    main\.g\(\)
   289        .+/main\.go:18 \+0x[0-9,a-f]+
   290    main\.f\(\)
   291        .+/main\.go:14 \+0x[0-9,a-f]+
   292  
   293  Previous write at 0x[0-9,a-f]+ by main goroutine:
   294    main\.main\(\)
   295        .+/main\.go:9 \+0x[0-9,a-f]+
   296  
   297  Goroutine [0-9] \(running\) created at:
   298    main\.main\(\)
   299        .+/main\.go:8 \+0x[0-9,a-f]+
   300  ==================
   301  Found 1 data race\(s\)
   302  exit status 66
   303  `,
   304  	`==================
   305  WARNING: DATA RACE
   306  Write at 0x[0-9,a-f]+ by main goroutine:
   307    main\.main\(\)
   308        .+/main\.go:9 \+0x[0-9,a-f]+
   309  
   310  Previous read at 0x[0-9,a-f]+ by goroutine [0-9]:
   311    main\.h\(\)
   312        .+/main\.go:22 \+0x[0-9,a-f]+
   313    main\.g\(\)
   314        .+/main\.go:18 \+0x[0-9,a-f]+
   315    main\.f\(\)
   316        .+/main\.go:14 \+0x[0-9,a-f]+
   317  
   318  Goroutine [0-9] \(running\) created at:
   319    main\.main\(\)
   320        .+/main\.go:8 \+0x[0-9,a-f]+
   321  ==================
   322  Found 1 data race\(s\)
   323  exit status 66
   324  `}},
   325  
   326  	// Test for https://golang.org/issue/17190
   327  	{"external_cgo_thread", "run", "linux", "atexit_sleep_ms=0", `
   328  package main
   329  
   330  /*
   331  #include <pthread.h>
   332  typedef struct cb {
   333          int foo;
   334  } cb;
   335  extern void goCallback();
   336  static inline void *threadFunc(void *p) {
   337  	goCallback();
   338  	return 0;
   339  }
   340  static inline void startThread(cb* c) {
   341  	pthread_t th;
   342  	pthread_create(&th, 0, threadFunc, 0);
   343  }
   344  */
   345  import "C"
   346  
   347  var done chan bool
   348  var racy int
   349  
   350  //export goCallback
   351  func goCallback() {
   352  	racy++
   353  	done <- true
   354  }
   355  
   356  func main() {
   357  	done = make(chan bool)
   358  	var c C.cb
   359  	C.startThread(&c)
   360  	racy++
   361  	<- done
   362  }
   363  `, []string{
   364  	// Optionally allow ABI wrappers, and allow the race output in either order.
   365  	`==================
   366  WARNING: DATA RACE
   367  Read at 0x[0-9,a-f]+ by main goroutine:
   368    main\.main\(\)
   369        .*/main\.go:34 \+0x[0-9,a-f]+
   370  
   371  Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
   372    main\.goCallback\(\)
   373        .*/main\.go:27 \+0x[0-9,a-f]+
   374    _cgoexp_[0-9a-z]+_goCallback\(\)
   375        .*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
   376    _cgoexp_[0-9a-z]+_goCallback\(\)
   377        <autogenerated>:1 \+0x[0-9,a-f]+
   378  
   379  Goroutine [0-9] \(running\) created at:
   380    runtime\.newextram\(\)
   381        .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
   382  ==================`,
   383  	`==================
   384  WARNING: DATA RACE
   385  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   386    main\.goCallback\(\)
   387        .*/main\.go:27 \+0x[0-9,a-f]+
   388    _cgoexp_[0-9a-z]+_goCallback\(\)
   389        .*_cgo_gotypes\.go:[0-9]+ \+0x[0-9,a-f]+
   390    _cgoexp_[0-9a-z]+_goCallback\(\)
   391        <autogenerated>:1 \+0x[0-9,a-f]+
   392  
   393  Previous read at 0x[0-9,a-f]+ by main goroutine:
   394    main\.main\(\)
   395        .*/main\.go:34 \+0x[0-9,a-f]+
   396  
   397  Goroutine [0-9] \(running\) created at:
   398    runtime\.newextram\(\)
   399        .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
   400  ==================`,
   401  		`==================
   402  WARNING: DATA RACE
   403  Read at 0x[0-9,a-f]+ by .*:
   404    main\..*
   405        .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
   406  
   407  Previous write at 0x[0-9,a-f]+ by .*:
   408    main\..*
   409        .*/main\.go:[0-9]+ \+0x[0-9,a-f]+(?s).*
   410  
   411  Goroutine [0-9] \(running\) created at:
   412    runtime\.newextram\(\)
   413        .*/runtime/proc.go:[0-9]+ \+0x[0-9,a-f]+
   414  ==================`}},
   415  	{"second_test_passes", "test", "", "atexit_sleep_ms=0", `
   416  package main_test
   417  import "testing"
   418  func TestFail(t *testing.T) {
   419  	done := make(chan bool)
   420  	x := 0
   421  	_ = x
   422  	go func() {
   423  		x = 42
   424  		done <- true
   425  	}()
   426  	x = 43
   427  	<-done
   428  }
   429  
   430  func TestPass(t *testing.T) {
   431  }
   432  `, []string{`
   433  ==================
   434  --- FAIL: TestFail \([0-9.]+s\)
   435  .*testing.go:.*: race detected during execution of test
   436  FAIL`}},
   437  	{"mutex", "run", "", "atexit_sleep_ms=0", `
   438  package main
   439  import (
   440  	"sync"
   441  	"fmt"
   442  )
   443  func main() {
   444  	c := make(chan bool, 1)
   445  	threads := 1
   446  	iterations := 20000
   447  	data := 0
   448  	var wg sync.WaitGroup
   449  	for i := 0; i < threads; i++ {
   450  		wg.Add(1)
   451  		go func() {
   452  			defer wg.Done()
   453  			for i := 0; i < iterations; i++ {
   454  				c <- true
   455  				data += 1
   456  				<- c
   457  			}
   458  		}()
   459  	}
   460  	for i := 0; i < iterations; i++ {
   461  		c <- true
   462  		data += 1
   463  		<- c
   464  	}
   465  	wg.Wait()
   466  	if (data == iterations*(threads+1)) { fmt.Println("pass") }
   467  }`, []string{`pass`}},
   468  	// Test for https://github.com/golang/go/issues/37355
   469  	{"chanmm", "run", "", "atexit_sleep_ms=0", `
   470  package main
   471  import (
   472  	"sync"
   473  	"time"
   474  )
   475  func main() {
   476  	c := make(chan bool, 1)
   477  	var data uint64
   478  	var wg sync.WaitGroup
   479  	wg.Add(2)
   480  	c <- true
   481  	go func() {
   482  		defer wg.Done()
   483  		c <- true
   484  	}()
   485  	go func() {
   486  		defer wg.Done()
   487  		time.Sleep(time.Second)
   488  		<-c
   489  		data = 2
   490  	}()
   491  	data = 1
   492  	<-c
   493  	wg.Wait()
   494  	_ = data
   495  }
   496  `, []string{
   497  	// Allow the race output in either order.
   498  	`==================
   499  WARNING: DATA RACE
   500  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   501    main\.main\.func2\(\)
   502        .*/main\.go:21 \+0x[0-9,a-f]+
   503  
   504  Previous write at 0x[0-9,a-f]+ by main goroutine:
   505    main\.main\(\)
   506        .*/main\.go:23 \+0x[0-9,a-f]+
   507  
   508  Goroutine [0-9] \(running\) created at:
   509    main\.main\(\)
   510        .*/main.go:[0-9]+ \+0x[0-9,a-f]+
   511  ==================`,
   512  	`==================
   513  WARNING: DATA RACE
   514  Write at 0x[0-9,a-f]+ by main goroutine:
   515    main\.main\(\)
   516        .*/main\.go:23 \+0x[0-9,a-f]+
   517  
   518  Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
   519    main\.main\.func2\(\)
   520        .*/main\.go:21 \+0x[0-9,a-f]+
   521  
   522  Goroutine [0-9] \(running\) created at:
   523    main\.main\(\)
   524        .*/main.go:[0-9]+ \+0x[0-9,a-f]+
   525  ==================`}},
   526  	// Test symbolizing wrappers. Both (*T).f and main.gowrap1 are wrappers.
   527  	// go.dev/issue/60245
   528  	{"wrappersym", "run", "", "atexit_sleep_ms=0", `
   529  package main
   530  import "sync"
   531  var wg sync.WaitGroup
   532  var x int
   533  func main() {
   534  	f := (*T).f
   535  	wg.Add(2)
   536  	go f(new(T))
   537  	f(new(T))
   538  	wg.Wait()
   539  }
   540  type T struct{}
   541  func (t T) f() {
   542  	x = 42
   543  	wg.Done()
   544  }
   545  `, []string{
   546  	// Allow the race output in either order.
   547  	`==================
   548  WARNING: DATA RACE
   549  Write at 0x[0-9,a-f]+ by goroutine [0-9]:
   550    main\.T\.f\(\)
   551        .*/main.go:15 \+0x[0-9,a-f]+
   552    main\.\(\*T\)\.f\(\)
   553        <autogenerated>:1 \+0x[0-9,a-f]+
   554    main\.main\.gowrap1\(\)
   555        .*/main.go:9 \+0x[0-9,a-f]+
   556  
   557  Previous write at 0x[0-9,a-f]+ by main goroutine:
   558    main\.T\.f\(\)
   559        .*/main.go:15 \+0x[0-9,a-f]+
   560    main\.\(\*T\)\.f\(\)
   561        <autogenerated>:1 \+0x[0-9,a-f]+
   562    main\.main\(\)
   563        .*/main.go:10 \+0x[0-9,a-f]+
   564  
   565  `,
   566  	`==================
   567  WARNING: DATA RACE
   568  Write at 0x[0-9,a-f]+ by main goroutine:
   569    main\.T\.f\(\)
   570        .*/main.go:15 \+0x[0-9,a-f]+
   571    main\.\(\*T\)\.f\(\)
   572        <autogenerated>:1 \+0x[0-9,a-f]+
   573    main\.main\(\)
   574        .*/main.go:10 \+0x[0-9,a-f]+
   575  
   576  Previous write at 0x[0-9,a-f]+ by goroutine [0-9]:
   577    main\.T\.f\(\)
   578        .*/main.go:15 \+0x[0-9,a-f]+
   579    main\.\(\*T\)\.f\(\)
   580        <autogenerated>:1 \+0x[0-9,a-f]+
   581    main\.main\.gowrap1\(\)
   582        .*/main.go:9 \+0x[0-9,a-f]+
   583  
   584  `}},
   585  	{"non_inline_array_compare", "run", "", "atexit_sleep_ms=0", `
   586  package main
   587  
   588  import (
   589  	"math/rand/v2"
   590  )
   591  
   592  var x = [1024]byte{}
   593  
   594  var ch = make(chan bool)
   595  
   596  func main() {
   597  	started := make(chan struct{})
   598  	go func() {
   599  		close(started)
   600  		var y = [len(x)]byte{}
   601  		eq := x == y
   602  		ch <- eq
   603  	}()
   604  	<-started
   605  	x[rand.IntN(len(x))]++
   606  	println(<-ch)
   607  }
   608  `, []string{`==================
   609  WARNING: DATA RACE
   610  `}},
   611  	{"non_inline_struct_compare", "run", "", "atexit_sleep_ms=0", `
   612  package main
   613  
   614  import "math/rand/v2"
   615  
   616  type S struct {
   617  	a [1024]byte
   618  }
   619  
   620  var x = S{a: [1024]byte{}}
   621  
   622  var ch = make(chan bool)
   623  
   624  func main() {
   625  	started := make(chan struct{})
   626  	go func() {
   627  		close(started)
   628  		var y = S{a: [len(x.a)]byte{}}
   629  		eq := x == y
   630  		ch <- eq
   631  	}()
   632  	<-started
   633  	x.a[rand.IntN(len(x.a))]++
   634  	println(<-ch)
   635  }
   636  `, []string{`==================
   637  WARNING: DATA RACE
   638  `}},
   639  }
   640  

View as plain text