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, error) { 65 events, err := trace.Parse(r) 66 if err == trace.ErrTimeOrder { 67 t.Skipf("skipping trace: %v", err) 68 } 69 if err != nil { 70 return nil, nil, 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, nil 79 } 80 81 func TestTraceStress(t *testing.T) { 82 var wg sync.WaitGroup 83 done := make(chan bool) 84 85 // Create a goroutine blocked before tracing. 86 wg.Add(1) 87 go func() { 88 <-done 89 wg.Done() 90 }() 91 92 // Create a goroutine blocked in syscall before tracing. 93 rp, wp, err := os.Pipe() 94 if err != nil { 95 t.Fatalf("failed to create pipe: %v", err) 96 } 97 defer func() { 98 rp.Close() 99 wp.Close() 100 }() 101 wg.Add(1) 102 go func() { 103 var tmp [1]byte 104 rp.Read(tmp[:]) 105 <-done 106 wg.Done() 107 }() 108 time.Sleep(time.Millisecond) // give the goroutine above time to block 109 110 buf := new(bytes.Buffer) 111 if err := Start(buf); err != nil { 112 t.Fatalf("failed to start tracing: %v", err) 113 } 114 115 procs := runtime.GOMAXPROCS(10) 116 time.Sleep(50 * time.Millisecond) // test proc stop/start events 117 118 go func() { 119 runtime.LockOSThread() 120 for { 121 select { 122 case <-done: 123 return 124 default: 125 runtime.Gosched() 126 } 127 } 128 }() 129 130 runtime.GC() 131 // Trigger GC from malloc. 132 for i := 0; i < 1e3; i++ { 133 _ = make([]byte, 1<<20) 134 } 135 136 // Create a bunch of busy goroutines to load all Ps. 137 for p := 0; p < 10; p++ { 138 wg.Add(1) 139 go func() { 140 // Do something useful. 141 tmp := make([]byte, 1<<16) 142 for i := range tmp { 143 tmp[i]++ 144 } 145 _ = tmp 146 <-done 147 wg.Done() 148 }() 149 } 150 151 // Block in syscall. 152 wg.Add(1) 153 go func() { 154 var tmp [1]byte 155 rp.Read(tmp[:]) 156 <-done 157 wg.Done() 158 }() 159 160 // Test timers. 161 timerDone := make(chan bool) 162 go func() { 163 time.Sleep(time.Millisecond) 164 timerDone <- true 165 }() 166 <-timerDone 167 168 // A bit of network. 169 ln, err := net.Listen("tcp", "127.0.0.1:0") 170 if err != nil { 171 t.Fatalf("listen failed: %v", err) 172 } 173 defer ln.Close() 174 go func() { 175 c, err := ln.Accept() 176 if err != nil { 177 return 178 } 179 time.Sleep(time.Millisecond) 180 var buf [1]byte 181 c.Write(buf[:]) 182 c.Close() 183 }() 184 c, err := net.Dial("tcp", ln.Addr().String()) 185 if err != nil { 186 t.Fatalf("dial failed: %v", err) 187 } 188 var tmp [1]byte 189 c.Read(tmp[:]) 190 c.Close() 191 192 go func() { 193 runtime.Gosched() 194 select {} 195 }() 196 197 // Unblock helper goroutines and wait them to finish. 198 wp.Write(tmp[:]) 199 wp.Write(tmp[:]) 200 close(done) 201 wg.Wait() 202 203 runtime.GOMAXPROCS(procs) 204 205 Stop() 206 _, _, err = parseTrace(t, buf) 207 if err != nil { 208 t.Fatalf("failed to parse trace: %v", err) 209 } 210 } 211 212 // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine. 213 // And concurrently with all that start/stop trace 3 times. 214 func TestTraceStressStartStop(t *testing.T) { 215 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 216 outerDone := make(chan bool) 217 218 go func() { 219 defer func() { 220 outerDone <- true 221 }() 222 223 var wg sync.WaitGroup 224 done := make(chan bool) 225 226 wg.Add(1) 227 go func() { 228 <-done 229 wg.Done() 230 }() 231 232 rp, wp, err := os.Pipe() 233 if err != nil { 234 t.Fatalf("failed to create pipe: %v", err) 235 } 236 defer func() { 237 rp.Close() 238 wp.Close() 239 }() 240 wg.Add(1) 241 go func() { 242 var tmp [1]byte 243 rp.Read(tmp[:]) 244 <-done 245 wg.Done() 246 }() 247 time.Sleep(time.Millisecond) 248 249 go func() { 250 runtime.LockOSThread() 251 for { 252 select { 253 case <-done: 254 return 255 default: 256 runtime.Gosched() 257 } 258 } 259 }() 260 261 runtime.GC() 262 // Trigger GC from malloc. 263 for i := 0; i < 1e3; i++ { 264 _ = make([]byte, 1<<20) 265 } 266 267 // Create a bunch of busy goroutines to load all Ps. 268 for p := 0; p < 10; p++ { 269 wg.Add(1) 270 go func() { 271 // Do something useful. 272 tmp := make([]byte, 1<<16) 273 for i := range tmp { 274 tmp[i]++ 275 } 276 _ = tmp 277 <-done 278 wg.Done() 279 }() 280 } 281 282 // Block in syscall. 283 wg.Add(1) 284 go func() { 285 var tmp [1]byte 286 rp.Read(tmp[:]) 287 <-done 288 wg.Done() 289 }() 290 291 runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 292 293 // Test timers. 294 timerDone := make(chan bool) 295 go func() { 296 time.Sleep(time.Millisecond) 297 timerDone <- true 298 }() 299 <-timerDone 300 301 // A bit of network. 302 ln, err := net.Listen("tcp", "127.0.0.1:0") 303 if err != nil { 304 t.Fatalf("listen failed: %v", err) 305 } 306 defer ln.Close() 307 go func() { 308 c, err := ln.Accept() 309 if err != nil { 310 return 311 } 312 time.Sleep(time.Millisecond) 313 var buf [1]byte 314 c.Write(buf[:]) 315 c.Close() 316 }() 317 c, err := net.Dial("tcp", ln.Addr().String()) 318 if err != nil { 319 t.Fatalf("dial failed: %v", err) 320 } 321 var tmp [1]byte 322 c.Read(tmp[:]) 323 c.Close() 324 325 go func() { 326 runtime.Gosched() 327 select {} 328 }() 329 330 // Unblock helper goroutines and wait them to finish. 331 wp.Write(tmp[:]) 332 wp.Write(tmp[:]) 333 close(done) 334 wg.Wait() 335 }() 336 337 for i := 0; i < 3; i++ { 338 buf := new(bytes.Buffer) 339 if err := Start(buf); err != nil { 340 t.Fatalf("failed to start tracing: %v", err) 341 } 342 time.Sleep(time.Millisecond) 343 Stop() 344 if _, _, err := parseTrace(t, buf); err != nil { 345 t.Fatalf("failed to parse trace: %v", err) 346 } 347 } 348 <-outerDone 349 } 350 351 func TestTraceFutileWakeup(t *testing.T) { 352 buf := new(bytes.Buffer) 353 if err := Start(buf); err != nil { 354 t.Fatalf("failed to start tracing: %v", err) 355 } 356 357 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8)) 358 c0 := make(chan int, 1) 359 c1 := make(chan int, 1) 360 c2 := make(chan int, 1) 361 const procs = 2 362 var done sync.WaitGroup 363 done.Add(4 * procs) 364 for p := 0; p < procs; p++ { 365 const iters = 1e3 366 go func() { 367 for i := 0; i < iters; i++ { 368 runtime.Gosched() 369 c0 <- 0 370 } 371 done.Done() 372 }() 373 go func() { 374 for i := 0; i < iters; i++ { 375 runtime.Gosched() 376 <-c0 377 } 378 done.Done() 379 }() 380 go func() { 381 for i := 0; i < iters; i++ { 382 runtime.Gosched() 383 select { 384 case c1 <- 0: 385 case c2 <- 0: 386 } 387 } 388 done.Done() 389 }() 390 go func() { 391 for i := 0; i < iters; i++ { 392 runtime.Gosched() 393 select { 394 case <-c1: 395 case <-c2: 396 } 397 } 398 done.Done() 399 }() 400 } 401 done.Wait() 402 403 Stop() 404 events, _, err := parseTrace(t, buf) 405 if err != nil { 406 t.Fatalf("failed to parse trace: %v", err) 407 } 408 // Check that (1) trace does not contain EvFutileWakeup events and 409 // (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events 410 // (we call runtime.Gosched between all operations, so these would be futile wakeups). 411 gs := make(map[uint64]int) 412 for _, ev := range events { 413 switch ev.Type { 414 case trace.EvFutileWakeup: 415 t.Fatalf("found EvFutileWakeup event") 416 case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect: 417 if gs[ev.G] == 2 { 418 t.Fatalf("goroutine %v blocked on %v at %v right after start", 419 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 420 } 421 if gs[ev.G] == 1 { 422 t.Fatalf("goroutine %v blocked on %v at %v while blocked", 423 ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts) 424 } 425 gs[ev.G] = 1 426 case trace.EvGoStart: 427 if gs[ev.G] == 1 { 428 gs[ev.G] = 2 429 } 430 default: 431 delete(gs, ev.G) 432 } 433 } 434 } 435