github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/runtime/pprof/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 pprof_test
     6  
     7  import (
     8  	"bytes"
     9  	"internal/trace"
    10  	"io"
    11  	"net"
    12  	"os"
    13  	"runtime"
    14  	. "runtime/pprof"
    15  	"sync"
    16  	"testing"
    17  	"time"
    18  )
    19  
    20  func skipTraceTestsIfNeeded(t *testing.T) {
    21  	switch runtime.GOOS {
    22  	case "solaris":
    23  		t.Skip("skipping: solaris timer can go backwards (http://golang.org/issue/8976)")
    24  	case "darwin":
    25  		switch runtime.GOARCH {
    26  		case "arm", "arm64":
    27  			t.Skipf("skipping on %s/%s, cannot fork", runtime.GOOS, runtime.GOARCH)
    28  		}
    29  	}
    30  
    31  	switch runtime.GOARCH {
    32  	case "arm":
    33  		t.Skip("skipping: arm tests fail with 'failed to parse trace' (http://golang.org/issue/9725)")
    34  	}
    35  }
    36  
    37  func TestTraceStartStop(t *testing.T) {
    38  	skipTraceTestsIfNeeded(t)
    39  	buf := new(bytes.Buffer)
    40  	if err := StartTrace(buf); err != nil {
    41  		t.Fatalf("failed to start tracing: %v", err)
    42  	}
    43  	StopTrace()
    44  	size := buf.Len()
    45  	if size == 0 {
    46  		t.Fatalf("trace is empty")
    47  	}
    48  	time.Sleep(100 * time.Millisecond)
    49  	if size != buf.Len() {
    50  		t.Fatalf("trace writes after stop: %v -> %v", size, buf.Len())
    51  	}
    52  }
    53  
    54  func TestTraceDoubleStart(t *testing.T) {
    55  	skipTraceTestsIfNeeded(t)
    56  	StopTrace()
    57  	buf := new(bytes.Buffer)
    58  	if err := StartTrace(buf); err != nil {
    59  		t.Fatalf("failed to start tracing: %v", err)
    60  	}
    61  	if err := StartTrace(buf); err == nil {
    62  		t.Fatalf("succeed to start tracing second time")
    63  	}
    64  	StopTrace()
    65  	StopTrace()
    66  }
    67  
    68  func TestTrace(t *testing.T) {
    69  	skipTraceTestsIfNeeded(t)
    70  	buf := new(bytes.Buffer)
    71  	if err := StartTrace(buf); err != nil {
    72  		t.Fatalf("failed to start tracing: %v", err)
    73  	}
    74  	StopTrace()
    75  	_, err := trace.Parse(buf)
    76  	if err != nil {
    77  		t.Fatalf("failed to parse trace: %v", err)
    78  	}
    79  }
    80  
    81  func parseTrace(r io.Reader) ([]*trace.Event, map[uint64]*trace.GDesc, error) {
    82  	events, err := trace.Parse(r)
    83  	if err != nil {
    84  		return nil, nil, err
    85  	}
    86  	gs := trace.GoroutineStats(events)
    87  	for goid := range gs {
    88  		// We don't do any particular checks on the result at the moment.
    89  		// But still check that RelatedGoroutines does not crash, hang, etc.
    90  		_ = trace.RelatedGoroutines(events, goid)
    91  	}
    92  	return events, gs, nil
    93  }
    94  
    95  func TestTraceStress(t *testing.T) {
    96  	skipTraceTestsIfNeeded(t)
    97  
    98  	var wg sync.WaitGroup
    99  	done := make(chan bool)
   100  
   101  	// Create a goroutine blocked before tracing.
   102  	wg.Add(1)
   103  	go func() {
   104  		<-done
   105  		wg.Done()
   106  	}()
   107  
   108  	// Create a goroutine blocked in syscall before tracing.
   109  	rp, wp, err := os.Pipe()
   110  	if err != nil {
   111  		t.Fatalf("failed to create pipe: %v", err)
   112  	}
   113  	defer func() {
   114  		rp.Close()
   115  		wp.Close()
   116  	}()
   117  	wg.Add(1)
   118  	go func() {
   119  		var tmp [1]byte
   120  		rp.Read(tmp[:])
   121  		<-done
   122  		wg.Done()
   123  	}()
   124  	time.Sleep(time.Millisecond) // give the goroutine above time to block
   125  
   126  	buf := new(bytes.Buffer)
   127  	if err := StartTrace(buf); err != nil {
   128  		t.Fatalf("failed to start tracing: %v", err)
   129  	}
   130  
   131  	procs := runtime.GOMAXPROCS(10)
   132  	time.Sleep(50 * time.Millisecond) // test proc stop/start events
   133  
   134  	go func() {
   135  		runtime.LockOSThread()
   136  		for {
   137  			select {
   138  			case <-done:
   139  				return
   140  			default:
   141  				runtime.Gosched()
   142  			}
   143  		}
   144  	}()
   145  
   146  	runtime.GC()
   147  	// Trigger GC from malloc.
   148  	for i := 0; i < 1e3; i++ {
   149  		_ = make([]byte, 1<<20)
   150  	}
   151  
   152  	// Create a bunch of busy goroutines to load all Ps.
   153  	for p := 0; p < 10; p++ {
   154  		wg.Add(1)
   155  		go func() {
   156  			// Do something useful.
   157  			tmp := make([]byte, 1<<16)
   158  			for i := range tmp {
   159  				tmp[i]++
   160  			}
   161  			_ = tmp
   162  			<-done
   163  			wg.Done()
   164  		}()
   165  	}
   166  
   167  	// Block in syscall.
   168  	wg.Add(1)
   169  	go func() {
   170  		var tmp [1]byte
   171  		rp.Read(tmp[:])
   172  		<-done
   173  		wg.Done()
   174  	}()
   175  
   176  	// Test timers.
   177  	timerDone := make(chan bool)
   178  	go func() {
   179  		time.Sleep(time.Millisecond)
   180  		timerDone <- true
   181  	}()
   182  	<-timerDone
   183  
   184  	// A bit of network.
   185  	ln, err := net.Listen("tcp", "127.0.0.1:0")
   186  	if err != nil {
   187  		t.Fatalf("listen failed: %v", err)
   188  	}
   189  	defer ln.Close()
   190  	go func() {
   191  		c, err := ln.Accept()
   192  		if err != nil {
   193  			return
   194  		}
   195  		time.Sleep(time.Millisecond)
   196  		var buf [1]byte
   197  		c.Write(buf[:])
   198  		c.Close()
   199  	}()
   200  	c, err := net.Dial("tcp", ln.Addr().String())
   201  	if err != nil {
   202  		t.Fatalf("dial failed: %v", err)
   203  	}
   204  	var tmp [1]byte
   205  	c.Read(tmp[:])
   206  	c.Close()
   207  
   208  	go func() {
   209  		runtime.Gosched()
   210  		select {}
   211  	}()
   212  
   213  	// Unblock helper goroutines and wait them to finish.
   214  	wp.Write(tmp[:])
   215  	wp.Write(tmp[:])
   216  	close(done)
   217  	wg.Wait()
   218  
   219  	runtime.GOMAXPROCS(procs)
   220  
   221  	StopTrace()
   222  	_, _, err = parseTrace(buf)
   223  	if err != nil {
   224  		t.Fatalf("failed to parse trace: %v", err)
   225  	}
   226  }
   227  
   228  // Do a bunch of various stuff (timers, GC, network, etc) in a separate goroutine.
   229  // And concurrently with all that start/stop trace 3 times.
   230  func TestTraceStressStartStop(t *testing.T) {
   231  	skipTraceTestsIfNeeded(t)
   232  
   233  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
   234  	outerDone := make(chan bool)
   235  
   236  	go func() {
   237  		defer func() {
   238  			outerDone <- true
   239  		}()
   240  
   241  		var wg sync.WaitGroup
   242  		done := make(chan bool)
   243  
   244  		wg.Add(1)
   245  		go func() {
   246  			<-done
   247  			wg.Done()
   248  		}()
   249  
   250  		rp, wp, err := os.Pipe()
   251  		if err != nil {
   252  			t.Fatalf("failed to create pipe: %v", err)
   253  		}
   254  		defer func() {
   255  			rp.Close()
   256  			wp.Close()
   257  		}()
   258  		wg.Add(1)
   259  		go func() {
   260  			var tmp [1]byte
   261  			rp.Read(tmp[:])
   262  			<-done
   263  			wg.Done()
   264  		}()
   265  		time.Sleep(time.Millisecond)
   266  
   267  		go func() {
   268  			runtime.LockOSThread()
   269  			for {
   270  				select {
   271  				case <-done:
   272  					return
   273  				default:
   274  					runtime.Gosched()
   275  				}
   276  			}
   277  		}()
   278  
   279  		runtime.GC()
   280  		// Trigger GC from malloc.
   281  		for i := 0; i < 1e3; i++ {
   282  			_ = make([]byte, 1<<20)
   283  		}
   284  
   285  		// Create a bunch of busy goroutines to load all Ps.
   286  		for p := 0; p < 10; p++ {
   287  			wg.Add(1)
   288  			go func() {
   289  				// Do something useful.
   290  				tmp := make([]byte, 1<<16)
   291  				for i := range tmp {
   292  					tmp[i]++
   293  				}
   294  				_ = tmp
   295  				<-done
   296  				wg.Done()
   297  			}()
   298  		}
   299  
   300  		// Block in syscall.
   301  		wg.Add(1)
   302  		go func() {
   303  			var tmp [1]byte
   304  			rp.Read(tmp[:])
   305  			<-done
   306  			wg.Done()
   307  		}()
   308  
   309  		runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
   310  
   311  		// Test timers.
   312  		timerDone := make(chan bool)
   313  		go func() {
   314  			time.Sleep(time.Millisecond)
   315  			timerDone <- true
   316  		}()
   317  		<-timerDone
   318  
   319  		// A bit of network.
   320  		ln, err := net.Listen("tcp", "127.0.0.1:0")
   321  		if err != nil {
   322  			t.Fatalf("listen failed: %v", err)
   323  		}
   324  		defer ln.Close()
   325  		go func() {
   326  			c, err := ln.Accept()
   327  			if err != nil {
   328  				return
   329  			}
   330  			time.Sleep(time.Millisecond)
   331  			var buf [1]byte
   332  			c.Write(buf[:])
   333  			c.Close()
   334  		}()
   335  		c, err := net.Dial("tcp", ln.Addr().String())
   336  		if err != nil {
   337  			t.Fatalf("dial failed: %v", err)
   338  		}
   339  		var tmp [1]byte
   340  		c.Read(tmp[:])
   341  		c.Close()
   342  
   343  		go func() {
   344  			runtime.Gosched()
   345  			select {}
   346  		}()
   347  
   348  		// Unblock helper goroutines and wait them to finish.
   349  		wp.Write(tmp[:])
   350  		wp.Write(tmp[:])
   351  		close(done)
   352  		wg.Wait()
   353  	}()
   354  
   355  	for i := 0; i < 3; i++ {
   356  		buf := new(bytes.Buffer)
   357  		if err := StartTrace(buf); err != nil {
   358  			t.Fatalf("failed to start tracing: %v", err)
   359  		}
   360  		time.Sleep(time.Millisecond)
   361  		StopTrace()
   362  		if _, _, err := parseTrace(buf); err != nil {
   363  			t.Fatalf("failed to parse trace: %v", err)
   364  		}
   365  	}
   366  	<-outerDone
   367  }
   368  
   369  func TestTraceFutileWakeup(t *testing.T) {
   370  	// The test generates a full-load of futile wakeups on channels,
   371  	// and ensures that the trace is consistent after their removal.
   372  	skipTraceTestsIfNeeded(t)
   373  	buf := new(bytes.Buffer)
   374  	if err := StartTrace(buf); err != nil {
   375  		t.Fatalf("failed to start tracing: %v", err)
   376  	}
   377  
   378  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(8))
   379  	c0 := make(chan int, 1)
   380  	c1 := make(chan int, 1)
   381  	c2 := make(chan int, 1)
   382  	const procs = 2
   383  	var done sync.WaitGroup
   384  	done.Add(4 * procs)
   385  	for p := 0; p < procs; p++ {
   386  		const iters = 1e3
   387  		go func() {
   388  			for i := 0; i < iters; i++ {
   389  				runtime.Gosched()
   390  				c0 <- 0
   391  			}
   392  			done.Done()
   393  		}()
   394  		go func() {
   395  			for i := 0; i < iters; i++ {
   396  				runtime.Gosched()
   397  				<-c0
   398  			}
   399  			done.Done()
   400  		}()
   401  		go func() {
   402  			for i := 0; i < iters; i++ {
   403  				runtime.Gosched()
   404  				select {
   405  				case c1 <- 0:
   406  				case c2 <- 0:
   407  				}
   408  			}
   409  			done.Done()
   410  		}()
   411  		go func() {
   412  			for i := 0; i < iters; i++ {
   413  				runtime.Gosched()
   414  				select {
   415  				case <-c1:
   416  				case <-c2:
   417  				}
   418  			}
   419  			done.Done()
   420  		}()
   421  	}
   422  	done.Wait()
   423  
   424  	StopTrace()
   425  	events, _, err := parseTrace(buf)
   426  	if err != nil {
   427  		t.Fatalf("failed to parse trace: %v", err)
   428  	}
   429  	// Check that (1) trace does not contain EvFutileWakeup events and
   430  	// (2) there are no consecutive EvGoBlock/EvGCStart/EvGoBlock events
   431  	// (we call runtime.Gosched between all operations, so these would be futile wakeups).
   432  	gs := make(map[uint64]int)
   433  	for _, ev := range events {
   434  		switch ev.Type {
   435  		case trace.EvFutileWakeup:
   436  			t.Fatalf("found EvFutileWakeup event")
   437  		case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect:
   438  			if gs[ev.G] == 2 {
   439  				t.Fatalf("goroutine %v blocked on %v at %v right after start",
   440  					ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
   441  			}
   442  			if gs[ev.G] == 1 {
   443  				t.Fatalf("goroutine %v blocked on %v at %v while blocked",
   444  					ev.G, trace.EventDescriptions[ev.Type].Name, ev.Ts)
   445  			}
   446  			gs[ev.G] = 1
   447  		case trace.EvGoStart:
   448  			if gs[ev.G] == 1 {
   449  				gs[ev.G] = 2
   450  			}
   451  		default:
   452  			delete(gs, ev.G)
   453  		}
   454  	}
   455  }