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