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.
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.
33 for i
:= 0; i
< 500 || time
.Since(t0
) < dur
; i
++ {
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.
48 for i
:= 0; i
< 1e5
; i
++ {
60 for i
:= 0; i
< 1e5
; i
++ {
70 func TestCPUProfile(t
*testing
.T
) {
71 testCPUProfile(t
, []string{"pprof_test.cpuHog1"}, func(dur time
.Duration
) {
72 cpuHogger(cpuHog1
, dur
)
76 func TestCPUProfileMultithreaded(t
*testing
.T
) {
77 defer runtime
.GOMAXPROCS(runtime
.GOMAXPROCS(2))
78 testCPUProfile(t
, []string{"pprof_test.cpuHog1", "pprof_test.cpuHog2"}, func(dur time
.Duration
) {
81 cpuHogger(cpuHog1
, dur
)
84 cpuHogger(cpuHog2
, dur
)
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
))
95 // 5 for the header, 3 for the trailer.
97 t
.Logf("profile too short: %#x", val
)
98 if badOS
[runtime
.GOOS
] {
99 t
.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime
.GOOS
)
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
)
110 if tl
[0] != 0 || tl
[1] != 1 || tl
[2] != 0 {
111 t
.Fatalf("malformed end-of-data marker %#x", tl
)
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
)
118 f(val
[0], val
[2:2+val
[1]])
123 func testCPUProfile(t
*testing
.T
, need
[]string, f
func(dur time
.Duration
)) {
124 switch runtime
.GOOS
{
126 switch runtime
.GOARCH
{
130 out
, err
:= exec
.Command("uname", "-a").CombinedOutput()
135 t
.Logf("uname -a: %v", vers
)
138 t
.Skip("skipping on plan9")
141 const maxDuration
= 5 * time
.Second
142 // If we're running a long test, start with a long duration
143 // because some of the tests (e.g., TestStackBarrierProfiling)
144 // are trying to make sure something *doesn't* happen.
145 duration
:= 5 * time
.Second
147 duration
= 200 * time
.Millisecond
150 // Profiling tests are inherently flaky, especially on a
151 // loaded system, such as when this test is running with
152 // several others under go test std. If a test fails in a way
153 // that could mean it just didn't run long enough, try with a
155 for duration
<= maxDuration
{
156 var prof bytes
.Buffer
157 if err
:= StartCPUProfile(&prof
); err
!= nil {
163 if profileOk(t
, need
, prof
, duration
) {
168 if duration
<= maxDuration
{
169 t
.Logf("retrying with %s duration", duration
)
173 if badOS
[runtime
.GOOS
] {
174 t
.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime
.GOOS
)
177 // Ignore the failure if the tests are running in a QEMU-based emulator,
178 // QEMU is not perfect at emulating everything.
179 // IN_QEMU environmental variable is set by some of the Go builders.
180 // IN_QEMU=1 indicates that the tests are running in QEMU. See issue 9605.
181 if os
.Getenv("IN_QEMU") == "1" {
182 t
.Skip("ignore the failure in QEMU; see golang.org/issue/9605")
188 func profileOk(t
*testing
.T
, need
[]string, prof bytes
.Buffer
, duration time
.Duration
) (ok
bool) {
191 // Check that profile is well formed and contains need.
192 have
:= make([]uintptr, len(need
))
194 parseProfile(t
, prof
.Bytes(), func(count
uintptr, stk
[]uintptr) {
196 for _
, pc
:= range stk
{
197 f
:= runtime
.FuncForPC(pc
)
201 t
.Log(f
.Name(), count
)
202 for i
, name
:= range need
{
203 if strings
.Contains(f
.Name(), name
) {
207 if strings
.Contains(f
.Name(), "stackBarrier") {
208 // The runtime should have unwound this.
209 t
.Fatalf("profile includes stackBarrier")
213 t
.Logf("total %d CPU profile samples collected", samples
)
215 if samples
< 10 && runtime
.GOOS
== "windows" {
216 // On some windows machines we end up with
217 // not enough samples due to coarse timer
218 // resolution. Let it go.
219 t
.Log("too few samples on Windows (golang.org/issue/10842)")
223 // Check that we got a reasonable number of samples.
224 if ideal
:= uintptr(duration
* 100 / time
.Second
); samples
== 0 || samples
< ideal
/4 {
225 t
.Logf("too few samples; got %d, want at least %d, ideally %d", samples
, ideal
/4, ideal
)
234 for i
, name
:= range need
{
236 t
.Logf("%s: %d\n", name
, have
[i
])
239 t
.Logf("no samples in expected functions")
242 // We'd like to check a reasonable minimum, like
243 // total / len(have) / smallconstant, but this test is
244 // pretty flaky (see bug 7095). So we'll just test to
245 // make sure we got at least one sample.
247 for i
, name
:= range need
{
249 t
.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name
, have
[i
], total
, min
, total
/uintptr(len(have
)))
256 // Fork can hang if preempted with signals frequently enough (see issue 5517).
257 // Ensure that we do not do this.
258 func TestCPUProfileWithFork(t
*testing
.T
) {
259 testenv
.MustHaveExec(t
)
262 if runtime
.GOOS
== "android" {
263 // Use smaller size for Android to avoid crash.
269 // This makes fork slower.
270 garbage
:= make([]byte, heap
)
271 // Need to touch the slice, otherwise it won't be paged in.
272 done
:= make(chan bool)
274 for i
:= range garbage
{
281 var prof bytes
.Buffer
282 if err
:= StartCPUProfile(&prof
); err
!= nil {
285 defer StopCPUProfile()
287 for i
:= 0; i
< 10; i
++ {
288 exec
.Command(os
.Args
[0], "-h").CombinedOutput()
292 // Test that profiler does not observe runtime.gogo as "user" goroutine execution.
293 // If it did, it would see inconsistent state and would either record an incorrect stack
294 // or crash because the stack was malformed.
295 func TestGoroutineSwitch(t
*testing
.T
) {
296 // How much to try. These defaults take about 1 seconds
297 // on a 2012 MacBook Pro. The ones in short mode take
298 // about 0.1 seconds.
304 for try
:= 0; try
< tries
; try
++ {
305 var prof bytes
.Buffer
306 if err
:= StartCPUProfile(&prof
); err
!= nil {
309 for i
:= 0; i
< count
; i
++ {
314 // Read profile to look for entries for runtime.gogo with an attempt at a traceback.
316 parseProfile(t
, prof
.Bytes(), func(count
uintptr, stk
[]uintptr) {
317 // An entry with two frames with 'System' in its top frame
318 // exists to record a PC without a traceback. Those are okay.
320 f
:= runtime
.FuncForPC(stk
[1])
321 if f
!= nil && (f
.Name() == "runtime._System" || f
.Name() == "runtime._ExternalCode" || f
.Name() == "runtime._GC") {
326 // Otherwise, should not see runtime.gogo.
327 // The place we'd see it would be the inner most frame.
328 f
:= runtime
.FuncForPC(stk
[0])
329 if f
!= nil && f
.Name() == "runtime.gogo" {
331 for _
, pc
:= range stk
{
332 f
:= runtime
.FuncForPC(pc
)
334 fmt
.Fprintf(&buf
, "%#x ?:0\n", pc
)
336 file
, line
:= f
.FileLine(pc
)
337 fmt
.Fprintf(&buf
, "%#x %s:%d\n", pc
, file
, line
)
340 t
.Fatalf("found profile entry for runtime.gogo:\n%s", buf
.String())
346 // Test that profiling of division operations is okay, especially on ARM. See issue 6681.
347 func TestMathBigDivide(t
*testing
.T
) {
348 testCPUProfile(t
, nil, func(duration time
.Duration
) {
349 t
:= time
.After(duration
)
352 for i
:= 0; i
< 100; i
++ {
353 n
:= big
.NewInt(2646693125139304345)
354 d
:= big
.NewInt(842468587426513207)
366 func TestStackBarrierProfiling(t
*testing
.T
) {
367 if (runtime
.GOOS
== "linux" && runtime
.GOARCH
== "arm") || runtime
.GOOS
== "openbsd" || runtime
.GOOS
== "solaris" || runtime
.GOOS
== "dragonfly" || runtime
.GOOS
== "freebsd" {
368 // This test currently triggers a large number of
369 // usleep(100)s. These kernels/arches have poor
370 // resolution timers, so this gives up a whole
371 // scheduling quantum. On Linux and the BSDs (and
372 // probably Solaris), profiling signals are only
373 // generated when a process completes a whole
374 // scheduling quantum, so this test often gets zero
375 // profiling signals and fails.
376 t
.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405)")
380 if !strings
.Contains(os
.Getenv("GODEBUG"), "gcstackbarrierall=1") {
381 // Re-execute this test with constant GC and stack
382 // barriers at every frame.
383 testenv
.MustHaveExec(t
)
384 if runtime
.GOARCH
== "ppc64" || runtime
.GOARCH
== "ppc64le" {
385 t
.Skip("gcstackbarrierall doesn't work on ppc64")
387 args
:= []string{"-test.run=TestStackBarrierProfiling"}
389 args
= append(args
, "-test.short")
391 cmd
:= exec
.Command(os
.Args
[0], args
...)
392 cmd
.Env
= append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1"}, os
.Environ()...)
393 if out
, err
:= cmd
.CombinedOutput(); err
!= nil {
394 t
.Fatalf("subprocess failed with %v:\n%s", err
, out
)
399 testCPUProfile(t
, nil, func(duration time
.Duration
) {
400 // In long mode, we're likely to get one or two
401 // samples in stackBarrier.
402 t
:= time
.After(duration
)
416 func deepStack(depth
int) int {
420 x
= make([]byte, 1024)
421 return deepStack(depth
-1) + 1
424 // Operating systems that are expected to fail the tests. See issue 13841.
425 var badOS
= map[string]bool{
433 func TestBlockProfile(t
*testing
.T
) {
434 t
.Skip("lots of details are different for gccgo; FIXME")
435 type TestCase
struct {
440 tests
:= [...]TestCase
{
441 {"chan recv", blockChanRecv
, `
442 [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]+
443 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
444 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
445 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
447 {"chan send", blockChanSend
, `
448 [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]+
449 # 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
450 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
451 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
453 {"chan close", blockChanClose
, `
454 [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]+
455 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+
456 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
457 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
459 {"select recv async", blockSelectRecvAsync
, `
460 [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]+
461 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+
462 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
463 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
465 {"select send sync", blockSelectSendSync
, `
466 [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]+
467 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+
468 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
469 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
471 {"mutex", blockMutex
, `
472 [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]+
473 # 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/sync/mutex\.go:[0-9]+
474 # 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
475 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
477 {"cond", blockCond
, `
478 [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]+
479 # 0x[0-9,a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+ .*/src/sync/cond\.go:[0-9]+
480 # 0x[0-9,a-f]+ runtime/pprof_test\.blockCond\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
481 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+
485 runtime
.SetBlockProfileRate(1)
486 defer runtime
.SetBlockProfileRate(0)
487 for _
, test
:= range tests
{
491 Lookup("block").WriteTo(&w
, 1)
494 if !strings
.HasPrefix(prof
, "--- contention:\ncycles/second=") {
495 t
.Fatalf("Bad profile header:\n%v", prof
)
498 for _
, test
:= range tests
{
499 if !regexp
.MustCompile(strings
.Replace(test
.re
, "\t", "\t+", -1)).MatchString(prof
) {
500 t
.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test
.name
, test
.re
, prof
)
505 const blockDelay
= 10 * time
.Millisecond
507 func blockChanRecv() {
510 time
.Sleep(blockDelay
)
516 func blockChanSend() {
519 time
.Sleep(blockDelay
)
525 func blockChanClose() {
528 time
.Sleep(blockDelay
)
534 func blockSelectRecvAsync() {
535 c
:= make(chan bool, 1)
536 c2
:= make(chan bool, 1)
538 time
.Sleep(blockDelay
)
547 func blockSelectSendSync() {
549 c2
:= make(chan bool)
551 time
.Sleep(blockDelay
)
564 time
.Sleep(blockDelay
)
572 c
:= sync
.NewCond(&mu
)
575 time
.Sleep(blockDelay
)