...
Run Format

Source file src/runtime/race/race_test.go

Documentation: runtime/race

  // Copyright 2012 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  // +build race
  
  // This program is used to verify the race detector
  // by running the tests and parsing their output.
  // It does not check stack correctness, completeness or anything else:
  // it merely verifies that if a test is expected to be racy
  // then the race is detected.
  package race_test
  
  import (
  	"bufio"
  	"bytes"
  	"fmt"
  	"internal/testenv"
  	"io"
  	"log"
  	"math/rand"
  	"os"
  	"os/exec"
  	"path/filepath"
  	"strings"
  	"sync"
  	"sync/atomic"
  	"testing"
  )
  
  var (
  	passedTests = 0
  	totalTests  = 0
  	falsePos    = 0
  	falseNeg    = 0
  	failingPos  = 0
  	failingNeg  = 0
  	failed      = false
  )
  
  const (
  	visibleLen = 40
  	testPrefix = "=== RUN   Test"
  )
  
  func TestRace(t *testing.T) {
  	testOutput, err := runTests(t)
  	if err != nil {
  		t.Fatalf("Failed to run tests: %v\n%v", err, string(testOutput))
  	}
  	reader := bufio.NewReader(bytes.NewReader(testOutput))
  
  	funcName := ""
  	var tsanLog []string
  	for {
  		s, err := nextLine(reader)
  		if err != nil {
  			fmt.Printf("%s\n", processLog(funcName, tsanLog))
  			break
  		}
  		if strings.HasPrefix(s, testPrefix) {
  			fmt.Printf("%s\n", processLog(funcName, tsanLog))
  			tsanLog = make([]string, 0, 100)
  			funcName = s[len(testPrefix):]
  		} else {
  			tsanLog = append(tsanLog, s)
  		}
  	}
  
  	if totalTests == 0 {
  		t.Fatalf("failed to parse test output:\n%s", testOutput)
  	}
  	fmt.Printf("\nPassed %d of %d tests (%.02f%%, %d+, %d-)\n",
  		passedTests, totalTests, 100*float64(passedTests)/float64(totalTests), falsePos, falseNeg)
  	fmt.Printf("%d expected failures (%d has not fail)\n", failingPos+failingNeg, failingNeg)
  	if failed {
  		t.Fail()
  	}
  }
  
  // nextLine is a wrapper around bufio.Reader.ReadString.
  // It reads a line up to the next '\n' character. Error
  // is non-nil if there are no lines left, and nil
  // otherwise.
  func nextLine(r *bufio.Reader) (string, error) {
  	s, err := r.ReadString('\n')
  	if err != nil {
  		if err != io.EOF {
  			log.Fatalf("nextLine: expected EOF, received %v", err)
  		}
  		return s, err
  	}
  	return s[:len(s)-1], nil
  }
  
  // processLog verifies whether the given ThreadSanitizer's log
  // contains a race report, checks this information against
  // the name of the testcase and returns the result of this
  // comparison.
  func processLog(testName string, tsanLog []string) string {
  	if !strings.HasPrefix(testName, "Race") && !strings.HasPrefix(testName, "NoRace") {
  		return ""
  	}
  	gotRace := false
  	for _, s := range tsanLog {
  		if strings.Contains(s, "DATA RACE") {
  			gotRace = true
  			break
  		}
  	}
  
  	failing := strings.Contains(testName, "Failing")
  	expRace := !strings.HasPrefix(testName, "No")
  	for len(testName) < visibleLen {
  		testName += " "
  	}
  	if expRace == gotRace {
  		passedTests++
  		totalTests++
  		if failing {
  			failed = true
  			failingNeg++
  		}
  		return fmt.Sprintf("%s .", testName)
  	}
  	pos := ""
  	if expRace {
  		falseNeg++
  	} else {
  		falsePos++
  		pos = "+"
  	}
  	if failing {
  		failingPos++
  	} else {
  		failed = true
  	}
  	totalTests++
  	return fmt.Sprintf("%s %s%s", testName, "FAILED", pos)
  }
  
  // runTests assures that the package and its dependencies is
  // built with instrumentation enabled and returns the output of 'go test'
  // which includes possible data race reports from ThreadSanitizer.
  func runTests(t *testing.T) ([]byte, error) {
  	tests, err := filepath.Glob("./testdata/*_test.go")
  	if err != nil {
  		return nil, err
  	}
  	args := []string{"test", "-race", "-v"}
  	args = append(args, tests...)
  	cmd := exec.Command(testenv.GoToolPath(t), args...)
  	// The following flags turn off heuristics that suppress seemingly identical reports.
  	// It is required because the tests contain a lot of data races on the same addresses
  	// (the tests are simple and the memory is constantly reused).
  	for _, env := range os.Environ() {
  		if strings.HasPrefix(env, "GOMAXPROCS=") ||
  			strings.HasPrefix(env, "GODEBUG=") ||
  			strings.HasPrefix(env, "GORACE=") {
  			continue
  		}
  		cmd.Env = append(cmd.Env, env)
  	}
  	// We set GOMAXPROCS=1 to prevent test flakiness.
  	// There are two sources of flakiness:
  	// 1. Some tests rely on particular execution order.
  	//    If the order is different, race does not happen at all.
  	// 2. Ironically, ThreadSanitizer runtime contains a logical race condition
  	//    that can lead to false negatives if racy accesses happen literally at the same time.
  	// Tests used to work reliably in the good old days of GOMAXPROCS=1.
  	// So let's set it for now. A more reliable solution is to explicitly annotate tests
  	// with required execution order by means of a special "invisible" synchronization primitive
  	// (that's what is done for C++ ThreadSanitizer tests). This is issue #14119.
  	cmd.Env = append(cmd.Env,
  		"GOMAXPROCS=1",
  		"GORACE=suppress_equal_stacks=0 suppress_equal_addresses=0",
  	)
  	// There are races: we expect tests to fail and the exit code to be non-zero.
  	out, _ := cmd.CombinedOutput()
  	return out, nil
  }
  
  func TestIssue8102(t *testing.T) {
  	// If this compiles with -race, the test passes.
  	type S struct {
  		x interface{}
  		i int
  	}
  	c := make(chan int)
  	a := [2]*int{}
  	for ; ; c <- *a[S{}.i] {
  		if t != nil {
  			break
  		}
  	}
  }
  
  func TestIssue9137(t *testing.T) {
  	a := []string{"a"}
  	i := 0
  	a[i], a[len(a)-1], a = a[len(a)-1], "", a[:len(a)-1]
  	if len(a) != 0 || a[:1][0] != "" {
  		t.Errorf("mangled a: %q %q", a, a[:1])
  	}
  }
  
  func BenchmarkSyncLeak(b *testing.B) {
  	const (
  		G = 1000
  		S = 1000
  		H = 10
  	)
  	var wg sync.WaitGroup
  	wg.Add(G)
  	for g := 0; g < G; g++ {
  		go func() {
  			defer wg.Done()
  			hold := make([][]uint32, H)
  			for i := 0; i < b.N; i++ {
  				a := make([]uint32, S)
  				atomic.AddUint32(&a[rand.Intn(len(a))], 1)
  				hold[rand.Intn(len(hold))] = a
  			}
  			_ = hold
  		}()
  	}
  	wg.Wait()
  }
  
  func BenchmarkStackLeak(b *testing.B) {
  	done := make(chan bool, 1)
  	for i := 0; i < b.N; i++ {
  		go func() {
  			growStack(rand.Intn(100))
  			done <- true
  		}()
  		<-done
  	}
  }
  
  func growStack(i int) {
  	if i == 0 {
  		return
  	}
  	growStack(i - 1)
  }
  

View as plain text