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