Home | History | Annotate | Download | only in race
      1 // Copyright 2012 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 // +build race
      6 
      7 // This program is used to verify the race detector
      8 // by running the tests and parsing their output.
      9 // It does not check stack correctness, completeness or anything else:
     10 // it merely verifies that if a test is expected to be racy
     11 // then the race is detected.
     12 package race_test
     13 
     14 import (
     15 	"bufio"
     16 	"bytes"
     17 	"fmt"
     18 	"internal/testenv"
     19 	"io"
     20 	"log"
     21 	"math/rand"
     22 	"os"
     23 	"os/exec"
     24 	"path/filepath"
     25 	"strings"
     26 	"sync"
     27 	"sync/atomic"
     28 	"testing"
     29 )
     30 
     31 var (
     32 	passedTests = 0
     33 	totalTests  = 0
     34 	falsePos    = 0
     35 	falseNeg    = 0
     36 	failingPos  = 0
     37 	failingNeg  = 0
     38 	failed      = false
     39 )
     40 
     41 const (
     42 	visibleLen = 40
     43 	testPrefix = "=== RUN   Test"
     44 )
     45 
     46 func TestRace(t *testing.T) {
     47 	testOutput, err := runTests(t)
     48 	if err != nil {
     49 		t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
     50 	}
     51 	reader := bufio.NewReader(bytes.NewReader(testOutput))
     52 
     53 	funcName := ""
     54 	var tsanLog []string
     55 	for {
     56 		s, err := nextLine(reader)
     57 		if err != nil {
     58 			fmt.Printf("%s\n", processLog(funcName, tsanLog))
     59 			break
     60 		}
     61 		if strings.HasPrefix(s, testPrefix) {
     62 			fmt.Printf("%s\n", processLog(funcName, tsanLog))
     63 			tsanLog = make([]string, 0, 100)
     64 			funcName = s[len(testPrefix):]
     65 		} else {
     66 			tsanLog = append(tsanLog, s)
     67 		}
     68 	}
     69 
     70 	if totalTests == 0 {
     71 		t.Fatalf("failed to parse test output")
     72 	}
     73 	fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
     74 		passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
     75 	fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
     76 	if failed {
     77 		t.Fail()
     78 	}
     79 }
     80 
     81 // nextLine is a wrapper around bufio.Reader.ReadString.
     82 // It reads a line up to the next '\n' character. Error
     83 // is non-nil if there are no lines left, and nil
     84 // otherwise.
     85 func nextLine(r *bufio.Reader) (string, error) {
     86 	s, err := r.ReadString('\n')
     87 	if err != nil {
     88 		if err != io.EOF {
     89 			log.Fatalf("nextLine: expected EOF, received %v", err)
     90 		}
     91 		return s, err
     92 	}
     93 	return s[:len(s)-1], nil
     94 }
     95 
     96 // processLog verifies whether the given ThreadSanitizer's log
     97 // contains a race report, checks this information against
     98 // the name of the testcase and returns the result of this
     99 // comparison.
    100 func processLog(testName string, tsanLog []string) string {
    101 	if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
    102 		return ""
    103 	}
    104 	gotRace := false
    105 	for _, s := range tsanLog {
    106 		if strings.Contains(s, "DATA RACE") {
    107 			gotRace = true
    108 			break
    109 		}
    110 	}
    111 
    112 	failing := strings.Contains(testName, "Failing")
    113 	expRace := !strings.HasPrefix(testName, "No")
    114 	for len(testName) < visibleLen {
    115 		testName += " "
    116 	}
    117 	if expRace == gotRace {
    118 		passedTests++
    119 		totalTests++
    120 		if failing {
    121 			failed = true
    122 			failingNeg++
    123 		}
    124 		return fmt.Sprintf("%s .", testName)
    125 	}
    126 	pos := ""
    127 	if expRace {
    128 		falseNeg++
    129 	} else {
    130 		falsePos++
    131 		pos = "+"
    132 	}
    133 	if failing {
    134 		failingPos++
    135 	} else {
    136 		failed = true
    137 	}
    138 	totalTests++
    139 	return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
    140 }
    141 
    142 // runTests assures that the package and its dependencies is
    143 // built with instrumentation enabled and returns the output of 'go test'
    144 // which includes possible data race reports from ThreadSanitizer.
    145 func runTests(t *testing.T) ([]byte, error) {
    146 	tests, err := filepath.Glob("./testdata/*_test.go")
    147 	if err != nil {
    148 		return nil, err
    149 	}
    150 	args := []string{"test", "-race", "-v"}
    151 	args = append(args, tests...)
    152 	cmd := exec.Command(testenv.GoToolPath(t), args...)
    153 	// The following flags turn off heuristics that suppress seemingly identical reports.
    154 	// It is required because the tests contain a lot of data races on the same addresses
    155 	// (the tests are simple and the memory is constantly reused).
    156 	for _, env := range os.Environ() {
    157 		if strings.HasPrefix(env, "GOMAXPROCS=") ||
    158 			strings.HasPrefix(env, "GODEBUG=") ||
    159 			strings.HasPrefix(env, "GORACE=") {
    160 			continue
    161 		}
    162 		cmd.Env = append(cmd.Env, env)
    163 	}
    164 	// We set GOMAXPROCS=1 to prevent test flakiness.
    165 	// There are two sources of flakiness:
    166 	// 1. Some tests rely on particular execution order.
    167 	//    If the order is different, race does not happen at all.
    168 	// 2. Ironically, ThreadSanitizer runtime contains a logical race condition
    169 	//    that can lead to false negatives if racy accesses happen literally at the same time.
    170 	// Tests used to work reliably in the good old days of GOMAXPROCS=1.
    171 	// So let's set it for now. A more reliable solution is to explicitly annotate tests
    172 	// with required execution order by means of a special "invisible" synchronization primitive
    173 	// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
    174 	cmd.Env = append(cmd.Env,
    175 		"GOMAXPROCS=1",
    176 		"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
    177 	)
    178 	// There are races: we expect tests to fail and the exit code to be non-zero.
    179 	out, _ := cmd.CombinedOutput()
    180 	return out, nil
    181 }
    182 
    183 func TestIssue8102(t *testing.T) {
    184 	// If this compiles with -race, the test passes.
    185 	type S struct {
    186 		x interface{}
    187 		i int
    188 	}
    189 	c := make(chan int)
    190 	a := [2]*int{}
    191 	for ; ; c <- *a[S{}.i] {
    192 		if t != nil {
    193 			break
    194 		}
    195 	}
    196 }
    197 
    198 func TestIssue9137(t *testing.T) {
    199 	a := []string{"a"}
    200 	i := 0
    201 	a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1]
    202 	if len(a) != 0 || a[:1][0] != "" {
    203 		t.Errorf("mangled a: %q %q", a, a[:1])
    204 	}
    205 }
    206 
    207 func BenchmarkSyncLeak(b *testing.B) {
    208 	const (
    209 		G = 1000
    210 		S = 1000
    211 		H = 10
    212 	)
    213 	var wg sync.WaitGroup
    214 	wg.Add(G)
    215 	for g := 0; g < G; g++ {
    216 		go func() {
    217 			defer wg.Done()
    218 			hold := make([][]uint32, H)
    219 			for i := 0; i < b.N; i++ {
    220 				a := make([]uint32, S)
    221 				atomic.AddUint32(&a[rand.Intn(len(a))], 1)
    222 				hold[rand.Intn(len(hold))] = a
    223 			}
    224 			_ = hold
    225 		}()
    226 	}
    227 	wg.Wait()
    228 }
    229 
    230 func BenchmarkStackLeak(b *testing.B) {
    231 	done := make(chan bool, 1)
    232 	for i := 0; i < b.N; i++ {
    233 		go func() {
    234 			growStack(rand.Intn(100))
    235 			done <- true
    236 		}()
    237 		<-done
    238 	}
    239 }
    240 
    241 func growStack(i int) {
    242 	if i == 0 {
    243 		return
    244 	}
    245 	growStack(i - 1)
    246 }
    247