github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/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 package pprof_test 6 7 import ( 8 "bytes" 9 "hash/crc32" 10 "os/exec" 11 "regexp" 12 "runtime" 13 . "runtime/pprof" 14 "strings" 15 "sync" 16 "testing" 17 "time" 18 "unsafe" 19 ) 20 21 func TestCPUProfile(t *testing.T) { 22 buf := make([]byte, 100000) 23 testCPUProfile(t, []string{"crc32.ChecksumIEEE"}, func() { 24 // This loop takes about a quarter second on a 2 GHz laptop. 25 // We only need to get one 100 Hz clock tick, so we've got 26 // a 25x safety buffer. 27 for i := 0; i < 1000; i++ { 28 crc32.ChecksumIEEE(buf) 29 } 30 }) 31 } 32 33 func TestCPUProfileMultithreaded(t *testing.T) { 34 buf := make([]byte, 100000) 35 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 36 testCPUProfile(t, []string{"crc32.ChecksumIEEE", "crc32.Update"}, func() { 37 c := make(chan int) 38 go func() { 39 for i := 0; i < 2000; i++ { 40 crc32.Update(0, crc32.IEEETable, buf) 41 } 42 c <- 1 43 }() 44 // This loop takes about a quarter second on a 2 GHz laptop. 45 // We only need to get one 100 Hz clock tick, so we've got 46 // a 25x safety buffer. 47 for i := 0; i < 2000; i++ { 48 crc32.ChecksumIEEE(buf) 49 } 50 <-c 51 }) 52 } 53 54 func testCPUProfile(t *testing.T, need []string, f func()) { 55 switch runtime.GOOS { 56 case "darwin": 57 out, err := exec.Command("uname", "-a").CombinedOutput() 58 if err != nil { 59 t.Fatal(err) 60 } 61 vers := string(out) 62 t.Logf("uname -a: %v", vers) 63 case "plan9": 64 // unimplemented 65 return 66 } 67 68 var prof bytes.Buffer 69 if err := StartCPUProfile(&prof); err != nil { 70 t.Fatal(err) 71 } 72 f() 73 StopCPUProfile() 74 75 // Convert []byte to []uintptr. 76 bytes := prof.Bytes() 77 l := len(bytes) / int(unsafe.Sizeof(uintptr(0))) 78 val := *(*[]uintptr)(unsafe.Pointer(&bytes)) 79 val = val[:l] 80 81 if l < 13 { 82 t.Logf("profile too short: %#x", val) 83 if badOS[runtime.GOOS] { 84 t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS) 85 return 86 } 87 t.FailNow() 88 } 89 90 hd, val, tl := val[:5], val[5:l-3], val[l-3:] 91 if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 { 92 t.Fatalf("unexpected header %#x", hd) 93 } 94 95 if tl[0] != 0 || tl[1] != 1 || tl[2] != 0 { 96 t.Fatalf("malformed end-of-data marker %#x", tl) 97 } 98 99 // Check that profile is well formed and contains ChecksumIEEE. 100 have := make([]uintptr, len(need)) 101 for len(val) > 0 { 102 if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] { 103 t.Fatalf("malformed profile. leftover: %#x", val) 104 } 105 for _, pc := range val[2 : 2+val[1]] { 106 f := runtime.FuncForPC(pc) 107 if f == nil { 108 continue 109 } 110 for i, name := range need { 111 if strings.Contains(f.Name(), name) { 112 have[i] += val[0] 113 } 114 } 115 } 116 val = val[2+val[1]:] 117 } 118 119 var total uintptr 120 for i, name := range need { 121 total += have[i] 122 t.Logf("%s: %d\n", name, have[i]) 123 } 124 ok := true 125 if total == 0 { 126 t.Logf("no CPU profile samples collected") 127 ok = false 128 } 129 min := total / uintptr(len(have)) / 3 130 for i, name := range need { 131 if have[i] < min { 132 t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have))) 133 ok = false 134 } 135 } 136 137 if !ok { 138 if badOS[runtime.GOOS] { 139 t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS) 140 return 141 } 142 t.FailNow() 143 } 144 } 145 146 func TestCPUProfileWithFork(t *testing.T) { 147 // Fork can hang if preempted with signals frequently enough (see issue 5517). 148 // Ensure that we do not do this. 149 heap := 1 << 30 150 if testing.Short() { 151 heap = 100 << 20 152 } 153 // This makes fork slower. 154 garbage := make([]byte, heap) 155 // Need to touch the slice, otherwise it won't be paged in. 156 done := make(chan bool) 157 go func() { 158 for i := range garbage { 159 garbage[i] = 42 160 } 161 done <- true 162 }() 163 <-done 164 165 var prof bytes.Buffer 166 if err := StartCPUProfile(&prof); err != nil { 167 t.Fatal(err) 168 } 169 defer StopCPUProfile() 170 171 for i := 0; i < 10; i++ { 172 exec.Command("go").CombinedOutput() 173 } 174 } 175 176 // Operating systems that are expected to fail the tests. See issue 6047. 177 var badOS = map[string]bool{ 178 "darwin": true, 179 "netbsd": true, 180 "openbsd": true, 181 } 182 183 func TestBlockProfile(t *testing.T) { 184 type TestCase struct { 185 name string 186 f func() 187 re string 188 } 189 tests := [...]TestCase{ 190 {"chan recv", blockChanRecv, ` 191 [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]+ 192 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ 193 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 194 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 195 `}, 196 {"chan send", blockChanSend, ` 197 [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]+ 198 # 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ 199 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 200 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 201 `}, 202 {"chan close", blockChanClose, ` 203 [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]+ 204 # 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ 205 # 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 206 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 207 `}, 208 {"select recv async", blockSelectRecvAsync, ` 209 [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]+ 210 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ 211 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 212 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 213 `}, 214 {"select send sync", blockSelectSendSync, ` 215 [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]+ 216 # 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ 217 # 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 218 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 219 `}, 220 {"mutex", blockMutex, ` 221 [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]+ 222 # 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/pkg/sync/mutex\.go:[0-9]+ 223 # 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 224 # 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ 225 `}, 226 } 227 228 runtime.SetBlockProfileRate(1) 229 defer runtime.SetBlockProfileRate(0) 230 for _, test := range tests { 231 test.f() 232 } 233 var w bytes.Buffer 234 Lookup("block").WriteTo(&w, 1) 235 prof := w.String() 236 237 if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") { 238 t.Fatalf("Bad profile header:\n%v", prof) 239 } 240 241 for _, test := range tests { 242 if !regexp.MustCompile(test.re).MatchString(prof) { 243 t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof) 244 } 245 } 246 } 247 248 const blockDelay = 10 * time.Millisecond 249 250 func blockChanRecv() { 251 c := make(chan bool) 252 go func() { 253 time.Sleep(blockDelay) 254 c <- true 255 }() 256 <-c 257 } 258 259 func blockChanSend() { 260 c := make(chan bool) 261 go func() { 262 time.Sleep(blockDelay) 263 <-c 264 }() 265 c <- true 266 } 267 268 func blockChanClose() { 269 c := make(chan bool) 270 go func() { 271 time.Sleep(blockDelay) 272 close(c) 273 }() 274 <-c 275 } 276 277 func blockSelectRecvAsync() { 278 c := make(chan bool, 1) 279 c2 := make(chan bool, 1) 280 go func() { 281 time.Sleep(blockDelay) 282 c <- true 283 }() 284 select { 285 case <-c: 286 case <-c2: 287 } 288 } 289 290 func blockSelectSendSync() { 291 c := make(chan bool) 292 c2 := make(chan bool) 293 go func() { 294 time.Sleep(blockDelay) 295 <-c 296 }() 297 select { 298 case c <- true: 299 case c2 <- true: 300 } 301 } 302 303 func blockMutex() { 304 var mu sync.Mutex 305 mu.Lock() 306 go func() { 307 time.Sleep(blockDelay) 308 mu.Unlock() 309 }() 310 mu.Lock() 311 }