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