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