github.com/riscv/riscv-go@v0.0.0-20200123204226-124ebd6fcc8e/src/runtime/pprof/pprof_test.go (about)

     1  // Copyright 2011 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 !nacl
     6  
     7  package pprof_test
     8  
     9  import (
    10  	"bytes"
    11  	"compress/gzip"
    12  	"fmt"
    13  	"internal/pprof/profile"
    14  	"internal/testenv"
    15  	"io"
    16  	"io/ioutil"
    17  	"math/big"
    18  	"os"
    19  	"os/exec"
    20  	"regexp"
    21  	"runtime"
    22  	. "runtime/pprof"
    23  	"strings"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  func cpuHogger(f func(), dur time.Duration) {
    30  	// We only need to get one 100 Hz clock tick, so we've got
    31  	// a large safety buffer.
    32  	// But do at least 500 iterations (which should take about 100ms),
    33  	// otherwise TestCPUProfileMultithreaded can fail if only one
    34  	// thread is scheduled during the testing period.
    35  	t0 := time.Now()
    36  	for i := 0; i < 500 || time.Since(t0) < dur; i++ {
    37  		f()
    38  	}
    39  }
    40  
    41  var (
    42  	salt1 = 0
    43  	salt2 = 0
    44  )
    45  
    46  // The actual CPU hogging function.
    47  // Must not call other functions nor access heap/globals in the loop,
    48  // otherwise under race detector the samples will be in the race runtime.
    49  func cpuHog1() {
    50  	foo := salt1
    51  	for i := 0; i < 1e5; i++ {
    52  		if foo > 0 {
    53  			foo *= foo
    54  		} else {
    55  			foo *= foo + 1
    56  		}
    57  	}
    58  	salt1 = foo
    59  }
    60  
    61  func cpuHog2() {
    62  	foo := salt2
    63  	for i := 0; i < 1e5; i++ {
    64  		if foo > 0 {
    65  			foo *= foo
    66  		} else {
    67  			foo *= foo + 2
    68  		}
    69  	}
    70  	salt2 = foo
    71  }
    72  
    73  func TestCPUProfile(t *testing.T) {
    74  	testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1"}, func(dur time.Duration) {
    75  		cpuHogger(cpuHog1, dur)
    76  	})
    77  }
    78  
    79  func TestCPUProfileMultithreaded(t *testing.T) {
    80  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
    81  	testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1", "runtime/pprof_test.cpuHog2"}, func(dur time.Duration) {
    82  		c := make(chan int)
    83  		go func() {
    84  			cpuHogger(cpuHog1, dur)
    85  			c <- 1
    86  		}()
    87  		cpuHogger(cpuHog2, dur)
    88  		<-c
    89  	})
    90  }
    91  
    92  func parseProfile(t *testing.T, valBytes []byte, f func(uintptr, []uintptr)) {
    93  	p, err := profile.Parse(bytes.NewReader(valBytes))
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	for _, sample := range p.Sample {
    98  		count := uintptr(sample.Value[0])
    99  		stk := make([]uintptr, len(sample.Location))
   100  		for i := range sample.Location {
   101  			stk[i] = uintptr(sample.Location[i].Address)
   102  		}
   103  		f(count, stk)
   104  	}
   105  }
   106  
   107  func testCPUProfile(t *testing.T, need []string, f func(dur time.Duration)) {
   108  	switch runtime.GOOS {
   109  	case "darwin":
   110  		switch runtime.GOARCH {
   111  		case "arm", "arm64":
   112  			// nothing
   113  		default:
   114  			out, err := exec.Command("uname", "-a").CombinedOutput()
   115  			if err != nil {
   116  				t.Fatal(err)
   117  			}
   118  			vers := string(out)
   119  			t.Logf("uname -a: %v", vers)
   120  		}
   121  	case "plan9":
   122  		t.Skip("skipping on plan9")
   123  	}
   124  
   125  	const maxDuration = 5 * time.Second
   126  	// If we're running a long test, start with a long duration
   127  	// because some of the tests (e.g., TestStackBarrierProfiling)
   128  	// are trying to make sure something *doesn't* happen.
   129  	duration := 5 * time.Second
   130  	if testing.Short() {
   131  		duration = 200 * time.Millisecond
   132  	}
   133  
   134  	// Profiling tests are inherently flaky, especially on a
   135  	// loaded system, such as when this test is running with
   136  	// several others under go test std. If a test fails in a way
   137  	// that could mean it just didn't run long enough, try with a
   138  	// longer duration.
   139  	for duration <= maxDuration {
   140  		var prof bytes.Buffer
   141  		if err := StartCPUProfile(&prof); err != nil {
   142  			t.Fatal(err)
   143  		}
   144  		f(duration)
   145  		StopCPUProfile()
   146  
   147  		if profileOk(t, need, prof, duration) {
   148  			return
   149  		}
   150  
   151  		duration *= 2
   152  		if duration <= maxDuration {
   153  			t.Logf("retrying with %s duration", duration)
   154  		}
   155  	}
   156  
   157  	if badOS[runtime.GOOS] {
   158  		t.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime.GOOS)
   159  		return
   160  	}
   161  	// Ignore the failure if the tests are running in a QEMU-based emulator,
   162  	// QEMU is not perfect at emulating everything.
   163  	// IN_QEMU environmental variable is set by some of the Go builders.
   164  	// IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
   165  	if os.Getenv("IN_QEMU") == "1" {
   166  		t.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
   167  		return
   168  	}
   169  	t.FailNow()
   170  }
   171  
   172  func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Duration) (ok bool) {
   173  	ok = true
   174  
   175  	// Check that profile is well formed and contains need.
   176  	have := make([]uintptr, len(need))
   177  	var samples uintptr
   178  	parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
   179  		samples += count
   180  		for _, pc := range stk {
   181  			f := runtime.FuncForPC(pc)
   182  			if f == nil {
   183  				continue
   184  			}
   185  			for i, name := range need {
   186  				if strings.Contains(f.Name(), name) {
   187  					have[i] += count
   188  				}
   189  			}
   190  			if strings.Contains(f.Name(), "stackBarrier") {
   191  				// The runtime should have unwound this.
   192  				t.Fatalf("profile includes stackBarrier")
   193  			}
   194  		}
   195  	})
   196  	t.Logf("total %d CPU profile samples collected", samples)
   197  
   198  	if samples < 10 && runtime.GOOS == "windows" {
   199  		// On some windows machines we end up with
   200  		// not enough samples due to coarse timer
   201  		// resolution. Let it go.
   202  		t.Log("too few samples on Windows (golang.org/issue/10842)")
   203  		return false
   204  	}
   205  
   206  	// Check that we got a reasonable number of samples.
   207  	// We used to always require at least ideal/4 samples,
   208  	// but that is too hard to guarantee on a loaded system.
   209  	// Now we accept 10 or more samples, which we take to be
   210  	// enough to show that at least some profiling is occurring.
   211  	if ideal := uintptr(duration * 100 / time.Second); samples == 0 || (samples < ideal/4 && samples < 10) {
   212  		t.Logf("too few samples; got %d, want at least %d, ideally %d", samples, ideal/4, ideal)
   213  		ok = false
   214  	}
   215  
   216  	if len(need) == 0 {
   217  		return ok
   218  	}
   219  
   220  	var total uintptr
   221  	for i, name := range need {
   222  		total += have[i]
   223  		t.Logf("%s: %d\n", name, have[i])
   224  	}
   225  	if total == 0 {
   226  		t.Logf("no samples in expected functions")
   227  		ok = false
   228  	}
   229  	// We'd like to check a reasonable minimum, like
   230  	// total / len(have) / smallconstant, but this test is
   231  	// pretty flaky (see bug 7095).  So we'll just test to
   232  	// make sure we got at least one sample.
   233  	min := uintptr(1)
   234  	for i, name := range need {
   235  		if have[i] < min {
   236  			t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
   237  			ok = false
   238  		}
   239  	}
   240  	return ok
   241  }
   242  
   243  // Fork can hang if preempted with signals frequently enough (see issue 5517).
   244  // Ensure that we do not do this.
   245  func TestCPUProfileWithFork(t *testing.T) {
   246  	testenv.MustHaveExec(t)
   247  
   248  	heap := 1 << 30
   249  	if runtime.GOOS == "android" {
   250  		// Use smaller size for Android to avoid crash.
   251  		heap = 100 << 20
   252  	}
   253  	if testing.Short() {
   254  		heap = 100 << 20
   255  	}
   256  	// This makes fork slower.
   257  	garbage := make([]byte, heap)
   258  	// Need to touch the slice, otherwise it won't be paged in.
   259  	done := make(chan bool)
   260  	go func() {
   261  		for i := range garbage {
   262  			garbage[i] = 42
   263  		}
   264  		done <- true
   265  	}()
   266  	<-done
   267  
   268  	var prof bytes.Buffer
   269  	if err := StartCPUProfile(&prof); err != nil {
   270  		t.Fatal(err)
   271  	}
   272  	defer StopCPUProfile()
   273  
   274  	for i := 0; i < 10; i++ {
   275  		exec.Command(os.Args[0], "-h").CombinedOutput()
   276  	}
   277  }
   278  
   279  // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
   280  // If it did, it would see inconsistent state and would either record an incorrect stack
   281  // or crash because the stack was malformed.
   282  func TestGoroutineSwitch(t *testing.T) {
   283  	// How much to try. These defaults take about 1 seconds
   284  	// on a 2012 MacBook Pro. The ones in short mode take
   285  	// about 0.1 seconds.
   286  	tries := 10
   287  	count := 1000000
   288  	if testing.Short() {
   289  		tries = 1
   290  	}
   291  	for try := 0; try < tries; try++ {
   292  		var prof bytes.Buffer
   293  		if err := StartCPUProfile(&prof); err != nil {
   294  			t.Fatal(err)
   295  		}
   296  		for i := 0; i < count; i++ {
   297  			runtime.Gosched()
   298  		}
   299  		StopCPUProfile()
   300  
   301  		// Read profile to look for entries for runtime.gogo with an attempt at a traceback.
   302  		// The special entry
   303  		parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
   304  			// An entry with two frames with 'System' in its top frame
   305  			// exists to record a PC without a traceback. Those are okay.
   306  			if len(stk) == 2 {
   307  				f := runtime.FuncForPC(stk[1])
   308  				if f != nil && (f.Name() == "runtime._System" || f.Name() == "runtime._ExternalCode" || f.Name() == "runtime._GC") {
   309  					return
   310  				}
   311  			}
   312  
   313  			// Otherwise, should not see runtime.gogo.
   314  			// The place we'd see it would be the inner most frame.
   315  			f := runtime.FuncForPC(stk[0])
   316  			if f != nil && f.Name() == "runtime.gogo" {
   317  				var buf bytes.Buffer
   318  				for _, pc := range stk {
   319  					f := runtime.FuncForPC(pc)
   320  					if f == nil {
   321  						fmt.Fprintf(&buf, "%#x ?:0\n", pc)
   322  					} else {
   323  						file, line := f.FileLine(pc)
   324  						fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
   325  					}
   326  				}
   327  				t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
   328  			}
   329  		})
   330  	}
   331  }
   332  
   333  // Test that profiling of division operations is okay, especially on ARM. See issue 6681.
   334  func TestMathBigDivide(t *testing.T) {
   335  	testCPUProfile(t, nil, func(duration time.Duration) {
   336  		t := time.After(duration)
   337  		pi := new(big.Int)
   338  		for {
   339  			for i := 0; i < 100; i++ {
   340  				n := big.NewInt(2646693125139304345)
   341  				d := big.NewInt(842468587426513207)
   342  				pi.Div(n, d)
   343  			}
   344  			select {
   345  			case <-t:
   346  				return
   347  			default:
   348  			}
   349  		}
   350  	})
   351  }
   352  
   353  func slurpString(r io.Reader) string {
   354  	slurp, _ := ioutil.ReadAll(r)
   355  	return string(slurp)
   356  }
   357  
   358  func getLinuxKernelConfig() string {
   359  	if f, err := os.Open("/proc/config"); err == nil {
   360  		defer f.Close()
   361  		return slurpString(f)
   362  	}
   363  	if f, err := os.Open("/proc/config.gz"); err == nil {
   364  		defer f.Close()
   365  		r, err := gzip.NewReader(f)
   366  		if err != nil {
   367  			return ""
   368  		}
   369  		return slurpString(r)
   370  	}
   371  	if f, err := os.Open("/boot/config"); err == nil {
   372  		defer f.Close()
   373  		return slurpString(f)
   374  	}
   375  	uname, _ := exec.Command("uname", "-r").Output()
   376  	if len(uname) > 0 {
   377  		if f, err := os.Open("/boot/config-" + strings.TrimSpace(string(uname))); err == nil {
   378  			defer f.Close()
   379  			return slurpString(f)
   380  		}
   381  	}
   382  	return ""
   383  }
   384  
   385  func haveLinuxHiresTimers() bool {
   386  	config := getLinuxKernelConfig()
   387  	return strings.Contains(config, "CONFIG_HIGH_RES_TIMERS=y")
   388  }
   389  
   390  func TestStackBarrierProfiling(t *testing.T) {
   391  	if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") ||
   392  		runtime.GOOS == "openbsd" ||
   393  		runtime.GOOS == "solaris" ||
   394  		runtime.GOOS == "dragonfly" ||
   395  		runtime.GOOS == "freebsd" {
   396  		// This test currently triggers a large number of
   397  		// usleep(100)s. These kernels/arches have poor
   398  		// resolution timers, so this gives up a whole
   399  		// scheduling quantum. On Linux and the BSDs (and
   400  		// probably Solaris), profiling signals are only
   401  		// generated when a process completes a whole
   402  		// scheduling quantum, so this test often gets zero
   403  		// profiling signals and fails.
   404  		t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405)")
   405  		return
   406  	}
   407  
   408  	if runtime.GOOS == "linux" && strings.HasPrefix(runtime.GOARCH, "mips") {
   409  		if !haveLinuxHiresTimers() {
   410  			t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405, golang.org/issue/17936)")
   411  		}
   412  	}
   413  
   414  	if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") {
   415  		// Re-execute this test with constant GC and stack
   416  		// barriers at every frame.
   417  		testenv.MustHaveExec(t)
   418  		if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
   419  			t.Skip("gcstackbarrierall doesn't work on ppc64")
   420  		}
   421  		args := []string{"-test.run=TestStackBarrierProfiling"}
   422  		if testing.Short() {
   423  			args = append(args, "-test.short")
   424  		}
   425  		cmd := exec.Command(os.Args[0], args...)
   426  		cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1", "GOTRACEBACK=system"}, os.Environ()...)
   427  		if out, err := cmd.CombinedOutput(); err != nil {
   428  			t.Fatalf("subprocess failed with %v:\n%s", err, out)
   429  		}
   430  		return
   431  	}
   432  
   433  	testCPUProfile(t, nil, func(duration time.Duration) {
   434  		// In long mode, we're likely to get one or two
   435  		// samples in stackBarrier.
   436  		t := time.After(duration)
   437  		for {
   438  			deepStack(1000)
   439  			select {
   440  			case <-t:
   441  				return
   442  			default:
   443  			}
   444  		}
   445  	})
   446  }
   447  
   448  var x []byte
   449  
   450  func deepStack(depth int) int {
   451  	if depth == 0 {
   452  		return 0
   453  	}
   454  	x = make([]byte, 1024)
   455  	return deepStack(depth-1) + 1
   456  }
   457  
   458  // Operating systems that are expected to fail the tests. See issue 13841.
   459  var badOS = map[string]bool{
   460  	"darwin":    true,
   461  	"netbsd":    true,
   462  	"plan9":     true,
   463  	"dragonfly": true,
   464  	"solaris":   true,
   465  }
   466  
   467  func TestBlockProfile(t *testing.T) {
   468  	type TestCase struct {
   469  		name string
   470  		f    func()
   471  		re   string
   472  	}
   473  	tests := [...]TestCase{
   474  		{"chan recv", blockChanRecv, `
   475  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   476  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   477  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   478  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   479  `},
   480  		{"chan send", blockChanSend, `
   481  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   482  #	0x[0-9,a-f]+	runtime\.chansend1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   483  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   484  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   485  `},
   486  		{"chan close", blockChanClose, `
   487  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   488  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   489  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   490  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   491  `},
   492  		{"select recv async", blockSelectRecvAsync, `
   493  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   494  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
   495  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   496  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   497  `},
   498  		{"select send sync", blockSelectSendSync, `
   499  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   500  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
   501  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   502  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   503  `},
   504  		{"mutex", blockMutex, `
   505  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   506  #	0x[0-9,a-f]+	sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+	.*/src/sync/mutex\.go:[0-9]+
   507  #	0x[0-9,a-f]+	runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   508  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   509  `},
   510  		{"cond", blockCond, `
   511  [0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
   512  #	0x[0-9,a-f]+	sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+	.*/src/sync/cond\.go:[0-9]+
   513  #	0x[0-9,a-f]+	runtime/pprof_test\.blockCond\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   514  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   515  `},
   516  	}
   517  
   518  	runtime.SetBlockProfileRate(1)
   519  	defer runtime.SetBlockProfileRate(0)
   520  	for _, test := range tests {
   521  		test.f()
   522  	}
   523  	var w bytes.Buffer
   524  	Lookup("block").WriteTo(&w, 1)
   525  	prof := w.String()
   526  
   527  	if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
   528  		t.Fatalf("Bad profile header:\n%v", prof)
   529  	}
   530  
   531  	if strings.HasSuffix(prof, "#\t0x0\n\n") {
   532  		t.Errorf("Useless 0 suffix:\n%v", prof)
   533  	}
   534  
   535  	for _, test := range tests {
   536  		if !regexp.MustCompile(strings.Replace(test.re, "\t", "\t+", -1)).MatchString(prof) {
   537  			t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
   538  		}
   539  	}
   540  }
   541  
   542  const blockDelay = 10 * time.Millisecond
   543  
   544  func blockChanRecv() {
   545  	c := make(chan bool)
   546  	go func() {
   547  		time.Sleep(blockDelay)
   548  		c <- true
   549  	}()
   550  	<-c
   551  }
   552  
   553  func blockChanSend() {
   554  	c := make(chan bool)
   555  	go func() {
   556  		time.Sleep(blockDelay)
   557  		<-c
   558  	}()
   559  	c <- true
   560  }
   561  
   562  func blockChanClose() {
   563  	c := make(chan bool)
   564  	go func() {
   565  		time.Sleep(blockDelay)
   566  		close(c)
   567  	}()
   568  	<-c
   569  }
   570  
   571  func blockSelectRecvAsync() {
   572  	const numTries = 3
   573  	c := make(chan bool, 1)
   574  	c2 := make(chan bool, 1)
   575  	go func() {
   576  		for i := 0; i < numTries; i++ {
   577  			time.Sleep(blockDelay)
   578  			c <- true
   579  		}
   580  	}()
   581  	for i := 0; i < numTries; i++ {
   582  		select {
   583  		case <-c:
   584  		case <-c2:
   585  		}
   586  	}
   587  }
   588  
   589  func blockSelectSendSync() {
   590  	c := make(chan bool)
   591  	c2 := make(chan bool)
   592  	go func() {
   593  		time.Sleep(blockDelay)
   594  		<-c
   595  	}()
   596  	select {
   597  	case c <- true:
   598  	case c2 <- true:
   599  	}
   600  }
   601  
   602  func blockMutex() {
   603  	var mu sync.Mutex
   604  	mu.Lock()
   605  	go func() {
   606  		time.Sleep(blockDelay)
   607  		mu.Unlock()
   608  	}()
   609  	mu.Lock()
   610  }
   611  
   612  func blockCond() {
   613  	var mu sync.Mutex
   614  	c := sync.NewCond(&mu)
   615  	mu.Lock()
   616  	go func() {
   617  		time.Sleep(blockDelay)
   618  		mu.Lock()
   619  		c.Signal()
   620  		mu.Unlock()
   621  	}()
   622  	c.Wait()
   623  	mu.Unlock()
   624  }
   625  
   626  func TestMutexProfile(t *testing.T) {
   627  	old := runtime.SetMutexProfileFraction(1)
   628  	defer runtime.SetMutexProfileFraction(old)
   629  	if old != 0 {
   630  		t.Fatalf("need MutexProfileRate 0, got %d", old)
   631  	}
   632  
   633  	blockMutex()
   634  
   635  	var w bytes.Buffer
   636  	Lookup("mutex").WriteTo(&w, 1)
   637  	prof := w.String()
   638  
   639  	if !strings.HasPrefix(prof, "--- mutex:\ncycles/second=") {
   640  		t.Errorf("Bad profile header:\n%v", prof)
   641  	}
   642  	prof = strings.Trim(prof, "\n")
   643  	lines := strings.Split(prof, "\n")
   644  	if len(lines) != 6 {
   645  		t.Errorf("expected 6 lines, got %d %q\n%s", len(lines), prof, prof)
   646  	}
   647  	if len(lines) < 6 {
   648  		return
   649  	}
   650  	// checking that the line is like "35258904 1 @ 0x48288d 0x47cd28 0x458931"
   651  	r2 := `^\d+ 1 @(?: 0x[[:xdigit:]]+)+`
   652  	//r2 := "^[0-9]+ 1 @ 0x[0-9a-f x]+$"
   653  	if ok, err := regexp.MatchString(r2, lines[3]); err != nil || !ok {
   654  		t.Errorf("%q didn't match %q", lines[3], r2)
   655  	}
   656  	r3 := "^#.*runtime/pprof_test.blockMutex.*$"
   657  	if ok, err := regexp.MatchString(r3, lines[5]); err != nil || !ok {
   658  		t.Errorf("%q didn't match %q", lines[5], r3)
   659  	}
   660  }
   661  
   662  func func1(c chan int) { <-c }
   663  func func2(c chan int) { <-c }
   664  func func3(c chan int) { <-c }
   665  func func4(c chan int) { <-c }
   666  
   667  func TestGoroutineCounts(t *testing.T) {
   668  	if runtime.GOOS == "openbsd" {
   669  		testenv.SkipFlaky(t, 15156)
   670  	}
   671  	c := make(chan int)
   672  	for i := 0; i < 100; i++ {
   673  		if i%10 == 0 {
   674  			go func1(c)
   675  			continue
   676  		}
   677  		if i%2 == 0 {
   678  			go func2(c)
   679  			continue
   680  		}
   681  		go func3(c)
   682  	}
   683  	time.Sleep(10 * time.Millisecond) // let goroutines block on channel
   684  
   685  	var w bytes.Buffer
   686  	goroutineProf := Lookup("goroutine")
   687  
   688  	// Check debug profile
   689  	goroutineProf.WriteTo(&w, 1)
   690  	prof := w.String()
   691  
   692  	if !containsInOrder(prof, "\n50 @ ", "\n40 @", "\n10 @", "\n1 @") {
   693  		t.Errorf("expected sorted goroutine counts:\n%s", prof)
   694  	}
   695  
   696  	// Check proto profile
   697  	w.Reset()
   698  	goroutineProf.WriteTo(&w, 0)
   699  	p, err := profile.Parse(&w)
   700  	if err != nil {
   701  		t.Errorf("error parsing protobuf profile: %v", err)
   702  	}
   703  	if err := p.CheckValid(); err != nil {
   704  		t.Errorf("protobuf profile is invalid: %v", err)
   705  	}
   706  	if !containsCounts(p, []int64{50, 40, 10, 1}) {
   707  		t.Errorf("expected count profile to contain goroutines with counts %v, got %v",
   708  			[]int64{50, 40, 10, 1}, p)
   709  	}
   710  
   711  	close(c)
   712  
   713  	time.Sleep(10 * time.Millisecond) // let goroutines exit
   714  }
   715  
   716  func containsInOrder(s string, all ...string) bool {
   717  	for _, t := range all {
   718  		i := strings.Index(s, t)
   719  		if i < 0 {
   720  			return false
   721  		}
   722  		s = s[i+len(t):]
   723  	}
   724  	return true
   725  }
   726  
   727  func containsCounts(prof *profile.Profile, counts []int64) bool {
   728  	m := make(map[int64]int)
   729  	for _, c := range counts {
   730  		m[c]++
   731  	}
   732  	for _, s := range prof.Sample {
   733  		// The count is the single value in the sample
   734  		if len(s.Value) != 1 {
   735  			return false
   736  		}
   737  		m[s.Value[0]]--
   738  	}
   739  	for _, n := range m {
   740  		if n > 0 {
   741  			return false
   742  		}
   743  	}
   744  	return true
   745  }