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