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