github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/runtime/pprof/trace_test.go (about) 1 // Copyright 2014 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 "internal/trace" 10 "io" 11 "net" 12 "os" 13 "runtime" 14 . "runtime/pprof" 15 "sync" 16 "testing" 17 "time" 18 ) 19 20 func skipTraceTestsIfNeeded(t *testing.T) { 21 switch runtime.GOOS { 22 case "solaris": 23 t.Skip("skipping: solaris timer can go backwards (http://golang.org/issue/8976)") 24 case "darwin": 25 switch runtime.GOARCH { 26 case "arm", "arm64": 27 t.Skipf("skipping on %s/%s, cannot fork", runtime.GOOS, runtime.GOARCH) 28 } 29 } 30 31 switch runtime.GOARCH { 32 case "arm": 33 t.Skip("skipping: arm tests fail with 'failed to parse trace' (http://golang.org/issue/9725)") 34 } 35 } 36 37 func TestTraceStartStop(t *testing.T) { 38 skipTraceTestsIfNeeded(t) 39 buf := new(bytes.Buffer) 40 if err := StartTrace(buf); err != nil { 41 t.Fatalf("failed to start tracing: %v", err) 42 } 43 StopTrace() 44 size := buf.Len() 45 if size == 0 { 46 t.Fatalf("trace is empty") 47 } 48 time.Sleep(100 * time.Millisecond) 49 if size != buf.Len() { 50 t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len()) 51 } 52 } 53 54 func TestTraceDoubleStart(t *testing.T) { 55 skipTraceTestsIfNeeded(t) 56 StopTrace() 57 buf := new(bytes.Buffer) 58 if err := StartTrace(buf); err != nil { 59 t.Fatalf("failed to start tracing: %v", err) 60 } 61 if err := StartTrace(buf); err == nil { 62 t.Fatalf("succeed to start tracing second time") 63 } 64 StopTrace() 65 StopTrace() 66 } 67 68 func TestTrace(t *testing.T) { 69 skipTraceTestsIfNeeded(t) 70 buf := new(bytes.Buffer) 71 if err := StartTrace(buf); err != nil { 72 t.Fatalf("failed to start tracing: %v", err) 73 } 74 StopTrace() 75 _, err := trace.Parse(buf) 76 if err != nil { 77 t.Fatalf("failed to parse trace: %v", err) 78 } 79 } 80 81 func parseTrace(r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc, error) { 82 events, err := trace.Parse(r) 83 if err != nil { 84 return nil, nil, err 85 } 86 gs := trace.GoroutineStats(events) 87 for goid := range gs { 88 // We don't do any particular checks on the result at the moment. 89 // But still check that RelatedGoroutines does not crash, hang, etc. 90 _ = trace.RelatedGoroutines(events, goid) 91 } 92 return events, gs, nil 93 } 94 95 func TestTraceStress(t *testing.T) { 96 skipTraceTestsIfNeeded(t) 97 98 var wg sync.WaitGroup 99 done := make(chan bool) 100 101 // Create a goroutine blocked before tracing. 102 wg.Add(1) 103 go func() { 104 <-done 105 wg.Done() 106 }() 107 108 // Create a goroutine blocked in syscall before tracing. 109 rp, wp, err := os.Pipe() 110 if err != nil { 111 t.Fatalf("failed to create pipe: %v", err) 112 } 113 defer func() { 114 rp.Close() 115 wp.Close() 116 }() 117 wg.Add(1) 118 go func() { 119 var tmp [1]byte 120 rp.Read(tmp[:]) 121 <-done 122 wg.Done() 123 }() 124 time.Sleep(time.Millisecond) // give the goroutine above time to block 125 126 buf := new(bytes.Buffer) 127 if err := StartTrace(buf); err != nil { 128 t.Fatalf("failed to start tracing: %v", err) 129 } 130 131 procs := runtime.GOMAXPROCS(10) 132 time.Sleep(50 * time.Millisecond) // test proc stop/start events 133 134 go func() { 135 runtime.LockOSThread() 136 for { 137 select { 138 case <-done: 139 return 140 default: 141 runtime.Gosched() 142 } 143 } 144 }() 145 146 runtime.GC() 147 // Trigger GC from malloc. 148 for i := 0; i < 1e3; i++ { 149 _ = make([]byte, 1<<20) 150 } 151 152 // Create a bunch of busy goroutines to load all Ps. 153 for p := 0; p < 10; p++ { 154 wg.Add(1) 155 go func() { 156 // Do something useful. 157 tmp := make([]byte, 1<<16) 158 for i := range tmp { 159 tmp[i]++ 160 } 161 _ = tmp 162 <-done 163 wg.Done() 164 }() 165 } 166 167 // Block in syscall. 168 wg.Add(1) 169 go func() { 170 var tmp [1]byte 171 rp.Read(tmp[:]) 172 <-done 173 wg.Done() 174 }() 175 176 // Test timers. 177 timerDone := make(chan bool) 178 go func() { 179 time.Sleep(time.Millisecond) 180 timerDone <- true 181 }() 182 <-timerDone 183 184 // A bit of network. 185 ln, err := net.Listen("tcp", "127.0.0.1:0") 186 if err != nil { 187 t.Fatalf("listen failed: %v", err) 188 } 189 defer ln.Close() 190 go func() { 191 c, err := ln.Accept() 192 if err != nil { 193 return 194 } 195 time.Sleep(time.Millisecond) 196 var buf [1]byte 197 c.Write(buf[:]) 198 c.Close() 199 }() 200 c, err := net.Dial("tcp", ln.Addr().String()) 201 if err != nil { 202 t.Fatalf("dial failed: %v", err) 203 } 204 var tmp [1]byte 205 c.Read(tmp[:]) 206 c.Close() 207 208 go func() { 209 runtime.Gosched() 210 select {} 211 }() 212 213 // Unblock helper goroutines and wait them to finish. 214 wp.Write(tmp[:]) 215 wp.Write(tmp[:]) 216 close(done) 217 wg.Wait() 218 219 runtime.GOMAXPROCS(procs) 220 221 StopTrace() 222 _, _, err = parseTrace(buf) 223 if err != nil { 224 t.Fatalf("failed to parse trace: %v", err) 225 } 226 } 227 228 // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine. 229 // And concurrently with all that start/stop trace 3 times. 230 func TestTraceStressStartStop(t *testing.T) { 231 skipTraceTestsIfNeeded(t) 232 233 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 234 outerDone := make(chan bool) 235 236 go func() { 237 defer func() { 238 outerDone <- true 239 }() 240 241 var wg sync.WaitGroup 242 done := make(chan bool) 243 244 wg.Add(1) 245 go func() { 246 <-done 247 wg.Done() 248 }() 249 250 rp, wp, err := os.Pipe() 251 if err != nil { 252 t.Fatalf("failed to create pipe: %v", err) 253 } 254 defer func() { 255 rp.Close() 256 wp.Close() 257 }() 258 wg.Add(1) 259 go func() { 260 var tmp [1]byte 261 rp.Read(tmp[:]) 262 <-done 263 wg.Done() 264 }() 265 time.Sleep(time.Millisecond) 266 267 go func() { 268 runtime.LockOSThread() 269 for { 270 select { 271 case <-done: 272 return 273 default: 274 runtime.Gosched() 275 } 276 } 277 }() 278 279 runtime.GC() 280 // Trigger GC from malloc. 281 for i := 0; i < 1e3; i++ { 282 _ = make([]byte, 1<<20) 283 } 284 285 // Create a bunch of busy goroutines to load all Ps. 286 for p := 0; p < 10; p++ { 287 wg.Add(1) 288 go func() { 289 // Do something useful. 290 tmp := make([]byte, 1<<16) 291 for i := range tmp { 292 tmp[i]++ 293 } 294 _ = tmp 295 <-done 296 wg.Done() 297 }() 298 } 299 300 // Block in syscall. 301 wg.Add(1) 302 go func() { 303 var tmp [1]byte 304 rp.Read(tmp[:]) 305 <-done 306 wg.Done() 307 }() 308 309 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 310 311 // Test timers. 312 timerDone := make(chan bool) 313 go func() { 314 time.Sleep(time.Millisecond) 315 timerDone <- true 316 }() 317 <-timerDone 318 319 // A bit of network. 320 ln, err := net.Listen("tcp", "127.0.0.1:0") 321 if err != nil { 322 t.Fatalf("listen failed: %v", err) 323 } 324 defer ln.Close() 325 go func() { 326 c, err := ln.Accept() 327 if err != nil { 328 return 329 } 330 time.Sleep(time.Millisecond) 331 var buf [1]byte 332 c.Write(buf[:]) 333 c.Close() 334 }() 335 c, err := net.Dial("tcp", ln.Addr().String()) 336 if err != nil { 337 t.Fatalf("dial failed: %v", err) 338 } 339 var tmp [1]byte 340 c.Read(tmp[:]) 341 c.Close() 342 343 go func() { 344 runtime.Gosched() 345 select {} 346 }() 347 348 // Unblock helper goroutines and wait them to finish. 349 wp.Write(tmp[:]) 350 wp.Write(tmp[:]) 351 close(done) 352 wg.Wait() 353 }() 354 355 for i := 0; i < 3; i++ { 356 buf := new(bytes.Buffer) 357 if err := StartTrace(buf); err != nil { 358 t.Fatalf("failed to start tracing: %v", err) 359 } 360 time.Sleep(time.Millisecond) 361 StopTrace() 362 if _, _, err := parseTrace(buf); err != nil { 363 t.Fatalf("failed to parse trace: %v", err) 364 } 365 } 366 <-outerDone 367 } 368 369 func TestTraceFutileWakeup(t *testing.T) { 370 // The test generates a full-load of futile wakeups on channels, 371 // and ensures that the trace is consistent after their removal. 372 skipTraceTestsIfNeeded(t) 373 buf := new(bytes.Buffer) 374 if err := StartTrace(buf); err != nil { 375 t.Fatalf("failed to start tracing: %v", err) 376 } 377 378 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 379 c0 := make(chan int, 1) 380 c1 := make(chan int, 1) 381 c2 := make(chan int, 1) 382 const procs = 2 383 var done sync.WaitGroup 384 done.Add(4 * procs) 385 for p := 0; p < procs; p++ { 386 const iters = 1e3 387 go func() { 388 for i := 0; i < iters; i++ { 389 runtime.Gosched() 390 c0 <- 0 391 } 392 done.Done() 393 }() 394 go func() { 395 for i := 0; i < iters; i++ { 396 runtime.Gosched() 397 <-c0 398 } 399 done.Done() 400 }() 401 go func() { 402 for i := 0; i < iters; i++ { 403 runtime.Gosched() 404 select { 405 case c1 <- 0: 406 case c2 <- 0: 407 } 408 } 409 done.Done() 410 }() 411 go func() { 412 for i := 0; i < iters; i++ { 413 runtime.Gosched() 414 select { 415 case <-c1: 416 case <-c2: 417 } 418 } 419 done.Done() 420 }() 421 } 422 done.Wait() 423 424 StopTrace() 425 events, _, err := parseTrace(buf) 426 if err != nil { 427 t.Fatalf("failed to parse trace: %v", err) 428 } 429 // Check that (1) trace does not contain EvFutileWakeup events and 430 // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events 431 // (we call runtime.Gosched between all operations, so these would be futile wakeups). 432 gs := make(map[uint64]int) 433 for _, ev := range events { 434 switch ev.Type { 435 case trace.EvFutileWakeup: 436 t.Fatalf("found EvFutileWakeup event") 437 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect: 438 if gs[ev.G] == 2 { 439 t.Fatalf("goroutine %v blocked on %v at %v right after start", 440 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 441 } 442 if gs[ev.G] == 1 { 443 t.Fatalf("goroutine %v blocked on %v at %v while blocked", 444 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 445 } 446 gs[ev.G] = 1 447 case trace.EvGoStart: 448 if gs[ev.G] == 1 { 449 gs[ev.G] = 2 450 } 451 default: 452 delete(gs, ev.G) 453 } 454 } 455 }