github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/src/runtime/race/race_test.go (about) 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:\n%s", testOutput) 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 }