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