github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/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/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  }