Home | History | Annotate | Download | only in pprof
      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()) {
     27 	// We only need to get one 100 Hz clock tick, so we've got
     28 	// a 25x 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 250ms period.
     32 	t0 := time.Now()
     33 	for i := 0; i < 500 || time.Since(t0) < 250*time.Millisecond; 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() {
     72 		cpuHogger(cpuHog1)
     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() {
     79 		c := make(chan int)
     80 		go func() {
     81 			cpuHogger(cpuHog1)
     82 			c <- 1
     83 		}()
     84 		cpuHogger(cpuHog2)
     85 		<-c
     86 	})
     87 }
     88 
     89 func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) {
     90 	// Convert []byte to []uintptr.
     91 	l := len(bytes) / int(unsafe.Sizeof(uintptr(0)))
     92 	val := *(*[]uintptr)(unsafe.Pointer(&bytes))
     93 	val = val[:l]
     94 
     95 	// 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer.
     96 	if l < 5+2+3 {
     97 		t.Logf("profile too short: %#x", val)
     98 		if badOS[runtime.GOOS] {
     99 			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
    100 			return
    101 		}
    102 		t.FailNow()
    103 	}
    104 
    105 	hd, val, tl := val[:5], val[5:l-3], val[l-3:]
    106 	if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 {
    107 		t.Fatalf("unexpected header %#x", hd)
    108 	}
    109 
    110 	if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 {
    111 		t.Fatalf("malformed end-of-data marker %#x", tl)
    112 	}
    113 
    114 	for len(val) > 0 {
    115 		if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] {
    116 			t.Fatalf("malformed profile.  leftover: %#x", val)
    117 		}
    118 		f(val[0], val[2:2+val[1]])
    119 		val = val[2+val[1]:]
    120 	}
    121 }
    122 
    123 func testCPUProfile(t *testing.T, need []string, f func()) {
    124 	switch runtime.GOOS {
    125 	case "darwin":
    126 		switch runtime.GOARCH {
    127 		case "arm", "arm64":
    128 			// nothing
    129 		default:
    130 			out, err := exec.Command("uname", "-a").CombinedOutput()
    131 			if err != nil {
    132 				t.Fatal(err)
    133 			}
    134 			vers := string(out)
    135 			t.Logf("uname -a: %v", vers)
    136 		}
    137 	case "plan9":
    138 		t.Skip("skipping on plan9")
    139 	}
    140 
    141 	var prof bytes.Buffer
    142 	if err := StartCPUProfile(&prof); err != nil {
    143 		t.Fatal(err)
    144 	}
    145 	f()
    146 	StopCPUProfile()
    147 
    148 	// Check that profile is well formed and contains need.
    149 	have := make([]uintptr, len(need))
    150 	var samples uintptr
    151 	parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
    152 		samples += count
    153 		for _, pc := range stk {
    154 			f := runtime.FuncForPC(pc)
    155 			if f == nil {
    156 				continue
    157 			}
    158 			for i, name := range need {
    159 				if strings.Contains(f.Name(), name) {
    160 					have[i] += count
    161 				}
    162 			}
    163 		}
    164 	})
    165 	t.Logf("total %d CPU profile samples collected", samples)
    166 
    167 	if samples < 10 && runtime.GOOS == "windows" {
    168 		// On some windows machines we end up with
    169 		// not enough samples due to coarse timer
    170 		// resolution. Let it go.
    171 		t.Skip("too few samples on Windows (golang.org/issue/10842)")
    172 	}
    173 
    174 	if len(need) == 0 {
    175 		return
    176 	}
    177 
    178 	var total uintptr
    179 	for i, name := range need {
    180 		total += have[i]
    181 		t.Logf("%s: %d\n", name, have[i])
    182 	}
    183 	ok := true
    184 	if total == 0 {
    185 		t.Logf("no CPU profile samples collected")
    186 		ok = false
    187 	}
    188 	// We'd like to check a reasonable minimum, like
    189 	// total / len(have) / smallconstant, but this test is
    190 	// pretty flaky (see bug 7095).  So we'll just test to
    191 	// make sure we got at least one sample.
    192 	min := uintptr(1)
    193 	for i, name := range need {
    194 		if have[i] < min {
    195 			t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have)))
    196 			ok = false
    197 		}
    198 	}
    199 
    200 	if !ok {
    201 		if badOS[runtime.GOOS] {
    202 			t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS)
    203 			return
    204 		}
    205 		// Ignore the failure if the tests are running in a QEMU-based emulator,
    206 		// QEMU is not perfect at emulating everything.
    207 		// IN_QEMU environmental variable is set by some of the Go builders.
    208 		// IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
    209 		if os.Getenv("IN_QEMU") == "1" {
    210 			t.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
    211 			return
    212 		}
    213 		t.FailNow()
    214 	}
    215 }
    216 
    217 // Fork can hang if preempted with signals frequently enough (see issue 5517).
    218 // Ensure that we do not do this.
    219 func TestCPUProfileWithFork(t *testing.T) {
    220 	testenv.MustHaveExec(t)
    221 
    222 	heap := 1 << 30
    223 	if runtime.GOOS == "android" {
    224 		// Use smaller size for Android to avoid crash.
    225 		heap = 100 << 20
    226 	}
    227 	if testing.Short() {
    228 		heap = 100 << 20
    229 	}
    230 	// This makes fork slower.
    231 	garbage := make([]byte, heap)
    232 	// Need to touch the slice, otherwise it won't be paged in.
    233 	done := make(chan bool)
    234 	go func() {
    235 		for i := range garbage {
    236 			garbage[i] = 42
    237 		}
    238 		done <- true
    239 	}()
    240 	<-done
    241 
    242 	var prof bytes.Buffer
    243 	if err := StartCPUProfile(&prof); err != nil {
    244 		t.Fatal(err)
    245 	}
    246 	defer StopCPUProfile()
    247 
    248 	for i := 0; i < 10; i++ {
    249 		exec.Command(os.Args[0], "-h").CombinedOutput()
    250 	}
    251 }
    252 
    253 // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
    254 // If it did, it would see inconsistent state and would either record an incorrect stack
    255 // or crash because the stack was malformed.
    256 func TestGoroutineSwitch(t *testing.T) {
    257 	// How much to try. These defaults take about 1 seconds
    258 	// on a 2012 MacBook Pro. The ones in short mode take
    259 	// about 0.1 seconds.
    260 	tries := 10
    261 	count := 1000000
    262 	if testing.Short() {
    263 		tries = 1
    264 	}
    265 	for try := 0; try < tries; try++ {
    266 		var prof bytes.Buffer
    267 		if err := StartCPUProfile(&prof); err != nil {
    268 			t.Fatal(err)
    269 		}
    270 		for i := 0; i < count; i++ {
    271 			runtime.Gosched()
    272 		}
    273 		StopCPUProfile()
    274 
    275 		// Read profile to look for entries for runtime.gogo with an attempt at a traceback.
    276 		// The special entry
    277 		parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) {
    278 			// An entry with two frames with 'System' in its top frame
    279 			// exists to record a PC without a traceback. Those are okay.
    280 			if len(stk) == 2 {
    281 				f := runtime.FuncForPC(stk[1])
    282 				if f != nil && (f.Name() == "runtime._System" || f.Name() == "runtime._ExternalCode" || f.Name() == "runtime._GC") {
    283 					return
    284 				}
    285 			}
    286 
    287 			// Otherwise, should not see runtime.gogo.
    288 			// The place we'd see it would be the inner most frame.
    289 			f := runtime.FuncForPC(stk[0])
    290 			if f != nil && f.Name() == "runtime.gogo" {
    291 				var buf bytes.Buffer
    292 				for _, pc := range stk {
    293 					f := runtime.FuncForPC(pc)
    294 					if f == nil {
    295 						fmt.Fprintf(&buf, "%#x ?:0\n", pc)
    296 					} else {
    297 						file, line := f.FileLine(pc)
    298 						fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line)
    299 					}
    300 				}
    301 				t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String())
    302 			}
    303 		})
    304 	}
    305 }
    306 
    307 // Test that profiling of division operations is okay, especially on ARM. See issue 6681.
    308 func TestMathBigDivide(t *testing.T) {
    309 	testCPUProfile(t, nil, func() {
    310 		t := time.After(5 * time.Second)
    311 		pi := new(big.Int)
    312 		for {
    313 			for i := 0; i < 100; i++ {
    314 				n := big.NewInt(2646693125139304345)
    315 				d := big.NewInt(842468587426513207)
    316 				pi.Div(n, d)
    317 			}
    318 			select {
    319 			case <-t:
    320 				return
    321 			default:
    322 			}
    323 		}
    324 	})
    325 }
    326 
    327 // Operating systems that are expected to fail the tests. See issue 6047.
    328 var badOS = map[string]bool{
    329 	"darwin": true,
    330 	"netbsd": true,
    331 	"plan9":  true,
    332 }
    333 
    334 func TestBlockProfile(t *testing.T) {
    335 	type TestCase struct {
    336 		name string
    337 		f    func()
    338 		re   string
    339 	}
    340 	tests := [...]TestCase{
    341 		{"chan recv", blockChanRecv, `
    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]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
    344 #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanRecv\+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 		{"chan send", blockChanSend, `
    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]+	runtime\.chansend1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
    350 #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanSend\+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 		{"chan close", blockChanClose, `
    354 [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]+
    355 #	0x[0-9,a-f]+	runtime\.chanrecv1\+0x[0-9,a-f]+	.*/src/runtime/chan.go:[0-9]+
    356 #	0x[0-9,a-f]+	runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    357 #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    358 `},
    359 		{"select recv async", blockSelectRecvAsync, `
    360 [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]+
    361 #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
    362 #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    363 #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    364 `},
    365 		{"select send sync", blockSelectSendSync, `
    366 [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]+
    367 #	0x[0-9,a-f]+	runtime\.selectgo\+0x[0-9,a-f]+	.*/src/runtime/select.go:[0-9]+
    368 #	0x[0-9,a-f]+	runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    369 #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    370 `},
    371 		{"mutex", blockMutex, `
    372 [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]+
    373 #	0x[0-9,a-f]+	sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+	.*/src/sync/mutex\.go:[0-9]+
    374 #	0x[0-9,a-f]+	runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    375 #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    376 `},
    377 		{"cond", blockCond, `
    378 [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]+
    379 #	0x[0-9,a-f]+	sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+	.*/src/sync/cond\.go:[0-9]+
    380 #	0x[0-9,a-f]+	runtime/pprof_test\.blockCond\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    381 #	0x[0-9,a-f]+	runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+	.*/src/runtime/pprof/pprof_test.go:[0-9]+
    382 `},
    383 	}
    384 
    385 	runtime.SetBlockProfileRate(1)
    386 	defer runtime.SetBlockProfileRate(0)
    387 	for _, test := range tests {
    388 		test.f()
    389 	}
    390 	var w bytes.Buffer
    391 	Lookup("block").WriteTo(&w, 1)
    392 	prof := w.String()
    393 
    394 	if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") {
    395 		t.Fatalf("Bad profile header:\n%v", prof)
    396 	}
    397 
    398 	for _, test := range tests {
    399 		if !regexp.MustCompile(strings.Replace(test.re, "\t", "\t+", -1)).MatchString(prof) {
    400 			t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof)
    401 		}
    402 	}
    403 }
    404 
    405 const blockDelay = 10 * time.Millisecond
    406 
    407 func blockChanRecv() {
    408 	c := make(chan bool)
    409 	go func() {
    410 		time.Sleep(blockDelay)
    411 		c <- true
    412 	}()
    413 	<-c
    414 }
    415 
    416 func blockChanSend() {
    417 	c := make(chan bool)
    418 	go func() {
    419 		time.Sleep(blockDelay)
    420 		<-c
    421 	}()
    422 	c <- true
    423 }
    424 
    425 func blockChanClose() {
    426 	c := make(chan bool)
    427 	go func() {
    428 		time.Sleep(blockDelay)
    429 		close(c)
    430 	}()
    431 	<-c
    432 }
    433 
    434 func blockSelectRecvAsync() {
    435 	c := make(chan bool, 1)
    436 	c2 := make(chan bool, 1)
    437 	go func() {
    438 		time.Sleep(blockDelay)
    439 		c <- true
    440 	}()
    441 	select {
    442 	case <-c:
    443 	case <-c2:
    444 	}
    445 }
    446 
    447 func blockSelectSendSync() {
    448 	c := make(chan bool)
    449 	c2 := make(chan bool)
    450 	go func() {
    451 		time.Sleep(blockDelay)
    452 		<-c
    453 	}()
    454 	select {
    455 	case c <- true:
    456 	case c2 <- true:
    457 	}
    458 }
    459 
    460 func blockMutex() {
    461 	var mu sync.Mutex
    462 	mu.Lock()
    463 	go func() {
    464 		time.Sleep(blockDelay)
    465 		mu.Unlock()
    466 	}()
    467 	mu.Lock()
    468 }
    469 
    470 func blockCond() {
    471 	var mu sync.Mutex
    472 	c := sync.NewCond(&mu)
    473 	mu.Lock()
    474 	go func() {
    475 		time.Sleep(blockDelay)
    476 		mu.Lock()
    477 		c.Signal()
    478 		mu.Unlock()
    479 	}()
    480 	c.Wait()
    481 	mu.Unlock()
    482 }
    483