github.com/activestate/go@v0.0.0-20170614201249-0b81c023a722/src/runtime/stack_test.go (about)

     1  // Copyright 2012 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 runtime_test
     6  
     7  import (
     8  	. "runtime"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  // TestStackMem measures per-thread stack segment cache behavior.
    17  // The test consumed up to 500MB in the past.
    18  func TestStackMem(t *testing.T) {
    19  	const (
    20  		BatchSize      = 32
    21  		BatchCount     = 256
    22  		ArraySize      = 1024
    23  		RecursionDepth = 128
    24  	)
    25  	if testing.Short() {
    26  		return
    27  	}
    28  	defer GOMAXPROCS(GOMAXPROCS(BatchSize))
    29  	s0 := new(MemStats)
    30  	ReadMemStats(s0)
    31  	for b := 0; b < BatchCount; b++ {
    32  		c := make(chan bool, BatchSize)
    33  		for i := 0; i < BatchSize; i++ {
    34  			go func() {
    35  				var f func(k int, a [ArraySize]byte)
    36  				f = func(k int, a [ArraySize]byte) {
    37  					if k == 0 {
    38  						time.Sleep(time.Millisecond)
    39  						return
    40  					}
    41  					f(k-1, a)
    42  				}
    43  				f(RecursionDepth, [ArraySize]byte{})
    44  				c <- true
    45  			}()
    46  		}
    47  		for i := 0; i < BatchSize; i++ {
    48  			<-c
    49  		}
    50  
    51  		// The goroutines have signaled via c that they are ready to exit.
    52  		// Give them a chance to exit by sleeping. If we don't wait, we
    53  		// might not reuse them on the next batch.
    54  		time.Sleep(10 * time.Millisecond)
    55  	}
    56  	s1 := new(MemStats)
    57  	ReadMemStats(s1)
    58  	consumed := int64(s1.StackSys - s0.StackSys)
    59  	t.Logf("Consumed %vMB for stack mem", consumed>>20)
    60  	estimate := int64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness.
    61  	if consumed > estimate {
    62  		t.Fatalf("Stack mem: want %v, got %v", estimate, consumed)
    63  	}
    64  	// Due to broken stack memory accounting (https://golang.org/issue/7468),
    65  	// StackInuse can decrease during function execution, so we cast the values to int64.
    66  	inuse := int64(s1.StackInuse) - int64(s0.StackInuse)
    67  	t.Logf("Inuse %vMB for stack mem", inuse>>20)
    68  	if inuse > 4<<20 {
    69  		t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse)
    70  	}
    71  }
    72  
    73  // Test stack growing in different contexts.
    74  func TestStackGrowth(t *testing.T) {
    75  	t.Parallel()
    76  	var wg sync.WaitGroup
    77  
    78  	// in a normal goroutine
    79  	wg.Add(1)
    80  	go func() {
    81  		defer wg.Done()
    82  		growStack()
    83  	}()
    84  	wg.Wait()
    85  
    86  	// in locked goroutine
    87  	wg.Add(1)
    88  	go func() {
    89  		defer wg.Done()
    90  		LockOSThread()
    91  		growStack()
    92  		UnlockOSThread()
    93  	}()
    94  	wg.Wait()
    95  
    96  	// in finalizer
    97  	wg.Add(1)
    98  	go func() {
    99  		defer wg.Done()
   100  		done := make(chan bool)
   101  		var started uint32
   102  		go func() {
   103  			s := new(string)
   104  			SetFinalizer(s, func(ss *string) {
   105  				atomic.StoreUint32(&started, 1)
   106  				growStack()
   107  				done <- true
   108  			})
   109  			s = nil
   110  			done <- true
   111  		}()
   112  		<-done
   113  		GC()
   114  		select {
   115  		case <-done:
   116  		case <-time.After(20 * time.Second):
   117  			if atomic.LoadUint32(&started) == 0 {
   118  				t.Log("finalizer did not start")
   119  			}
   120  			t.Error("finalizer did not run")
   121  			return
   122  		}
   123  	}()
   124  	wg.Wait()
   125  }
   126  
   127  // ... and in init
   128  //func init() {
   129  //	growStack()
   130  //}
   131  
   132  func growStack() {
   133  	n := 1 << 10
   134  	if testing.Short() {
   135  		n = 1 << 8
   136  	}
   137  	for i := 0; i < n; i++ {
   138  		x := 0
   139  		growStackIter(&x, i)
   140  		if x != i+1 {
   141  			panic("stack is corrupted")
   142  		}
   143  	}
   144  	GC()
   145  }
   146  
   147  // This function is not an anonymous func, so that the compiler can do escape
   148  // analysis and place x on stack (and subsequently stack growth update the pointer).
   149  func growStackIter(p *int, n int) {
   150  	if n == 0 {
   151  		*p = n + 1
   152  		GC()
   153  		return
   154  	}
   155  	*p = n + 1
   156  	x := 0
   157  	growStackIter(&x, n-1)
   158  	if x != n {
   159  		panic("stack is corrupted")
   160  	}
   161  }
   162  
   163  func TestStackGrowthCallback(t *testing.T) {
   164  	t.Parallel()
   165  	var wg sync.WaitGroup
   166  
   167  	// test stack growth at chan op
   168  	wg.Add(1)
   169  	go func() {
   170  		defer wg.Done()
   171  		c := make(chan int, 1)
   172  		growStackWithCallback(func() {
   173  			c <- 1
   174  			<-c
   175  		})
   176  	}()
   177  
   178  	// test stack growth at map op
   179  	wg.Add(1)
   180  	go func() {
   181  		defer wg.Done()
   182  		m := make(map[int]int)
   183  		growStackWithCallback(func() {
   184  			_, _ = m[1]
   185  			m[1] = 1
   186  		})
   187  	}()
   188  
   189  	// test stack growth at goroutine creation
   190  	wg.Add(1)
   191  	go func() {
   192  		defer wg.Done()
   193  		growStackWithCallback(func() {
   194  			done := make(chan bool)
   195  			go func() {
   196  				done <- true
   197  			}()
   198  			<-done
   199  		})
   200  	}()
   201  	wg.Wait()
   202  }
   203  
   204  func growStackWithCallback(cb func()) {
   205  	var f func(n int)
   206  	f = func(n int) {
   207  		if n == 0 {
   208  			cb()
   209  			return
   210  		}
   211  		f(n - 1)
   212  	}
   213  	for i := 0; i < 1<<10; i++ {
   214  		f(i)
   215  	}
   216  }
   217  
   218  // TestDeferPtrs tests the adjustment of Defer's argument pointers (p aka &y)
   219  // during a stack copy.
   220  func set(p *int, x int) {
   221  	*p = x
   222  }
   223  func TestDeferPtrs(t *testing.T) {
   224  	var y int
   225  
   226  	defer func() {
   227  		if y != 42 {
   228  			t.Errorf("defer's stack references were not adjusted appropriately")
   229  		}
   230  	}()
   231  	defer set(&y, 42)
   232  	growStack()
   233  }
   234  
   235  type bigBuf [4 * 1024]byte
   236  
   237  // TestDeferPtrsGoexit is like TestDeferPtrs but exercises the possibility that the
   238  // stack grows as part of starting the deferred function. It calls Goexit at various
   239  // stack depths, forcing the deferred function (with >4kB of args) to be run at
   240  // the bottom of the stack. The goal is to find a stack depth less than 4kB from
   241  // the end of the stack. Each trial runs in a different goroutine so that an earlier
   242  // stack growth does not invalidate a later attempt.
   243  func TestDeferPtrsGoexit(t *testing.T) {
   244  	for i := 0; i < 100; i++ {
   245  		c := make(chan int, 1)
   246  		go testDeferPtrsGoexit(c, i)
   247  		if n := <-c; n != 42 {
   248  			t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n)
   249  		}
   250  	}
   251  }
   252  
   253  func testDeferPtrsGoexit(c chan int, i int) {
   254  	var y int
   255  	defer func() {
   256  		c <- y
   257  	}()
   258  	defer setBig(&y, 42, bigBuf{})
   259  	useStackAndCall(i, Goexit)
   260  }
   261  
   262  func setBig(p *int, x int, b bigBuf) {
   263  	*p = x
   264  }
   265  
   266  // TestDeferPtrsPanic is like TestDeferPtrsGoexit, but it's using panic instead
   267  // of Goexit to run the Defers. Those two are different execution paths
   268  // in the runtime.
   269  func TestDeferPtrsPanic(t *testing.T) {
   270  	for i := 0; i < 100; i++ {
   271  		c := make(chan int, 1)
   272  		go testDeferPtrsGoexit(c, i)
   273  		if n := <-c; n != 42 {
   274  			t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n)
   275  		}
   276  	}
   277  }
   278  
   279  func testDeferPtrsPanic(c chan int, i int) {
   280  	var y int
   281  	defer func() {
   282  		if recover() == nil {
   283  			c <- -1
   284  			return
   285  		}
   286  		c <- y
   287  	}()
   288  	defer setBig(&y, 42, bigBuf{})
   289  	useStackAndCall(i, func() { panic(1) })
   290  }
   291  
   292  // TestPanicUseStack checks that a chain of Panic structs on the stack are
   293  // updated correctly if the stack grows during the deferred execution that
   294  // happens as a result of the panic.
   295  func TestPanicUseStack(t *testing.T) {
   296  	pc := make([]uintptr, 10000)
   297  	defer func() {
   298  		recover()
   299  		Callers(0, pc) // force stack walk
   300  		useStackAndCall(100, func() {
   301  			defer func() {
   302  				recover()
   303  				Callers(0, pc) // force stack walk
   304  				useStackAndCall(200, func() {
   305  					defer func() {
   306  						recover()
   307  						Callers(0, pc) // force stack walk
   308  					}()
   309  					panic(3)
   310  				})
   311  			}()
   312  			panic(2)
   313  		})
   314  	}()
   315  	panic(1)
   316  }
   317  
   318  func TestPanicFar(t *testing.T) {
   319  	var xtree *xtreeNode
   320  	pc := make([]uintptr, 10000)
   321  	defer func() {
   322  		// At this point we created a large stack and unwound
   323  		// it via recovery. Force a stack walk, which will
   324  		// check the stack's consistency.
   325  		Callers(0, pc)
   326  	}()
   327  	defer func() {
   328  		recover()
   329  	}()
   330  	useStackAndCall(100, func() {
   331  		// Kick off the GC and make it do something nontrivial.
   332  		// (This used to force stack barriers to stick around.)
   333  		xtree = makeTree(18)
   334  		// Give the GC time to start scanning stacks.
   335  		time.Sleep(time.Millisecond)
   336  		panic(1)
   337  	})
   338  	_ = xtree
   339  }
   340  
   341  type xtreeNode struct {
   342  	l, r *xtreeNode
   343  }
   344  
   345  func makeTree(d int) *xtreeNode {
   346  	if d == 0 {
   347  		return new(xtreeNode)
   348  	}
   349  	return &xtreeNode{makeTree(d - 1), makeTree(d - 1)}
   350  }
   351  
   352  // use about n KB of stack and call f
   353  func useStackAndCall(n int, f func()) {
   354  	if n == 0 {
   355  		f()
   356  		return
   357  	}
   358  	var b [1024]byte // makes frame about 1KB
   359  	useStackAndCall(n-1+int(b[99]), f)
   360  }
   361  
   362  func useStack(n int) {
   363  	useStackAndCall(n, func() {})
   364  }
   365  
   366  func growing(c chan int, done chan struct{}) {
   367  	for n := range c {
   368  		useStack(n)
   369  		done <- struct{}{}
   370  	}
   371  	done <- struct{}{}
   372  }
   373  
   374  func TestStackCache(t *testing.T) {
   375  	// Allocate a bunch of goroutines and grow their stacks.
   376  	// Repeat a few times to test the stack cache.
   377  	const (
   378  		R = 4
   379  		G = 200
   380  		S = 5
   381  	)
   382  	for i := 0; i < R; i++ {
   383  		var reqchans [G]chan int
   384  		done := make(chan struct{})
   385  		for j := 0; j < G; j++ {
   386  			reqchans[j] = make(chan int)
   387  			go growing(reqchans[j], done)
   388  		}
   389  		for s := 0; s < S; s++ {
   390  			for j := 0; j < G; j++ {
   391  				reqchans[j] <- 1 << uint(s)
   392  			}
   393  			for j := 0; j < G; j++ {
   394  				<-done
   395  			}
   396  		}
   397  		for j := 0; j < G; j++ {
   398  			close(reqchans[j])
   399  		}
   400  		for j := 0; j < G; j++ {
   401  			<-done
   402  		}
   403  	}
   404  }
   405  
   406  func TestStackOutput(t *testing.T) {
   407  	b := make([]byte, 1024)
   408  	stk := string(b[:Stack(b, false)])
   409  	if !strings.HasPrefix(stk, "goroutine ") {
   410  		t.Errorf("Stack (len %d):\n%s", len(stk), stk)
   411  		t.Errorf("Stack output should begin with \"goroutine \"")
   412  	}
   413  }
   414  
   415  func TestStackAllOutput(t *testing.T) {
   416  	b := make([]byte, 1024)
   417  	stk := string(b[:Stack(b, true)])
   418  	if !strings.HasPrefix(stk, "goroutine ") {
   419  		t.Errorf("Stack (len %d):\n%s", len(stk), stk)
   420  		t.Errorf("Stack output should begin with \"goroutine \"")
   421  	}
   422  }
   423  
   424  func TestStackPanic(t *testing.T) {
   425  	// Test that stack copying copies panics correctly. This is difficult
   426  	// to test because it is very unlikely that the stack will be copied
   427  	// in the middle of gopanic. But it can happen.
   428  	// To make this test effective, edit panic.go:gopanic and uncomment
   429  	// the GC() call just before freedefer(d).
   430  	defer func() {
   431  		if x := recover(); x == nil {
   432  			t.Errorf("recover failed")
   433  		}
   434  	}()
   435  	useStack(32)
   436  	panic("test panic")
   437  }
   438  
   439  func BenchmarkStackCopy(b *testing.B) {
   440  	c := make(chan bool)
   441  	for i := 0; i < b.N; i++ {
   442  		go func() {
   443  			count(1000000)
   444  			c <- true
   445  		}()
   446  		<-c
   447  	}
   448  }
   449  
   450  func count(n int) int {
   451  	if n == 0 {
   452  		return 0
   453  	}
   454  	return 1 + count(n-1)
   455  }
   456  
   457  func BenchmarkStackCopyNoCache(b *testing.B) {
   458  	c := make(chan bool)
   459  	for i := 0; i < b.N; i++ {
   460  		go func() {
   461  			count1(1000000)
   462  			c <- true
   463  		}()
   464  		<-c
   465  	}
   466  }
   467  
   468  func count1(n int) int {
   469  	if n == 0 {
   470  		return 0
   471  	}
   472  	return 1 + count2(n-1)
   473  }
   474  
   475  func count2(n int) int {
   476  	if n == 0 {
   477  		return 0
   478  	}
   479  	return 1 + count3(n-1)
   480  }
   481  
   482  func count3(n int) int {
   483  	if n == 0 {
   484  		return 0
   485  	}
   486  	return 1 + count4(n-1)
   487  }
   488  
   489  func count4(n int) int {
   490  	if n == 0 {
   491  		return 0
   492  	}
   493  	return 1 + count5(n-1)
   494  }
   495  
   496  func count5(n int) int {
   497  	if n == 0 {
   498  		return 0
   499  	}
   500  	return 1 + count6(n-1)
   501  }
   502  
   503  func count6(n int) int {
   504  	if n == 0 {
   505  		return 0
   506  	}
   507  	return 1 + count7(n-1)
   508  }
   509  
   510  func count7(n int) int {
   511  	if n == 0 {
   512  		return 0
   513  	}
   514  	return 1 + count8(n-1)
   515  }
   516  
   517  func count8(n int) int {
   518  	if n == 0 {
   519  		return 0
   520  	}
   521  	return 1 + count9(n-1)
   522  }
   523  
   524  func count9(n int) int {
   525  	if n == 0 {
   526  		return 0
   527  	}
   528  	return 1 + count10(n-1)
   529  }
   530  
   531  func count10(n int) int {
   532  	if n == 0 {
   533  		return 0
   534  	}
   535  	return 1 + count11(n-1)
   536  }
   537  
   538  func count11(n int) int {
   539  	if n == 0 {
   540  		return 0
   541  	}
   542  	return 1 + count12(n-1)
   543  }
   544  
   545  func count12(n int) int {
   546  	if n == 0 {
   547  		return 0
   548  	}
   549  	return 1 + count13(n-1)
   550  }
   551  
   552  func count13(n int) int {
   553  	if n == 0 {
   554  		return 0
   555  	}
   556  	return 1 + count14(n-1)
   557  }
   558  
   559  func count14(n int) int {
   560  	if n == 0 {
   561  		return 0
   562  	}
   563  	return 1 + count15(n-1)
   564  }
   565  
   566  func count15(n int) int {
   567  	if n == 0 {
   568  		return 0
   569  	}
   570  	return 1 + count16(n-1)
   571  }
   572  
   573  func count16(n int) int {
   574  	if n == 0 {
   575  		return 0
   576  	}
   577  	return 1 + count17(n-1)
   578  }
   579  
   580  func count17(n int) int {
   581  	if n == 0 {
   582  		return 0
   583  	}
   584  	return 1 + count18(n-1)
   585  }
   586  
   587  func count18(n int) int {
   588  	if n == 0 {
   589  		return 0
   590  	}
   591  	return 1 + count19(n-1)
   592  }
   593  
   594  func count19(n int) int {
   595  	if n == 0 {
   596  		return 0
   597  	}
   598  	return 1 + count20(n-1)
   599  }
   600  
   601  func count20(n int) int {
   602  	if n == 0 {
   603  		return 0
   604  	}
   605  	return 1 + count21(n-1)
   606  }
   607  
   608  func count21(n int) int {
   609  	if n == 0 {
   610  		return 0
   611  	}
   612  	return 1 + count22(n-1)
   613  }
   614  
   615  func count22(n int) int {
   616  	if n == 0 {
   617  		return 0
   618  	}
   619  	return 1 + count23(n-1)
   620  }
   621  
   622  func count23(n int) int {
   623  	if n == 0 {
   624  		return 0
   625  	}
   626  	return 1 + count1(n-1)
   627  }