github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/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  package pprof_test
     6  
     7  import (
     8  	"bytes"
     9  	"hash/crc32"
    10  	"os/exec"
    11  	"regexp"
    12  	"runtime"
    13  	. "runtime/pprof"
    14  	"strings"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  	"unsafe"
    19  )
    20  
    21  func TestCPUProfile(t *testing.T) {
    22  	buf := make([]byte, 100000)
    23  	testCPUProfile(t, []string{"crc32.ChecksumIEEE"}, func() {
    24  		// This loop takes about a quarter second on a 2 GHz laptop.
    25  		// We only need to get one 100 Hz clock tick, so we've got
    26  		// a 25x safety buffer.
    27  		for i := 0; i < 1000; i++ {
    28  			crc32.ChecksumIEEE(buf)
    29  		}
    30  	})
    31  }
    32  
    33  func TestCPUProfileMultithreaded(t *testing.T) {
    34  	buf := make([]byte, 100000)
    35  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
    36  	testCPUProfile(t, []string{"crc32.ChecksumIEEE", "crc32.Update"}, func() {
    37  		c := make(chan int)
    38  		go func() {
    39  			for i := 0; i < 2000; i++ {
    40  				crc32.Update(0, crc32.IEEETable, buf)
    41  			}
    42  			c <- 1
    43  		}()
    44  		// This loop takes about a quarter second on a 2 GHz laptop.
    45  		// We only need to get one 100 Hz clock tick, so we've got
    46  		// a 25x safety buffer.
    47  		for i := 0; i < 2000; i++ {
    48  			crc32.ChecksumIEEE(buf)
    49  		}
    50  		<-c
    51  	})
    52  }
    53  
    54  func testCPUProfile(t *testing.T, need []string, f func()) {
    55  	switch runtime.GOOS {
    56  	case "darwin":
    57  		out, err := exec.Command("uname", "-a").CombinedOutput()
    58  		if err != nil {
    59  			t.Fatal(err)
    60  		}
    61  		vers := string(out)
    62  		t.Logf("uname -a: %v", vers)
    63  	case "plan9":
    64  		// unimplemented
    65  		return
    66  	}
    67  
    68  	var prof bytes.Buffer
    69  	if err := StartCPUProfile(&prof); err != nil {
    70  		t.Fatal(err)
    71  	}
    72  	f()
    73  	StopCPUProfile()
    74  
    75  	// Convert []byte to []uintptr.
    76  	bytes := prof.Bytes()
    77  	l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
    78  	val := *(*[]uintptr)(unsafe.Pointer(&bytes))
    79  	val = val[:l]
    80  
    81  	if l < 13 {
    82  		t.Logf("profile too short: %#x", val)
    83  		if badOS[runtime.GOOS] {
    84  			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
    85  			return
    86  		}
    87  		t.FailNow()
    88  	}
    89  
    90  	hd, val, tl := val[:5], val[5:l-3], val[l-3:]
    91  	if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
    92  		t.Fatalf("unexpected header %#x", hd)
    93  	}
    94  
    95  	if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
    96  		t.Fatalf("malformed end-of-data marker %#x", tl)
    97  	}
    98  
    99  	// Check that profile is well formed and contains ChecksumIEEE.
   100  	have := make([]uintptr, len(need))
   101  	for len(val) > 0 {
   102  		if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
   103  			t.Fatalf("malformed profile.  leftover: %#x", val)
   104  		}
   105  		for _, pc := range val[2 : 2+val[1]] {
   106  			f := runtime.FuncForPC(pc)
   107  			if f == nil {
   108  				continue
   109  			}
   110  			for i, name := range need {
   111  				if strings.Contains(f.Name(), name) {
   112  					have[i] += val[0]
   113  				}
   114  			}
   115  		}
   116  		val = val[2+val[1]:]
   117  	}
   118  
   119  	var total uintptr
   120  	for i, name := range need {
   121  		total += have[i]
   122  		t.Logf("%s: %d\n", name, have[i])
   123  	}
   124  	ok := true
   125  	if total == 0 {
   126  		t.Logf("no CPU profile samples collected")
   127  		ok = false
   128  	}
   129  	min := total / uintptr(len(have)) / 3
   130  	for i, name := range need {
   131  		if have[i] < min {
   132  			t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
   133  			ok = false
   134  		}
   135  	}
   136  
   137  	if !ok {
   138  		if badOS[runtime.GOOS] {
   139  			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
   140  			return
   141  		}
   142  		t.FailNow()
   143  	}
   144  }
   145  
   146  func TestCPUProfileWithFork(t *testing.T) {
   147  	// Fork can hang if preempted with signals frequently enough (see issue 5517).
   148  	// Ensure that we do not do this.
   149  	heap := 1 << 30
   150  	if testing.Short() {
   151  		heap = 100 << 20
   152  	}
   153  	// This makes fork slower.
   154  	garbage := make([]byte, heap)
   155  	// Need to touch the slice, otherwise it won't be paged in.
   156  	done := make(chan bool)
   157  	go func() {
   158  		for i := range garbage {
   159  			garbage[i] = 42
   160  		}
   161  		done <- true
   162  	}()
   163  	<-done
   164  
   165  	var prof bytes.Buffer
   166  	if err := StartCPUProfile(&prof); err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	defer StopCPUProfile()
   170  
   171  	for i := 0; i < 10; i++ {
   172  		exec.Command("go").CombinedOutput()
   173  	}
   174  }
   175  
   176  // Operating systems that are expected to fail the tests. See issue 6047.
   177  var badOS = map[string]bool{
   178  	"darwin":  true,
   179  	"netbsd":  true,
   180  	"openbsd": true,
   181  }
   182  
   183  func TestBlockProfile(t *testing.T) {
   184  	type TestCase struct {
   185  		name string
   186  		f    func()
   187  		re   string
   188  	}
   189  	tests := [...]TestCase{
   190  		{"chan recv", blockChanRecv, `
   191  [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]+
   192  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/pkg/runtime/chan.c:[0-9]+
   193  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   194  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   195  `},
   196  		{"chan send", blockChanSend, `
   197  [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]+
   198  #	0x[0-9,a-f]+	runtime\.chansend1\+0x[0-9,a-f]+	.*/src/pkg/runtime/chan.c:[0-9]+
   199  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   200  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   201  `},
   202  		{"chan close", blockChanClose, `
   203  [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]+
   204  #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/pkg/runtime/chan.c:[0-9]+
   205  #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   206  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   207  `},
   208  		{"select recv async", blockSelectRecvAsync, `
   209  [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]+
   210  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/pkg/runtime/chan.c:[0-9]+
   211  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   212  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   213  `},
   214  		{"select send sync", blockSelectSendSync, `
   215  [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]+
   216  #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/pkg/runtime/chan.c:[0-9]+
   217  #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   218  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   219  `},
   220  		{"mutex", blockMutex, `
   221  [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]+
   222  #	0x[0-9,a-f]+	sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+	.*/src/pkg/sync/mutex\.go:[0-9]+
   223  #	0x[0-9,a-f]+	runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   224  #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+
   225  `},
   226  	}
   227  
   228  	runtime.SetBlockProfileRate(1)
   229  	defer runtime.SetBlockProfileRate(0)
   230  	for _, test := range tests {
   231  		test.f()
   232  	}
   233  	var w bytes.Buffer
   234  	Lookup("block").WriteTo(&w, 1)
   235  	prof := w.String()
   236  
   237  	if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
   238  		t.Fatalf("Bad profile header:\n%v", prof)
   239  	}
   240  
   241  	for _, test := range tests {
   242  		if !regexp.MustCompile(test.re).MatchString(prof) {
   243  			t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
   244  		}
   245  	}
   246  }
   247  
   248  const blockDelay = 10 * time.Millisecond
   249  
   250  func blockChanRecv() {
   251  	c := make(chan bool)
   252  	go func() {
   253  		time.Sleep(blockDelay)
   254  		c <- true
   255  	}()
   256  	<-c
   257  }
   258  
   259  func blockChanSend() {
   260  	c := make(chan bool)
   261  	go func() {
   262  		time.Sleep(blockDelay)
   263  		<-c
   264  	}()
   265  	c <- true
   266  }
   267  
   268  func blockChanClose() {
   269  	c := make(chan bool)
   270  	go func() {
   271  		time.Sleep(blockDelay)
   272  		close(c)
   273  	}()
   274  	<-c
   275  }
   276  
   277  func blockSelectRecvAsync() {
   278  	c := make(chan bool, 1)
   279  	c2 := make(chan bool, 1)
   280  	go func() {
   281  		time.Sleep(blockDelay)
   282  		c <- true
   283  	}()
   284  	select {
   285  	case <-c:
   286  	case <-c2:
   287  	}
   288  }
   289  
   290  func blockSelectSendSync() {
   291  	c := make(chan bool)
   292  	c2 := make(chan bool)
   293  	go func() {
   294  		time.Sleep(blockDelay)
   295  		<-c
   296  	}()
   297  	select {
   298  	case c <- true:
   299  	case c2 <- true:
   300  	}
   301  }
   302  
   303  func blockMutex() {
   304  	var mu sync.Mutex
   305  	mu.Lock()
   306  	go func() {
   307  		time.Sleep(blockDelay)
   308  		mu.Unlock()
   309  	}()
   310  	mu.Lock()
   311  }