github.com/zxy12/golang151_with_comment@v0.0.0-20190507085033-721809559d3c/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, 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  }