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