...

Source file src/runtime/stack_test.go

Documentation: runtime

     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  	"fmt"
     9  	"internal/testenv"
    10  	"reflect"
    11  	"regexp"
    12  	. "runtime"
    13  	"strings"
    14  	"sync"
    15  	"sync/atomic"
    16  	"testing"
    17  	"time"
    18  	_ "unsafe" // for go:linkname
    19  )
    20  
    21  // TestStackMem measures per-thread stack segment cache behavior.
    22  // The test consumed up to 500MB in the past.
    23  func TestStackMem(t *testing.T) {
    24  	const (
    25  		BatchSize      = 32
    26  		BatchCount     = 256
    27  		ArraySize      = 1024
    28  		RecursionDepth = 128
    29  	)
    30  	if testing.Short() {
    31  		return
    32  	}
    33  	defer GOMAXPROCS(GOMAXPROCS(BatchSize))
    34  	s0 := new(MemStats)
    35  	ReadMemStats(s0)
    36  	for b := 0; b < BatchCount; b++ {
    37  		c := make(chan bool, BatchSize)
    38  		for i := 0; i < BatchSize; i++ {
    39  			go func() {
    40  				var f func(k int, a [ArraySize]byte)
    41  				f = func(k int, a [ArraySize]byte) {
    42  					if k == 0 {
    43  						time.Sleep(time.Millisecond)
    44  						return
    45  					}
    46  					f(k-1, a)
    47  				}
    48  				f(RecursionDepth, [ArraySize]byte{})
    49  				c <- true
    50  			}()
    51  		}
    52  		for i := 0; i < BatchSize; i++ {
    53  			<-c
    54  		}
    55  
    56  		// The goroutines have signaled via c that they are ready to exit.
    57  		// Give them a chance to exit by sleeping. If we don't wait, we
    58  		// might not reuse them on the next batch.
    59  		time.Sleep(10 * time.Millisecond)
    60  	}
    61  	s1 := new(MemStats)
    62  	ReadMemStats(s1)
    63  	consumed := int64(s1.StackSys - s0.StackSys)
    64  	t.Logf("Consumed %vMB for stack mem", consumed>>20)
    65  	estimate := int64(8 * BatchSize * ArraySize * RecursionDepth) // 8 is to reduce flakiness.
    66  	if consumed > estimate {
    67  		t.Fatalf("Stack mem: want %v, got %v", estimate, consumed)
    68  	}
    69  	// Due to broken stack memory accounting (https://golang.org/issue/7468),
    70  	// StackInuse can decrease during function execution, so we cast the values to int64.
    71  	inuse := int64(s1.StackInuse) - int64(s0.StackInuse)
    72  	t.Logf("Inuse %vMB for stack mem", inuse>>20)
    73  	if inuse > 4<<20 {
    74  		t.Fatalf("Stack inuse: want %v, got %v", 4<<20, inuse)
    75  	}
    76  }
    77  
    78  // Test stack growing in different contexts.
    79  func TestStackGrowth(t *testing.T) {
    80  	if *flagQuick {
    81  		t.Skip("-quick")
    82  	}
    83  
    84  	var wg sync.WaitGroup
    85  
    86  	// in a normal goroutine
    87  	var growDuration time.Duration // For debugging failures
    88  	wg.Add(1)
    89  	go func() {
    90  		defer wg.Done()
    91  		start := time.Now()
    92  		growStack(nil)
    93  		growDuration = time.Since(start)
    94  	}()
    95  	wg.Wait()
    96  	t.Log("first growStack took", growDuration)
    97  
    98  	// in locked goroutine
    99  	wg.Add(1)
   100  	go func() {
   101  		defer wg.Done()
   102  		LockOSThread()
   103  		growStack(nil)
   104  		UnlockOSThread()
   105  	}()
   106  	wg.Wait()
   107  
   108  	// in finalizer
   109  	var finalizerStart time.Time
   110  	var started atomic.Bool
   111  	var progress atomic.Uint32
   112  	wg.Add(1)
   113  	s := new(string) // Must be of a type that avoids the tiny allocator, or else the finalizer might not run.
   114  	SetFinalizer(s, func(ss *string) {
   115  		defer wg.Done()
   116  		finalizerStart = time.Now()
   117  		started.Store(true)
   118  		growStack(&progress)
   119  	})
   120  	setFinalizerTime := time.Now()
   121  	s = nil
   122  
   123  	if d, ok := t.Deadline(); ok {
   124  		// Pad the timeout by an arbitrary 5% to give the AfterFunc time to run.
   125  		timeout := time.Until(d) * 19 / 20
   126  		timer := time.AfterFunc(timeout, func() {
   127  			// Panic — instead of calling t.Error and returning from the test — so
   128  			// that we get a useful goroutine dump if the test times out, especially
   129  			// if GOTRACEBACK=system or GOTRACEBACK=crash is set.
   130  			if !started.Load() {
   131  				panic("finalizer did not start")
   132  			} else {
   133  				panic(fmt.Sprintf("finalizer started %s ago (%s after registration) and ran %d iterations, but did not return", time.Since(finalizerStart), finalizerStart.Sub(setFinalizerTime), progress.Load()))
   134  			}
   135  		})
   136  		defer timer.Stop()
   137  	}
   138  
   139  	GC()
   140  	wg.Wait()
   141  	t.Logf("finalizer started after %s and ran %d iterations in %v", finalizerStart.Sub(setFinalizerTime), progress.Load(), time.Since(finalizerStart))
   142  }
   143  
   144  // ... and in init
   145  //func init() {
   146  //	growStack()
   147  //}
   148  
   149  func growStack(progress *atomic.Uint32) {
   150  	n := 1 << 10
   151  	if testing.Short() {
   152  		n = 1 << 8
   153  	}
   154  	for i := 0; i < n; i++ {
   155  		x := 0
   156  		growStackIter(&x, i)
   157  		if x != i+1 {
   158  			panic("stack is corrupted")
   159  		}
   160  		if progress != nil {
   161  			progress.Store(uint32(i))
   162  		}
   163  	}
   164  	GC()
   165  }
   166  
   167  // This function is not an anonymous func, so that the compiler can do escape
   168  // analysis and place x on stack (and subsequently stack growth update the pointer).
   169  func growStackIter(p *int, n int) {
   170  	if n == 0 {
   171  		*p = n + 1
   172  		GC()
   173  		return
   174  	}
   175  	*p = n + 1
   176  	x := 0
   177  	growStackIter(&x, n-1)
   178  	if x != n {
   179  		panic("stack is corrupted")
   180  	}
   181  }
   182  
   183  func TestStackGrowthCallback(t *testing.T) {
   184  	t.Parallel()
   185  	var wg sync.WaitGroup
   186  
   187  	// test stack growth at chan op
   188  	wg.Add(1)
   189  	go func() {
   190  		defer wg.Done()
   191  		c := make(chan int, 1)
   192  		growStackWithCallback(func() {
   193  			c <- 1
   194  			<-c
   195  		})
   196  	}()
   197  
   198  	// test stack growth at map op
   199  	wg.Add(1)
   200  	go func() {
   201  		defer wg.Done()
   202  		m := make(map[int]int)
   203  		growStackWithCallback(func() {
   204  			_, _ = m[1]
   205  			m[1] = 1
   206  		})
   207  	}()
   208  
   209  	// test stack growth at goroutine creation
   210  	wg.Add(1)
   211  	go func() {
   212  		defer wg.Done()
   213  		growStackWithCallback(func() {
   214  			done := make(chan bool)
   215  			go func() {
   216  				done <- true
   217  			}()
   218  			<-done
   219  		})
   220  	}()
   221  	wg.Wait()
   222  }
   223  
   224  func growStackWithCallback(cb func()) {
   225  	var f func(n int)
   226  	f = func(n int) {
   227  		if n == 0 {
   228  			cb()
   229  			return
   230  		}
   231  		f(n - 1)
   232  	}
   233  	for i := 0; i < 1<<10; i++ {
   234  		f(i)
   235  	}
   236  }
   237  
   238  // TestDeferPtrs tests the adjustment of Defer's argument pointers (p aka &y)
   239  // during a stack copy.
   240  func set(p *int, x int) {
   241  	*p = x
   242  }
   243  func TestDeferPtrs(t *testing.T) {
   244  	var y int
   245  
   246  	defer func() {
   247  		if y != 42 {
   248  			t.Errorf("defer's stack references were not adjusted appropriately")
   249  		}
   250  	}()
   251  	defer set(&y, 42)
   252  	growStack(nil)
   253  }
   254  
   255  type bigBuf [4 * 1024]byte
   256  
   257  // TestDeferPtrsGoexit is like TestDeferPtrs but exercises the possibility that the
   258  // stack grows as part of starting the deferred function. It calls Goexit at various
   259  // stack depths, forcing the deferred function (with >4kB of args) to be run at
   260  // the bottom of the stack. The goal is to find a stack depth less than 4kB from
   261  // the end of the stack. Each trial runs in a different goroutine so that an earlier
   262  // stack growth does not invalidate a later attempt.
   263  func TestDeferPtrsGoexit(t *testing.T) {
   264  	for i := 0; i < 100; i++ {
   265  		c := make(chan int, 1)
   266  		go testDeferPtrsGoexit(c, i)
   267  		if n := <-c; n != 42 {
   268  			t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n)
   269  		}
   270  	}
   271  }
   272  
   273  func testDeferPtrsGoexit(c chan int, i int) {
   274  	var y int
   275  	defer func() {
   276  		c <- y
   277  	}()
   278  	defer setBig(&y, 42, bigBuf{})
   279  	useStackAndCall(i, Goexit)
   280  }
   281  
   282  func setBig(p *int, x int, b bigBuf) {
   283  	*p = x
   284  }
   285  
   286  // TestDeferPtrsPanic is like TestDeferPtrsGoexit, but it's using panic instead
   287  // of Goexit to run the Defers. Those two are different execution paths
   288  // in the runtime.
   289  func TestDeferPtrsPanic(t *testing.T) {
   290  	for i := 0; i < 100; i++ {
   291  		c := make(chan int, 1)
   292  		go testDeferPtrsGoexit(c, i)
   293  		if n := <-c; n != 42 {
   294  			t.Fatalf("defer's stack references were not adjusted appropriately (i=%d n=%d)", i, n)
   295  		}
   296  	}
   297  }
   298  
   299  func testDeferPtrsPanic(c chan int, i int) {
   300  	var y int
   301  	defer func() {
   302  		if recover() == nil {
   303  			c <- -1
   304  			return
   305  		}
   306  		c <- y
   307  	}()
   308  	defer setBig(&y, 42, bigBuf{})
   309  	useStackAndCall(i, func() { panic(1) })
   310  }
   311  
   312  //go:noinline
   313  func testDeferLeafSigpanic1() {
   314  	// Cause a sigpanic to be injected in this frame.
   315  	//
   316  	// This function has to be declared before
   317  	// TestDeferLeafSigpanic so the runtime will crash if we think
   318  	// this function's continuation PC is in
   319  	// TestDeferLeafSigpanic.
   320  	*(*int)(nil) = 0
   321  }
   322  
   323  // TestDeferLeafSigpanic tests defer matching around leaf functions
   324  // that sigpanic. This is tricky because on LR machines the outer
   325  // function and the inner function have the same SP, but it's critical
   326  // that we match up the defer correctly to get the right liveness map.
   327  // See issue #25499.
   328  func TestDeferLeafSigpanic(t *testing.T) {
   329  	// Push a defer that will walk the stack.
   330  	defer func() {
   331  		if err := recover(); err == nil {
   332  			t.Fatal("expected panic from nil pointer")
   333  		}
   334  		GC()
   335  	}()
   336  	// Call a leaf function. We must set up the exact call stack:
   337  	//
   338  	//  deferring function -> leaf function -> sigpanic
   339  	//
   340  	// On LR machines, the leaf function will have the same SP as
   341  	// the SP pushed for the defer frame.
   342  	testDeferLeafSigpanic1()
   343  }
   344  
   345  // TestPanicUseStack checks that a chain of Panic structs on the stack are
   346  // updated correctly if the stack grows during the deferred execution that
   347  // happens as a result of the panic.
   348  func TestPanicUseStack(t *testing.T) {
   349  	pc := make([]uintptr, 10000)
   350  	defer func() {
   351  		recover()
   352  		Callers(0, pc) // force stack walk
   353  		useStackAndCall(100, func() {
   354  			defer func() {
   355  				recover()
   356  				Callers(0, pc) // force stack walk
   357  				useStackAndCall(200, func() {
   358  					defer func() {
   359  						recover()
   360  						Callers(0, pc) // force stack walk
   361  					}()
   362  					panic(3)
   363  				})
   364  			}()
   365  			panic(2)
   366  		})
   367  	}()
   368  	panic(1)
   369  }
   370  
   371  func TestPanicFar(t *testing.T) {
   372  	var xtree *xtreeNode
   373  	pc := make([]uintptr, 10000)
   374  	defer func() {
   375  		// At this point we created a large stack and unwound
   376  		// it via recovery. Force a stack walk, which will
   377  		// check the stack's consistency.
   378  		Callers(0, pc)
   379  	}()
   380  	defer func() {
   381  		recover()
   382  	}()
   383  	useStackAndCall(100, func() {
   384  		// Kick off the GC and make it do something nontrivial.
   385  		// (This used to force stack barriers to stick around.)
   386  		xtree = makeTree(18)
   387  		// Give the GC time to start scanning stacks.
   388  		time.Sleep(time.Millisecond)
   389  		panic(1)
   390  	})
   391  	_ = xtree
   392  }
   393  
   394  type xtreeNode struct {
   395  	l, r *xtreeNode
   396  }
   397  
   398  func makeTree(d int) *xtreeNode {
   399  	if d == 0 {
   400  		return new(xtreeNode)
   401  	}
   402  	return &xtreeNode{makeTree(d - 1), makeTree(d - 1)}
   403  }
   404  
   405  // use about n KB of stack and call f
   406  func useStackAndCall(n int, f func()) {
   407  	if n == 0 {
   408  		f()
   409  		return
   410  	}
   411  	var b [1024]byte // makes frame about 1KB
   412  	useStackAndCall(n-1+int(b[99]), f)
   413  }
   414  
   415  func useStack(n int) {
   416  	useStackAndCall(n, func() {})
   417  }
   418  
   419  func growing(c chan int, done chan struct{}) {
   420  	for n := range c {
   421  		useStack(n)
   422  		done <- struct{}{}
   423  	}
   424  	done <- struct{}{}
   425  }
   426  
   427  func TestStackCache(t *testing.T) {
   428  	// Allocate a bunch of goroutines and grow their stacks.
   429  	// Repeat a few times to test the stack cache.
   430  	const (
   431  		R = 4
   432  		G = 200
   433  		S = 5
   434  	)
   435  	for i := 0; i < R; i++ {
   436  		var reqchans [G]chan int
   437  		done := make(chan struct{})
   438  		for j := 0; j < G; j++ {
   439  			reqchans[j] = make(chan int)
   440  			go growing(reqchans[j], done)
   441  		}
   442  		for s := 0; s < S; s++ {
   443  			for j := 0; j < G; j++ {
   444  				reqchans[j] <- 1 << uint(s)
   445  			}
   446  			for j := 0; j < G; j++ {
   447  				<-done
   448  			}
   449  		}
   450  		for j := 0; j < G; j++ {
   451  			close(reqchans[j])
   452  		}
   453  		for j := 0; j < G; j++ {
   454  			<-done
   455  		}
   456  	}
   457  }
   458  
   459  func TestStackOutput(t *testing.T) {
   460  	b := make([]byte, 1024)
   461  	stk := string(b[:Stack(b, false)])
   462  	if !strings.HasPrefix(stk, "goroutine ") {
   463  		t.Errorf("Stack (len %d):\n%s", len(stk), stk)
   464  		t.Errorf("Stack output should begin with \"goroutine \"")
   465  	}
   466  }
   467  
   468  func TestStackAllOutput(t *testing.T) {
   469  	b := make([]byte, 1024)
   470  	stk := string(b[:Stack(b, true)])
   471  	if !strings.HasPrefix(stk, "goroutine ") {
   472  		t.Errorf("Stack (len %d):\n%s", len(stk), stk)
   473  		t.Errorf("Stack output should begin with \"goroutine \"")
   474  	}
   475  }
   476  
   477  func TestStackPanic(t *testing.T) {
   478  	// Test that stack copying copies panics correctly. This is difficult
   479  	// to test because it is very unlikely that the stack will be copied
   480  	// in the middle of gopanic. But it can happen.
   481  	// To make this test effective, edit panic.go:gopanic and uncomment
   482  	// the GC() call just before freedefer(d).
   483  	defer func() {
   484  		if x := recover(); x == nil {
   485  			t.Errorf("recover failed")
   486  		}
   487  	}()
   488  	useStack(32)
   489  	panic("test panic")
   490  }
   491  
   492  func BenchmarkStackCopyPtr(b *testing.B) {
   493  	c := make(chan bool)
   494  	for i := 0; i < b.N; i++ {
   495  		go func() {
   496  			i := 1000000
   497  			countp(&i)
   498  			c <- true
   499  		}()
   500  		<-c
   501  	}
   502  }
   503  
   504  func countp(n *int) {
   505  	if *n == 0 {
   506  		return
   507  	}
   508  	*n--
   509  	countp(n)
   510  }
   511  
   512  func BenchmarkStackCopy(b *testing.B) {
   513  	c := make(chan bool)
   514  	for i := 0; i < b.N; i++ {
   515  		go func() {
   516  			count(1000000)
   517  			c <- true
   518  		}()
   519  		<-c
   520  	}
   521  }
   522  
   523  func count(n int) int {
   524  	if n == 0 {
   525  		return 0
   526  	}
   527  	return 1 + count(n-1)
   528  }
   529  
   530  func BenchmarkStackCopyNoCache(b *testing.B) {
   531  	c := make(chan bool)
   532  	for i := 0; i < b.N; i++ {
   533  		go func() {
   534  			count1(1000000)
   535  			c <- true
   536  		}()
   537  		<-c
   538  	}
   539  }
   540  
   541  func count1(n int) int {
   542  	if n <= 0 {
   543  		return 0
   544  	}
   545  	return 1 + count2(n-1)
   546  }
   547  
   548  func count2(n int) int  { return 1 + count3(n-1) }
   549  func count3(n int) int  { return 1 + count4(n-1) }
   550  func count4(n int) int  { return 1 + count5(n-1) }
   551  func count5(n int) int  { return 1 + count6(n-1) }
   552  func count6(n int) int  { return 1 + count7(n-1) }
   553  func count7(n int) int  { return 1 + count8(n-1) }
   554  func count8(n int) int  { return 1 + count9(n-1) }
   555  func count9(n int) int  { return 1 + count10(n-1) }
   556  func count10(n int) int { return 1 + count11(n-1) }
   557  func count11(n int) int { return 1 + count12(n-1) }
   558  func count12(n int) int { return 1 + count13(n-1) }
   559  func count13(n int) int { return 1 + count14(n-1) }
   560  func count14(n int) int { return 1 + count15(n-1) }
   561  func count15(n int) int { return 1 + count16(n-1) }
   562  func count16(n int) int { return 1 + count17(n-1) }
   563  func count17(n int) int { return 1 + count18(n-1) }
   564  func count18(n int) int { return 1 + count19(n-1) }
   565  func count19(n int) int { return 1 + count20(n-1) }
   566  func count20(n int) int { return 1 + count21(n-1) }
   567  func count21(n int) int { return 1 + count22(n-1) }
   568  func count22(n int) int { return 1 + count23(n-1) }
   569  func count23(n int) int { return 1 + count1(n-1) }
   570  
   571  type stkobjT struct {
   572  	p *stkobjT
   573  	x int64
   574  	y [20]int // consume some stack
   575  }
   576  
   577  // Sum creates a linked list of stkobjTs.
   578  func Sum(n int64, p *stkobjT) {
   579  	if n == 0 {
   580  		return
   581  	}
   582  	s := stkobjT{p: p, x: n}
   583  	Sum(n-1, &s)
   584  	p.x += s.x
   585  }
   586  
   587  func BenchmarkStackCopyWithStkobj(b *testing.B) {
   588  	c := make(chan bool)
   589  	for i := 0; i < b.N; i++ {
   590  		go func() {
   591  			var s stkobjT
   592  			Sum(100000, &s)
   593  			c <- true
   594  		}()
   595  		<-c
   596  	}
   597  }
   598  
   599  func BenchmarkIssue18138(b *testing.B) {
   600  	// Channel with N "can run a goroutine" tokens
   601  	const N = 10
   602  	c := make(chan []byte, N)
   603  	for i := 0; i < N; i++ {
   604  		c <- make([]byte, 1)
   605  	}
   606  
   607  	for i := 0; i < b.N; i++ {
   608  		<-c // get token
   609  		go func() {
   610  			useStackPtrs(1000, false) // uses ~1MB max
   611  			m := make([]byte, 8192)   // make GC trigger occasionally
   612  			c <- m                    // return token
   613  		}()
   614  	}
   615  }
   616  
   617  func useStackPtrs(n int, b bool) {
   618  	if b {
   619  		// This code contributes to the stack frame size, and hence to the
   620  		// stack copying cost. But since b is always false, it costs no
   621  		// execution time (not even the zeroing of a).
   622  		var a [128]*int // 1KB of pointers
   623  		a[n] = &n
   624  		n = *a[0]
   625  	}
   626  	if n == 0 {
   627  		return
   628  	}
   629  	useStackPtrs(n-1, b)
   630  }
   631  
   632  type structWithMethod struct{}
   633  
   634  func (s structWithMethod) caller() string {
   635  	_, file, line, ok := Caller(1)
   636  	if !ok {
   637  		panic("Caller failed")
   638  	}
   639  	return fmt.Sprintf("%s:%d", file, line)
   640  }
   641  
   642  func (s structWithMethod) callers() []uintptr {
   643  	pc := make([]uintptr, 16)
   644  	return pc[:Callers(0, pc)]
   645  }
   646  
   647  func (s structWithMethod) stack() string {
   648  	buf := make([]byte, 4<<10)
   649  	return string(buf[:Stack(buf, false)])
   650  }
   651  
   652  func (s structWithMethod) nop() {}
   653  
   654  func (s structWithMethod) inlinablePanic() { panic("panic") }
   655  
   656  func TestStackWrapperCaller(t *testing.T) {
   657  	var d structWithMethod
   658  	// Force the compiler to construct a wrapper method.
   659  	wrapper := (*structWithMethod).caller
   660  	// Check that the wrapper doesn't affect the stack trace.
   661  	if dc, ic := d.caller(), wrapper(&d); dc != ic {
   662  		t.Fatalf("direct caller %q != indirect caller %q", dc, ic)
   663  	}
   664  }
   665  
   666  func TestStackWrapperCallers(t *testing.T) {
   667  	var d structWithMethod
   668  	wrapper := (*structWithMethod).callers
   669  	// Check that <autogenerated> doesn't appear in the stack trace.
   670  	pcs := wrapper(&d)
   671  	frames := CallersFrames(pcs)
   672  	for {
   673  		fr, more := frames.Next()
   674  		if fr.File == "<autogenerated>" {
   675  			t.Fatalf("<autogenerated> appears in stack trace: %+v", fr)
   676  		}
   677  		if !more {
   678  			break
   679  		}
   680  	}
   681  }
   682  
   683  func TestStackWrapperStack(t *testing.T) {
   684  	var d structWithMethod
   685  	wrapper := (*structWithMethod).stack
   686  	// Check that <autogenerated> doesn't appear in the stack trace.
   687  	stk := wrapper(&d)
   688  	if strings.Contains(stk, "<autogenerated>") {
   689  		t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
   690  	}
   691  }
   692  
   693  func TestStackWrapperStackInlinePanic(t *testing.T) {
   694  	// Test that inline unwinding correctly tracks the callee by creating a
   695  	// stack of the form wrapper -> inlined function -> panic. If we mess up
   696  	// callee tracking, it will look like the wrapper called panic and we'll see
   697  	// the wrapper in the stack trace.
   698  	var d structWithMethod
   699  	wrapper := (*structWithMethod).inlinablePanic
   700  	defer func() {
   701  		err := recover()
   702  		if err == nil {
   703  			t.Fatalf("expected panic")
   704  		}
   705  		buf := make([]byte, 4<<10)
   706  		stk := string(buf[:Stack(buf, false)])
   707  		if strings.Contains(stk, "<autogenerated>") {
   708  			t.Fatalf("<autogenerated> appears in stack trace:\n%s", stk)
   709  		}
   710  		// Self-check: make sure inlinablePanic got inlined.
   711  		if !testenv.OptimizationOff() {
   712  			if !strings.Contains(stk, "inlinablePanic(...)") {
   713  				t.Fatalf("inlinablePanic not inlined")
   714  			}
   715  		}
   716  	}()
   717  	wrapper(&d)
   718  }
   719  
   720  type I interface {
   721  	M()
   722  }
   723  
   724  func TestStackWrapperStackPanic(t *testing.T) {
   725  	t.Run("sigpanic", func(t *testing.T) {
   726  		// nil calls to interface methods cause a sigpanic.
   727  		testStackWrapperPanic(t, func() { I.M(nil) }, "runtime_test.I.M")
   728  	})
   729  	t.Run("panicwrap", func(t *testing.T) {
   730  		// Nil calls to value method wrappers call panicwrap.
   731  		wrapper := (*structWithMethod).nop
   732  		testStackWrapperPanic(t, func() { wrapper(nil) }, "runtime_test.(*structWithMethod).nop")
   733  	})
   734  }
   735  
   736  func testStackWrapperPanic(t *testing.T, cb func(), expect string) {
   737  	// Test that the stack trace from a panicking wrapper includes
   738  	// the wrapper, even though elide these when they don't panic.
   739  	t.Run("CallersFrames", func(t *testing.T) {
   740  		defer func() {
   741  			err := recover()
   742  			if err == nil {
   743  				t.Fatalf("expected panic")
   744  			}
   745  			pcs := make([]uintptr, 10)
   746  			n := Callers(0, pcs)
   747  			frames := CallersFrames(pcs[:n])
   748  			for {
   749  				frame, more := frames.Next()
   750  				t.Log(frame.Function)
   751  				if frame.Function == expect {
   752  					return
   753  				}
   754  				if !more {
   755  					break
   756  				}
   757  			}
   758  			t.Fatalf("panicking wrapper %s missing from stack trace", expect)
   759  		}()
   760  		cb()
   761  	})
   762  	t.Run("Stack", func(t *testing.T) {
   763  		defer func() {
   764  			err := recover()
   765  			if err == nil {
   766  				t.Fatalf("expected panic")
   767  			}
   768  			buf := make([]byte, 4<<10)
   769  			stk := string(buf[:Stack(buf, false)])
   770  			if !strings.Contains(stk, "\n"+expect) {
   771  				t.Fatalf("panicking wrapper %s missing from stack trace:\n%s", expect, stk)
   772  			}
   773  		}()
   774  		cb()
   775  	})
   776  }
   777  
   778  func TestCallersFromWrapper(t *testing.T) {
   779  	// Test that invoking CallersFrames on a stack where the first
   780  	// PC is an autogenerated wrapper keeps the wrapper in the
   781  	// trace. Normally we elide these, assuming that the wrapper
   782  	// calls the thing you actually wanted to see, but in this
   783  	// case we need to keep it.
   784  	pc := reflect.ValueOf(I.M).Pointer()
   785  	frames := CallersFrames([]uintptr{pc})
   786  	frame, more := frames.Next()
   787  	if frame.Function != "runtime_test.I.M" {
   788  		t.Fatalf("want function %s, got %s", "runtime_test.I.M", frame.Function)
   789  	}
   790  	if more {
   791  		t.Fatalf("want 1 frame, got > 1")
   792  	}
   793  }
   794  
   795  func TestTracebackSystemstack(t *testing.T) {
   796  	if GOARCH == "ppc64" || GOARCH == "ppc64le" {
   797  		t.Skip("systemstack tail call not implemented on ppc64x")
   798  	}
   799  
   800  	// Test that profiles correctly jump over systemstack,
   801  	// including nested systemstack calls.
   802  	pcs := make([]uintptr, 20)
   803  	pcs = pcs[:TracebackSystemstack(pcs, 5)]
   804  	// Check that runtime.TracebackSystemstack appears five times
   805  	// and that we see TestTracebackSystemstack.
   806  	countIn, countOut := 0, 0
   807  	frames := CallersFrames(pcs)
   808  	var tb strings.Builder
   809  	for {
   810  		frame, more := frames.Next()
   811  		fmt.Fprintf(&tb, "\n%s+0x%x %s:%d", frame.Function, frame.PC-frame.Entry, frame.File, frame.Line)
   812  		switch frame.Function {
   813  		case "runtime.TracebackSystemstack":
   814  			countIn++
   815  		case "runtime_test.TestTracebackSystemstack":
   816  			countOut++
   817  		}
   818  		if !more {
   819  			break
   820  		}
   821  	}
   822  	if countIn != 5 || countOut != 1 {
   823  		t.Fatalf("expected 5 calls to TracebackSystemstack and 1 call to TestTracebackSystemstack, got:%s", tb.String())
   824  	}
   825  }
   826  
   827  func TestTracebackAncestors(t *testing.T) {
   828  	goroutineRegex := regexp.MustCompile(`goroutine [0-9]+ \[`)
   829  	for _, tracebackDepth := range []int{0, 1, 5, 50} {
   830  		output := runTestProg(t, "testprog", "TracebackAncestors", fmt.Sprintf("GODEBUG=tracebackancestors=%d", tracebackDepth))
   831  
   832  		numGoroutines := 3
   833  		numFrames := 2
   834  		ancestorsExpected := numGoroutines
   835  		if numGoroutines > tracebackDepth {
   836  			ancestorsExpected = tracebackDepth
   837  		}
   838  
   839  		matches := goroutineRegex.FindAllStringSubmatch(output, -1)
   840  		if len(matches) != 2 {
   841  			t.Fatalf("want 2 goroutines, got:\n%s", output)
   842  		}
   843  
   844  		// Check functions in the traceback.
   845  		fns := []string{"main.recurseThenCallGo", "main.main", "main.printStack", "main.TracebackAncestors"}
   846  		for _, fn := range fns {
   847  			if !strings.Contains(output, "\n"+fn+"(") {
   848  				t.Fatalf("expected %q function in traceback:\n%s", fn, output)
   849  			}
   850  		}
   851  
   852  		if want, count := "originating from goroutine", ancestorsExpected; strings.Count(output, want) != count {
   853  			t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
   854  		}
   855  
   856  		if want, count := "main.recurseThenCallGo(...)", ancestorsExpected*(numFrames+1); strings.Count(output, want) != count {
   857  			t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
   858  		}
   859  
   860  		if want, count := "main.recurseThenCallGo(0x", 1; strings.Count(output, want) != count {
   861  			t.Errorf("output does not contain %d instances of %q:\n%s", count, want, output)
   862  		}
   863  	}
   864  }
   865  
   866  // Test that defer closure is correctly scanned when the stack is scanned.
   867  func TestDeferLiveness(t *testing.T) {
   868  	output := runTestProg(t, "testprog", "DeferLiveness", "GODEBUG=clobberfree=1")
   869  	if output != "" {
   870  		t.Errorf("output:\n%s\n\nwant no output", output)
   871  	}
   872  }
   873  
   874  func TestDeferHeapAndStack(t *testing.T) {
   875  	P := 4     // processors
   876  	N := 10000 //iterations
   877  	D := 200   // stack depth
   878  
   879  	if testing.Short() {
   880  		P /= 2
   881  		N /= 10
   882  		D /= 10
   883  	}
   884  	c := make(chan bool)
   885  	for p := 0; p < P; p++ {
   886  		go func() {
   887  			for i := 0; i < N; i++ {
   888  				if deferHeapAndStack(D) != 2*D {
   889  					panic("bad result")
   890  				}
   891  			}
   892  			c <- true
   893  		}()
   894  	}
   895  	for p := 0; p < P; p++ {
   896  		<-c
   897  	}
   898  }
   899  
   900  // deferHeapAndStack(n) computes 2*n
   901  func deferHeapAndStack(n int) (r int) {
   902  	if n == 0 {
   903  		return 0
   904  	}
   905  	if n%2 == 0 {
   906  		// heap-allocated defers
   907  		for i := 0; i < 2; i++ {
   908  			defer func() {
   909  				r++
   910  			}()
   911  		}
   912  	} else {
   913  		// stack-allocated defers
   914  		defer func() {
   915  			r++
   916  		}()
   917  		defer func() {
   918  			r++
   919  		}()
   920  	}
   921  	r = deferHeapAndStack(n - 1)
   922  	escapeMe(new([1024]byte)) // force some GCs
   923  	return
   924  }
   925  
   926  // Pass a value to escapeMe to force it to escape.
   927  var escapeMe = func(x any) {}
   928  
   929  func TestFramePointerAdjust(t *testing.T) {
   930  	switch GOARCH {
   931  	case "amd64", "arm64":
   932  	default:
   933  		t.Skipf("frame pointer is not supported on %s", GOARCH)
   934  	}
   935  	output := runTestProg(t, "testprog", "FramePointerAdjust")
   936  	if output != "" {
   937  		t.Errorf("output:\n%s\n\nwant no output", output)
   938  	}
   939  }
   940  
   941  // TestSystemstackFramePointerAdjust is a regression test for issue 59692 that
   942  // ensures that the frame pointer of systemstack is correctly adjusted. See CL
   943  // 489015 for more details.
   944  func TestSystemstackFramePointerAdjust(t *testing.T) {
   945  	growAndShrinkStack(512, [1024]byte{})
   946  }
   947  
   948  // growAndShrinkStack grows the stack of the current goroutine in order to
   949  // shrink it again and verify that all frame pointers on the new stack have
   950  // been correctly adjusted. stackBallast is used to ensure we're not depending
   951  // on the current heuristics of stack shrinking too much.
   952  func growAndShrinkStack(n int, stackBallast [1024]byte) {
   953  	if n <= 0 {
   954  		return
   955  	}
   956  	growAndShrinkStack(n-1, stackBallast)
   957  	ShrinkStackAndVerifyFramePointers()
   958  }
   959  

View as plain text