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