github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/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  	"math/big"
    13  	"os/exec"
    14  	"regexp"
    15  	"runtime"
    16  	. "runtime/pprof"
    17  	"strings"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  	"unsafe"
    22  )
    23  
    24  func cpuHogger(f func()) {
    25  	// We only need to get one 100 Hz clock tick, so we've got
    26  	// a 25x safety buffer.
    27  	// But do at least 500 iterations (which should take about 100ms),
    28  	// otherwise TestCPUProfileMultithreaded can fail if only one
    29  	// thread is scheduled during the 250ms period.
    30  	t0 := time.Now()
    31  	for i := 0; i < 500 || time.Since(t0) < 250*time.Millisecond; i++ {
    32  		f()
    33  	}
    34  }
    35  
    36  var (
    37  	salt1 = 0
    38  	salt2 = 0
    39  )
    40  
    41  // The actual CPU hogging function.
    42  // Must not call other functions nor access heap/globals in the loop,
    43  // otherwise under race detector the samples will be in the race runtime.
    44  func cpuHog1() {
    45  	foo := salt1
    46  	for i := 0; i < 1e5; i++ {
    47  		if foo > 0 {
    48  			foo *= foo
    49  		} else {
    50  			foo *= foo + 1
    51  		}
    52  	}
    53  	salt1 = foo
    54  }
    55  
    56  func cpuHog2() {
    57  	foo := salt2
    58  	for i := 0; i < 1e5; i++ {
    59  		if foo > 0 {
    60  			foo *= foo
    61  		} else {
    62  			foo *= foo + 2
    63  		}
    64  	}
    65  	salt2 = foo
    66  }
    67  
    68  func TestCPUProfile(t *testing.T) {
    69  	testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1"}, func() {
    70  		cpuHogger(cpuHog1)
    71  	})
    72  }
    73  
    74  func TestCPUProfileMultithreaded(t *testing.T) {
    75  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
    76  	testCPUProfile(t, []string{"runtime/pprof_test.cpuHog1", "runtime/pprof_test.cpuHog2"}, func() {
    77  		c := make(chan int)
    78  		go func() {
    79  			cpuHogger(cpuHog1)
    80  			c <- 1
    81  		}()
    82  		cpuHogger(cpuHog2)
    83  		<-c
    84  	})
    85  }
    86  
    87  func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
    88  	// Convert []byte to []uintptr.
    89  	l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
    90  	val := *(*[]uintptr)(unsafe.Pointer(&bytes))
    91  	val = val[:l]
    92  
    93  	// 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
    94  	if l < 5+2+3 {
    95  		t.Logf("profile too short: %#x", val)
    96  		if badOS[runtime.GOOS] {
    97  			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
    98  			return
    99  		}
   100  		t.FailNow()
   101  	}
   102  
   103  	hd, val, tl := val[:5], val[5:l-3], val[l-3:]
   104  	if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
   105  		t.Fatalf("unexpected header %#x", hd)
   106  	}
   107  
   108  	if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
   109  		t.Fatalf("malformed end-of-data marker %#x", tl)
   110  	}
   111  
   112  	for len(val) > 0 {
   113  		if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
   114  			t.Fatalf("malformed profile.  leftover: %#x", val)
   115  		}
   116  		f(val[0], val[2:2+val[1]])
   117  		val = val[2+val[1]:]
   118  	}
   119  }
   120  
   121  func testCPUProfile(t *testing.T, need []string, f func()) {
   122  	switch runtime.GOOS {
   123  	case "darwin":
   124  		out, err := exec.Command("uname", "-a").CombinedOutput()
   125  		if err != nil {
   126  			t.Fatal(err)
   127  		}
   128  		vers := string(out)
   129  		t.Logf("uname -a: %v", vers)
   130  	case "plan9":
   131  		// unimplemented
   132  		return
   133  	}
   134  
   135  	var prof bytes.Buffer
   136  	if err := StartCPUProfile(&prof); err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	f()
   140  	StopCPUProfile()
   141  
   142  	// Check that profile is well formed and contains need.
   143  	have := make([]uintptr, len(need))
   144  	parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
   145  		for _, pc := range stk {
   146  			f := runtime.FuncForPC(pc)
   147  			if f == nil {
   148  				continue
   149  			}
   150  			for i, name := range need {
   151  				if strings.Contains(f.Name(), name) {
   152  					have[i] += count
   153  				}
   154  			}
   155  		}
   156  	})
   157  
   158  	if len(need) == 0 {
   159  		return
   160  	}
   161  
   162  	var total uintptr
   163  	for i, name := range need {
   164  		total += have[i]
   165  		t.Logf("%s: %d\n", name, have[i])
   166  	}
   167  	ok := true
   168  	if total == 0 {
   169  		t.Logf("no CPU profile samples collected")
   170  		ok = false
   171  	}
   172  	// We'd like to check a reasonable minimum, like
   173  	// total / len(have) / smallconstant, but this test is
   174  	// pretty flaky (see bug 7095).  So we'll just test to
   175  	// make sure we got at least one sample.
   176  	min := uintptr(1)
   177  	for i, name := range need {
   178  		if have[i] < min {
   179  			t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
   180  			ok = false
   181  		}
   182  	}
   183  
   184  	if !ok {
   185  		if badOS[runtime.GOOS] {
   186  			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
   187  			return
   188  		}
   189  		t.FailNow()
   190  	}
   191  }
   192  
   193  func TestCPUProfileWithFork(t *testing.T) {
   194  	// Fork can hang if preempted with signals frequently enough (see issue 5517).
   195  	// Ensure that we do not do this.
   196  	heap := 1 << 30
   197  	if testing.Short() {
   198  		heap = 100 << 20
   199  	}
   200  	// This makes fork slower.
   201  	garbage := make([]byte, heap)
   202  	// Need to touch the slice, otherwise it won't be paged in.
   203  	done := make(chan bool)
   204  	go func() {
   205  		for i := range garbage {
   206  			garbage[i] = 42
   207  		}
   208  		done <- true
   209  	}()
   210  	<-done
   211  
   212  	var prof bytes.Buffer
   213  	if err := StartCPUProfile(&prof); err != nil {
   214  		t.Fatal(err)
   215  	}
   216  	defer StopCPUProfile()
   217  
   218  	for i := 0; i < 10; i++ {
   219  		exec.Command("go").CombinedOutput()
   220  	}
   221  }
   222  
   223  // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
   224  // If it did, it would see inconsistent state and would either record an incorrect stack
   225  // or crash because the stack was malformed.
   226  func TestGoroutineSwitch(t *testing.T) {
   227  	// How much to try. These defaults take about 1 seconds
   228  	// on a 2012 MacBook Pro. The ones in short mode take
   229  	// about 0.1 seconds.
   230  	tries := 10
   231  	count := 1000000
   232  	if testing.Short() {
   233  		tries = 1
   234  	}
   235  	for try := 0; try < tries; try++ {
   236  		var prof bytes.Buffer
   237  		if err := StartCPUProfile(&prof); err != nil {
   238  			t.Fatal(err)
   239  		}
   240  		for i := 0; i < count; i++ {
   241  			runtime.Gosched()
   242  		}
   243  		StopCPUProfile()
   244  
   245  		// Read profile to look for entries for runtime.gogo with an attempt at a traceback.
   246  		// The special entry
   247  		parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
   248  			// An entry with two frames with 'System' in its top frame
   249  			// exists to record a PC without a traceback. Those are okay.
   250  			if len(stk) == 2 {
   251  				f := runtime.FuncForPC(stk[1])
   252  				if f != nil && (f.Name() == "runtime._System" || f.Name() == "runtime._ExternalCode" || f.Name() == "runtime._GC") {
   253  					return
   254  				}
   255  			}
   256  
   257  			// Otherwise, should not see runtime.gogo.
   258  			// The place we'd see it would be the inner most frame.
   259  			f := runtime.FuncForPC(stk[0])
   260  			if f != nil && f.Name() == "runtime.gogo" {
   261  				var buf bytes.Buffer
   262  				for _, pc := range stk {
   263  					f := runtime.FuncForPC(pc)
   264  					if f == nil {
   265  						fmt.Fprintf(&buf, "%#x ?:0\n", pc)
   266  					} else {
   267  						file, line := f.FileLine(pc)
   268  						fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
   269  					}
   270  				}
   271  				t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
   272  			}
   273  		})
   274  	}
   275  }
   276  
   277  // Test that profiling of division operations is okay, especially on ARM. See issue 6681.
   278  func TestMathBigDivide(t *testing.T) {
   279  	testCPUProfile(t, nil, func() {
   280  		t := time.After(5 * time.Second)
   281  		pi := new(big.Int)
   282  		for {
   283  			for i := 0; i < 100; i++ {
   284  				n := big.NewInt(2646693125139304345)
   285  				d := big.NewInt(842468587426513207)
   286  				pi.Div(n, d)
   287  			}
   288  			select {
   289  			case <-t:
   290  				return
   291  			default:
   292  			}
   293  		}
   294  	})
   295  }
   296  
   297  // Operating systems that are expected to fail the tests. See issue 6047.
   298  var badOS = map[string]bool{
   299  	"darwin": true,
   300  	"netbsd": true,
   301  	"plan9":  true,
   302  }
   303  
   304  func TestBlockProfile(t *testing.T) {
   305  	type TestCase struct {
   306  		name string
   307  		f    func()
   308  		re   string
   309  	}
   310  	tests := [...]TestCase{
   311  		{"chan recv", blockChanRecv, `
   312  [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]+
   313  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   314  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   315  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   316  `},
   317  		{"chan send", blockChanSend, `
   318  [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]+
   319  #	0x[0-9,a-f]+	runtime\.chansend1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   320  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   321  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   322  `},
   323  		{"chan close", blockChanClose, `
   324  [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]+
   325  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
   326  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   327  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   328  `},
   329  		{"select recv async", blockSelectRecvAsync, `
   330  [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]+
   331  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
   332  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   333  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   334  `},
   335  		{"select send sync", blockSelectSendSync, `
   336  [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]+
   337  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
   338  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   339  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   340  `},
   341  		{"mutex", blockMutex, `
   342  [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]+
   343  #	0x[0-9,a-f]+	sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+	.*/src/sync/mutex\.go:[0-9]+
   344  #	0x[0-9,a-f]+	runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   345  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   346  `},
   347  		{"cond", blockCond, `
   348  [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]+
   349  #	0x[0-9,a-f]+	sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+	.*/src/sync/cond\.go:[0-9]+
   350  #	0x[0-9,a-f]+	runtime/pprof_test\.blockCond\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   351  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
   352  `},
   353  	}
   354  
   355  	runtime.SetBlockProfileRate(1)
   356  	defer runtime.SetBlockProfileRate(0)
   357  	for _, test := range tests {
   358  		test.f()
   359  	}
   360  	var w bytes.Buffer
   361  	Lookup("block").WriteTo(&w, 1)
   362  	prof := w.String()
   363  
   364  	if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
   365  		t.Fatalf("Bad profile header:\n%v", prof)
   366  	}
   367  
   368  	for _, test := range tests {
   369  		if !regexp.MustCompile(test.re).MatchString(prof) {
   370  			t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
   371  		}
   372  	}
   373  }
   374  
   375  const blockDelay = 10 * time.Millisecond
   376  
   377  func blockChanRecv() {
   378  	c := make(chan bool)
   379  	go func() {
   380  		time.Sleep(blockDelay)
   381  		c <- true
   382  	}()
   383  	<-c
   384  }
   385  
   386  func blockChanSend() {
   387  	c := make(chan bool)
   388  	go func() {
   389  		time.Sleep(blockDelay)
   390  		<-c
   391  	}()
   392  	c <- true
   393  }
   394  
   395  func blockChanClose() {
   396  	c := make(chan bool)
   397  	go func() {
   398  		time.Sleep(blockDelay)
   399  		close(c)
   400  	}()
   401  	<-c
   402  }
   403  
   404  func blockSelectRecvAsync() {
   405  	c := make(chan bool, 1)
   406  	c2 := make(chan bool, 1)
   407  	go func() {
   408  		time.Sleep(blockDelay)
   409  		c <- true
   410  	}()
   411  	select {
   412  	case <-c:
   413  	case <-c2:
   414  	}
   415  }
   416  
   417  func blockSelectSendSync() {
   418  	c := make(chan bool)
   419  	c2 := make(chan bool)
   420  	go func() {
   421  		time.Sleep(blockDelay)
   422  		<-c
   423  	}()
   424  	select {
   425  	case c <- true:
   426  	case c2 <- true:
   427  	}
   428  }
   429  
   430  func blockMutex() {
   431  	var mu sync.Mutex
   432  	mu.Lock()
   433  	go func() {
   434  		time.Sleep(blockDelay)
   435  		mu.Unlock()
   436  	}()
   437  	mu.Lock()
   438  }
   439  
   440  func blockCond() {
   441  	var mu sync.Mutex
   442  	c := sync.NewCond(&mu)
   443  	mu.Lock()
   444  	go func() {
   445  		time.Sleep(blockDelay)
   446  		mu.Lock()
   447  		c.Signal()
   448  		mu.Unlock()
   449  	}()
   450  	c.Wait()
   451  	mu.Unlock()
   452  }