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