Home | History | Annotate | Download | only in trace
      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