github.com/flyinox/gosm@v0.0.0-20171117061539-16768cb62077/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.Errorf("failed to create pipe: %v", err) 273 return 274 } 275 defer func() { 276 rp.Close() 277 wp.Close() 278 }() 279 wg.Add(1) 280 go func() { 281 var tmp [1]byte 282 rp.Read(tmp[:]) 283 <-done 284 wg.Done() 285 }() 286 time.Sleep(time.Millisecond) 287 288 go func() { 289 runtime.LockOSThread() 290 for { 291 select { 292 case <-done: 293 return 294 default: 295 runtime.Gosched() 296 } 297 } 298 }() 299 300 runtime.GC() 301 // Trigger GC from malloc. 302 n := int(1e3) 303 if runtime.GOOS == "openbsd" && runtime.GOARCH == "arm" { 304 // Reduce allocation to avoid running out of 305 // memory on the builder - see issue/12032. 306 n = 512 307 } 308 for i := 0; i < n; i++ { 309 _ = make([]byte, 1<<20) 310 } 311 312 // Create a bunch of busy goroutines to load all Ps. 313 for p := 0; p < 10; p++ { 314 wg.Add(1) 315 go func() { 316 // Do something useful. 317 tmp := make([]byte, 1<<16) 318 for i := range tmp { 319 tmp[i]++ 320 } 321 _ = tmp 322 <-done 323 wg.Done() 324 }() 325 } 326 327 // Block in syscall. 328 wg.Add(1) 329 go func() { 330 var tmp [1]byte 331 rp.Read(tmp[:]) 332 <-done 333 wg.Done() 334 }() 335 336 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 337 338 // Test timers. 339 timerDone := make(chan bool) 340 go func() { 341 time.Sleep(time.Millisecond) 342 timerDone <- true 343 }() 344 <-timerDone 345 346 // A bit of network. 347 ln, err := net.Listen("tcp", "127.0.0.1:0") 348 if err != nil { 349 t.Errorf("listen failed: %v", err) 350 return 351 } 352 defer ln.Close() 353 go func() { 354 c, err := ln.Accept() 355 if err != nil { 356 return 357 } 358 time.Sleep(time.Millisecond) 359 var buf [1]byte 360 c.Write(buf[:]) 361 c.Close() 362 }() 363 c, err := net.Dial("tcp", ln.Addr().String()) 364 if err != nil { 365 t.Errorf("dial failed: %v", err) 366 return 367 } 368 var tmp [1]byte 369 c.Read(tmp[:]) 370 c.Close() 371 372 go func() { 373 runtime.Gosched() 374 select {} 375 }() 376 377 // Unblock helper goroutines and wait them to finish. 378 wp.Write(tmp[:]) 379 wp.Write(tmp[:]) 380 close(done) 381 wg.Wait() 382 }() 383 384 for i := 0; i < 3; i++ { 385 buf := new(bytes.Buffer) 386 if err := Start(buf); err != nil { 387 t.Fatalf("failed to start tracing: %v", err) 388 } 389 time.Sleep(time.Millisecond) 390 Stop() 391 saveTrace(t, buf, "TestTraceStressStartStop") 392 trace := buf.Bytes() 393 parseTrace(t, buf) 394 testBrokenTimestamps(t, trace) 395 } 396 <-outerDone 397 } 398 399 func TestTraceFutileWakeup(t *testing.T) { 400 buf := new(bytes.Buffer) 401 if err := Start(buf); err != nil { 402 t.Fatalf("failed to start tracing: %v", err) 403 } 404 405 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 406 c0 := make(chan int, 1) 407 c1 := make(chan int, 1) 408 c2 := make(chan int, 1) 409 const procs = 2 410 var done sync.WaitGroup 411 done.Add(4 * procs) 412 for p := 0; p < procs; p++ { 413 const iters = 1e3 414 go func() { 415 for i := 0; i < iters; i++ { 416 runtime.Gosched() 417 c0 <- 0 418 } 419 done.Done() 420 }() 421 go func() { 422 for i := 0; i < iters; i++ { 423 runtime.Gosched() 424 <-c0 425 } 426 done.Done() 427 }() 428 go func() { 429 for i := 0; i < iters; i++ { 430 runtime.Gosched() 431 select { 432 case c1 <- 0: 433 case c2 <- 0: 434 } 435 } 436 done.Done() 437 }() 438 go func() { 439 for i := 0; i < iters; i++ { 440 runtime.Gosched() 441 select { 442 case <-c1: 443 case <-c2: 444 } 445 } 446 done.Done() 447 }() 448 } 449 done.Wait() 450 451 Stop() 452 saveTrace(t, buf, "TestTraceFutileWakeup") 453 events, _ := parseTrace(t, buf) 454 // Check that (1) trace does not contain EvFutileWakeup events and 455 // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events 456 // (we call runtime.Gosched between all operations, so these would be futile wakeups). 457 gs := make(map[uint64]int) 458 for _, ev := range events { 459 switch ev.Type { 460 case trace.EvFutileWakeup: 461 t.Fatalf("found EvFutileWakeup event") 462 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect: 463 if gs[ev.G] == 2 { 464 t.Fatalf("goroutine %v blocked on %v at %v right after start", 465 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 466 } 467 if gs[ev.G] == 1 { 468 t.Fatalf("goroutine %v blocked on %v at %v while blocked", 469 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 470 } 471 gs[ev.G] = 1 472 case trace.EvGoStart: 473 if gs[ev.G] == 1 { 474 gs[ev.G] = 2 475 } 476 default: 477 delete(gs, ev.G) 478 } 479 } 480 } 481 482 func saveTrace(t *testing.T, buf *bytes.Buffer, name string) { 483 if !*saveTraces { 484 return 485 } 486 if err := ioutil.WriteFile(name+".trace", buf.Bytes(), 0600); err != nil { 487 t.Errorf("failed to write trace file: %s", err) 488 } 489 }