github.com/aloncn/graphics-go@v0.0.1/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 "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(), 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. 32 t0 := time.Now() 33 for i := 0; i < 500 || time.Since(t0) < dur; 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(dur time.Duration) { 72 cpuHogger(cpuHog1, dur) 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(dur time.Duration) { 79 c := make(chan int) 80 go func() { 81 cpuHogger(cpuHog1, dur) 82 c <- 1 83 }() 84 cpuHogger(cpuHog2, dur) 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, 3 for the trailer. 96 if l < 5+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/13841", 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(dur time.Duration)) { 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 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 146 if testing.Short() { 147 duration = 200 * time.Millisecond 148 } 149 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 154 // longer duration. 155 for duration <= maxDuration { 156 var prof bytes.Buffer 157 if err := StartCPUProfile(&prof); err != nil { 158 t.Fatal(err) 159 } 160 f(duration) 161 StopCPUProfile() 162 163 if profileOk(t, need, prof, duration) { 164 return 165 } 166 167 duration *= 2 168 if duration <= maxDuration { 169 t.Logf("retrying with %s duration", duration) 170 } 171 } 172 173 if badOS[runtime.GOOS] { 174 t.Skipf("ignoring failure on %s; see golang.org/issue/13841", runtime.GOOS) 175 return 176 } 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") 183 return 184 } 185 t.FailNow() 186 } 187 188 func profileOk(t *testing.T, need []string, prof bytes.Buffer, duration time.Duration) (ok bool) { 189 ok = true 190 191 // Check that profile is well formed and contains need. 192 have := make([]uintptr, len(need)) 193 var samples uintptr 194 parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) { 195 samples += count 196 for _, pc := range stk { 197 f := runtime.FuncForPC(pc) 198 if f == nil { 199 continue 200 } 201 for i, name := range need { 202 if strings.Contains(f.Name(), name) { 203 have[i] += count 204 } 205 } 206 if strings.Contains(f.Name(), "stackBarrier") { 207 // The runtime should have unwound this. 208 t.Fatalf("profile includes stackBarrier") 209 } 210 } 211 }) 212 t.Logf("total %d CPU profile samples collected", samples) 213 214 if samples < 10 && runtime.GOOS == "windows" { 215 // On some windows machines we end up with 216 // not enough samples due to coarse timer 217 // resolution. Let it go. 218 t.Log("too few samples on Windows (golang.org/issue/10842)") 219 return false 220 } 221 222 // Check that we got a reasonable number of samples. 223 if ideal := uintptr(duration * 100 / time.Second); samples == 0 || samples < ideal/4 { 224 t.Logf("too few samples; got %d, want at least %d, ideally %d", samples, ideal/4, ideal) 225 ok = false 226 } 227 228 if len(need) == 0 { 229 return ok 230 } 231 232 var total uintptr 233 for i, name := range need { 234 total += have[i] 235 t.Logf("%s: %d\n", name, have[i]) 236 } 237 if total == 0 { 238 t.Logf("no samples in expected functions") 239 ok = false 240 } 241 // We'd like to check a reasonable minimum, like 242 // total / len(have) / smallconstant, but this test is 243 // pretty flaky (see bug 7095). So we'll just test to 244 // make sure we got at least one sample. 245 min := uintptr(1) 246 for i, name := range need { 247 if have[i] < min { 248 t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have))) 249 ok = false 250 } 251 } 252 return ok 253 } 254 255 // Fork can hang if preempted with signals frequently enough (see issue 5517). 256 // Ensure that we do not do this. 257 func TestCPUProfileWithFork(t *testing.T) { 258 testenv.MustHaveExec(t) 259 260 heap := 1 << 30 261 if runtime.GOOS == "android" { 262 // Use smaller size for Android to avoid crash. 263 heap = 100 << 20 264 } 265 if testing.Short() { 266 heap = 100 << 20 267 } 268 // This makes fork slower. 269 garbage := make([]byte, heap) 270 // Need to touch the slice, otherwise it won't be paged in. 271 done := make(chan bool) 272 go func() { 273 for i := range garbage { 274 garbage[i] = 42 275 } 276 done <- true 277 }() 278 <-done 279 280 var prof bytes.Buffer 281 if err := StartCPUProfile(&prof); err != nil { 282 t.Fatal(err) 283 } 284 defer StopCPUProfile() 285 286 for i := 0; i < 10; i++ { 287 exec.Command(os.Args[0], "-h").CombinedOutput() 288 } 289 } 290 291 // Test that profiler does not observe runtime.gogo as "user" goroutine execution. 292 // If it did, it would see inconsistent state and would either record an incorrect stack 293 // or crash because the stack was malformed. 294 func TestGoroutineSwitch(t *testing.T) { 295 // How much to try. These defaults take about 1 seconds 296 // on a 2012 MacBook Pro. The ones in short mode take 297 // about 0.1 seconds. 298 tries := 10 299 count := 1000000 300 if testing.Short() { 301 tries = 1 302 } 303 for try := 0; try < tries; try++ { 304 var prof bytes.Buffer 305 if err := StartCPUProfile(&prof); err != nil { 306 t.Fatal(err) 307 } 308 for i := 0; i < count; i++ { 309 runtime.Gosched() 310 } 311 StopCPUProfile() 312 313 // Read profile to look for entries for runtime.gogo with an attempt at a traceback. 314 // The special entry 315 parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) { 316 // An entry with two frames with 'System' in its top frame 317 // exists to record a PC without a traceback. Those are okay. 318 if len(stk) == 2 { 319 f := runtime.FuncForPC(stk[1]) 320 if f != nil && (f.Name() == "runtime._System" || f.Name() == "runtime._ExternalCode" || f.Name() == "runtime._GC") { 321 return 322 } 323 } 324 325 // Otherwise, should not see runtime.gogo. 326 // The place we'd see it would be the inner most frame. 327 f := runtime.FuncForPC(stk[0]) 328 if f != nil && f.Name() == "runtime.gogo" { 329 var buf bytes.Buffer 330 for _, pc := range stk { 331 f := runtime.FuncForPC(pc) 332 if f == nil { 333 fmt.Fprintf(&buf, "%#x ?:0\n", pc) 334 } else { 335 file, line := f.FileLine(pc) 336 fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line) 337 } 338 } 339 t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String()) 340 } 341 }) 342 } 343 } 344 345 // Test that profiling of division operations is okay, especially on ARM. See issue 6681. 346 func TestMathBigDivide(t *testing.T) { 347 testCPUProfile(t, nil, func(duration time.Duration) { 348 t := time.After(duration) 349 pi := new(big.Int) 350 for { 351 for i := 0; i < 100; i++ { 352 n := big.NewInt(2646693125139304345) 353 d := big.NewInt(842468587426513207) 354 pi.Div(n, d) 355 } 356 select { 357 case <-t: 358 return 359 default: 360 } 361 } 362 }) 363 } 364 365 func TestStackBarrierProfiling(t *testing.T) { 366 if (runtime.GOOS == "linux" && runtime.GOARCH == "arm") || runtime.GOOS == "openbsd" || runtime.GOOS == "solaris" || runtime.GOOS == "dragonfly" || runtime.GOOS == "freebsd" { 367 // This test currently triggers a large number of 368 // usleep(100)s. These kernels/arches have poor 369 // resolution timers, so this gives up a whole 370 // scheduling quantum. On Linux and the BSDs (and 371 // probably Solaris), profiling signals are only 372 // generated when a process completes a whole 373 // scheduling quantum, so this test often gets zero 374 // profiling signals and fails. 375 t.Skipf("low resolution timers inhibit profiling signals (golang.org/issue/13405)") 376 return 377 } 378 379 if !strings.Contains(os.Getenv("GODEBUG"), "gcstackbarrierall=1") { 380 // Re-execute this test with constant GC and stack 381 // barriers at every frame. 382 testenv.MustHaveExec(t) 383 if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" { 384 t.Skip("gcstackbarrierall doesn't work on ppc64") 385 } 386 args := []string{"-test.run=TestStackBarrierProfiling"} 387 if testing.Short() { 388 args = append(args, "-test.short") 389 } 390 cmd := exec.Command(os.Args[0], args...) 391 cmd.Env = append([]string{"GODEBUG=gcstackbarrierall=1", "GOGC=1"}, os.Environ()...) 392 if out, err := cmd.CombinedOutput(); err != nil { 393 t.Fatalf("subprocess failed with %v:\n%s", err, out) 394 } 395 return 396 } 397 398 testCPUProfile(t, nil, func(duration time.Duration) { 399 // In long mode, we're likely to get one or two 400 // samples in stackBarrier. 401 t := time.After(duration) 402 for { 403 deepStack(1000) 404 select { 405 case <-t: 406 return 407 default: 408 } 409 } 410 }) 411 } 412 413 var x []byte 414 415 func deepStack(depth int) int { 416 if depth == 0 { 417 return 0 418 } 419 x = make([]byte, 1024) 420 return deepStack(depth-1) + 1 421 } 422 423 // Operating systems that are expected to fail the tests. See issue 13841. 424 var badOS = map[string]bool{ 425 "darwin": true, 426 "netbsd": true, 427 "plan9": true, 428 "dragonfly": true, 429 "solaris": true, 430 } 431 432 func TestBlockProfile(t *testing.T) { 433 type TestCase struct { 434 name string 435 f func() 436 re string 437 } 438 tests := [...]TestCase{ 439 {"chan recv", blockChanRecv, ` 440 [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]+ 441 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+ 442 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 443 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 444 `}, 445 {"chan send", blockChanSend, ` 446 [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]+ 447 # 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+ 448 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 449 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 450 `}, 451 {"chan close", blockChanClose, ` 452 [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]+ 453 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/runtime/chan.go:[0-9]+ 454 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 455 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 456 `}, 457 {"select recv async", blockSelectRecvAsync, ` 458 [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]+ 459 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+ 460 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 461 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 462 `}, 463 {"select send sync", blockSelectSendSync, ` 464 [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]+ 465 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/runtime/select.go:[0-9]+ 466 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 467 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 468 `}, 469 {"mutex", blockMutex, ` 470 [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]+ 471 # 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/sync/mutex\.go:[0-9]+ 472 # 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 473 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 474 `}, 475 {"cond", blockCond, ` 476 [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]+ 477 # 0x[0-9,a-f]+ sync\.\(\*Cond\)\.Wait\+0x[0-9,a-f]+ .*/src/sync/cond\.go:[0-9]+ 478 # 0x[0-9,a-f]+ runtime/pprof_test\.blockCond\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 479 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/runtime/pprof/pprof_test.go:[0-9]+ 480 `}, 481 } 482 483 runtime.SetBlockProfileRate(1) 484 defer runtime.SetBlockProfileRate(0) 485 for _, test := range tests { 486 test.f() 487 } 488 var w bytes.Buffer 489 Lookup("block").WriteTo(&w, 1) 490 prof := w.String() 491 492 if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") { 493 t.Fatalf("Bad profile header:\n%v", prof) 494 } 495 496 for _, test := range tests { 497 if !regexp.MustCompile(strings.Replace(test.re, "\t", "\t+", -1)).MatchString(prof) { 498 t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof) 499 } 500 } 501 } 502 503 const blockDelay = 10 * time.Millisecond 504 505 func blockChanRecv() { 506 c := make(chan bool) 507 go func() { 508 time.Sleep(blockDelay) 509 c <- true 510 }() 511 <-c 512 } 513 514 func blockChanSend() { 515 c := make(chan bool) 516 go func() { 517 time.Sleep(blockDelay) 518 <-c 519 }() 520 c <- true 521 } 522 523 func blockChanClose() { 524 c := make(chan bool) 525 go func() { 526 time.Sleep(blockDelay) 527 close(c) 528 }() 529 <-c 530 } 531 532 func blockSelectRecvAsync() { 533 c := make(chan bool, 1) 534 c2 := make(chan bool, 1) 535 go func() { 536 time.Sleep(blockDelay) 537 c <- true 538 }() 539 select { 540 case <-c: 541 case <-c2: 542 } 543 } 544 545 func blockSelectSendSync() { 546 c := make(chan bool) 547 c2 := make(chan bool) 548 go func() { 549 time.Sleep(blockDelay) 550 <-c 551 }() 552 select { 553 case c <- true: 554 case c2 <- true: 555 } 556 } 557 558 func blockMutex() { 559 var mu sync.Mutex 560 mu.Lock() 561 go func() { 562 time.Sleep(blockDelay) 563 mu.Unlock() 564 }() 565 mu.Lock() 566 } 567 568 func blockCond() { 569 var mu sync.Mutex 570 c := sync.NewCond(&mu) 571 mu.Lock() 572 go func() { 573 time.Sleep(blockDelay) 574 mu.Lock() 575 c.Signal() 576 mu.Unlock() 577 }() 578 c.Wait() 579 mu.Unlock() 580 }