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