Source file src/runtime/traceback_test.go

     1  // Copyright 2021 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  	"bytes"
     9  	"fmt"
    10  	"internal/abi"
    11  	"internal/asan"
    12  	"internal/msan"
    13  	"internal/race"
    14  	"internal/testenv"
    15  	"regexp"
    16  	"runtime"
    17  	"runtime/debug"
    18  	"strconv"
    19  	"strings"
    20  	"sync"
    21  	"testing"
    22  	_ "unsafe"
    23  )
    24  
    25  // Test traceback printing of inlined frames.
    26  func TestTracebackInlined(t *testing.T) {
    27  	testenv.SkipIfOptimizationOff(t) // This test requires inlining
    28  	check := func(t *testing.T, r *ttiResult, funcs ...string) {
    29  		t.Helper()
    30  
    31  		// Check the printed traceback.
    32  		frames := parseTraceback1(t, r.printed).frames
    33  		t.Log(r.printed)
    34  		// Find ttiLeaf
    35  		for len(frames) > 0 && frames[0].funcName != "runtime_test.ttiLeaf" {
    36  			frames = frames[1:]
    37  		}
    38  		if len(frames) == 0 {
    39  			t.Errorf("missing runtime_test.ttiLeaf")
    40  			return
    41  		}
    42  		frames = frames[1:]
    43  		// Check the function sequence.
    44  		for i, want := range funcs {
    45  			got := "<end>"
    46  			if i < len(frames) {
    47  				got = frames[i].funcName
    48  				if strings.HasSuffix(want, ")") {
    49  					got += "(" + frames[i].args + ")"
    50  				}
    51  			}
    52  			if got != want {
    53  				t.Errorf("got %s, want %s", got, want)
    54  				return
    55  			}
    56  		}
    57  	}
    58  
    59  	t.Run("simple", func(t *testing.T) {
    60  		// Check a simple case of inlining
    61  		r := ttiSimple1()
    62  		check(t, r, "runtime_test.ttiSimple3(...)", "runtime_test.ttiSimple2(...)", "runtime_test.ttiSimple1()")
    63  	})
    64  
    65  	t.Run("sigpanic", func(t *testing.T) {
    66  		// Check that sigpanic from an inlined function prints correctly
    67  		r := ttiSigpanic1()
    68  		check(t, r, "runtime_test.ttiSigpanic1.func1()", "panic", "runtime_test.ttiSigpanic3(...)", "runtime_test.ttiSigpanic2(...)", "runtime_test.ttiSigpanic1()")
    69  	})
    70  
    71  	t.Run("wrapper", func(t *testing.T) {
    72  		// Check that a method inlined into a wrapper prints correctly
    73  		r := ttiWrapper1()
    74  		check(t, r, "runtime_test.ttiWrapper.m1(...)", "runtime_test.ttiWrapper1()")
    75  	})
    76  
    77  	t.Run("excluded", func(t *testing.T) {
    78  		// Check that when F -> G is inlined and F is excluded from stack
    79  		// traces, G still appears.
    80  		r := ttiExcluded1()
    81  		check(t, r, "runtime_test.ttiExcluded3(...)", "runtime_test.ttiExcluded1()")
    82  	})
    83  }
    84  
    85  type ttiResult struct {
    86  	printed string
    87  }
    88  
    89  //go:noinline
    90  func ttiLeaf() *ttiResult {
    91  	// Get a printed stack trace.
    92  	printed := string(debug.Stack())
    93  	return &ttiResult{printed}
    94  }
    95  
    96  //go:noinline
    97  func ttiSimple1() *ttiResult {
    98  	return ttiSimple2()
    99  }
   100  func ttiSimple2() *ttiResult {
   101  	return ttiSimple3()
   102  }
   103  func ttiSimple3() *ttiResult {
   104  	return ttiLeaf()
   105  }
   106  
   107  //go:noinline
   108  func ttiSigpanic1() (res *ttiResult) {
   109  	defer func() {
   110  		res = ttiLeaf()
   111  		recover()
   112  	}()
   113  	ttiSigpanic2()
   114  	// without condition below the inliner might decide to de-prioritize
   115  	// the callsite above (since it would be on an "always leads to panic"
   116  	// path).
   117  	if alwaysTrue {
   118  		panic("did not panic")
   119  	}
   120  	return nil
   121  }
   122  func ttiSigpanic2() {
   123  	ttiSigpanic3()
   124  }
   125  func ttiSigpanic3() {
   126  	var p *int
   127  	*p = 3
   128  }
   129  
   130  var alwaysTrue = true
   131  
   132  //go:noinline
   133  func ttiWrapper1() *ttiResult {
   134  	var w ttiWrapper
   135  	m := (*ttiWrapper).m1
   136  	return m(&w)
   137  }
   138  
   139  type ttiWrapper struct{}
   140  
   141  func (w ttiWrapper) m1() *ttiResult {
   142  	return ttiLeaf()
   143  }
   144  
   145  //go:noinline
   146  func ttiExcluded1() *ttiResult {
   147  	return ttiExcluded2()
   148  }
   149  
   150  // ttiExcluded2 should be excluded from tracebacks. There are
   151  // various ways this could come up. Linking it to a "runtime." name is
   152  // rather synthetic, but it's easy and reliable. See issue #42754 for
   153  // one way this happened in real code.
   154  //
   155  //go:linkname ttiExcluded2 runtime.ttiExcluded2
   156  //go:noinline
   157  func ttiExcluded2() *ttiResult {
   158  	return ttiExcluded3()
   159  }
   160  func ttiExcluded3() *ttiResult {
   161  	return ttiLeaf()
   162  }
   163  
   164  var testTracebackArgsBuf [1000]byte
   165  
   166  func TestTracebackElision(t *testing.T) {
   167  	// Test printing exactly the maximum number of frames to make sure we don't
   168  	// print any "elided" message, eliding exactly 1 so we have to pick back up
   169  	// in the paused physical frame, and eliding 10 so we have to advance the
   170  	// physical frame forward.
   171  	for _, elided := range []int{0, 1, 10} {
   172  		t.Run(fmt.Sprintf("elided=%d", elided), func(t *testing.T) {
   173  			n := elided + runtime.TracebackInnerFrames + runtime.TracebackOuterFrames
   174  
   175  			// Start a new goroutine so we have control over the whole stack.
   176  			stackChan := make(chan string)
   177  			go tteStack(n, stackChan)
   178  			stack := <-stackChan
   179  			tb := parseTraceback1(t, stack)
   180  
   181  			// Check the traceback.
   182  			i := 0
   183  			for i < n {
   184  				if len(tb.frames) == 0 {
   185  					t.Errorf("traceback ended early")
   186  					break
   187  				}
   188  				fr := tb.frames[0]
   189  				if i == runtime.TracebackInnerFrames && elided > 0 {
   190  					// This should be an "elided" frame.
   191  					if fr.elided != elided {
   192  						t.Errorf("want %d frames elided", elided)
   193  						break
   194  					}
   195  					i += fr.elided
   196  				} else {
   197  					want := fmt.Sprintf("runtime_test.tte%d", (i+1)%5)
   198  					if i == 0 {
   199  						want = "runtime/debug.Stack"
   200  					} else if i == n-1 {
   201  						want = "runtime_test.tteStack"
   202  					}
   203  					if fr.funcName != want {
   204  						t.Errorf("want %s, got %s", want, fr.funcName)
   205  						break
   206  					}
   207  					i++
   208  				}
   209  				tb.frames = tb.frames[1:]
   210  			}
   211  			if !t.Failed() && len(tb.frames) > 0 {
   212  				t.Errorf("got %d more frames than expected", len(tb.frames))
   213  			}
   214  			if t.Failed() {
   215  				t.Logf("traceback diverged at frame %d", i)
   216  				off := len(stack)
   217  				if len(tb.frames) > 0 {
   218  					off = tb.frames[0].off
   219  				}
   220  				t.Logf("traceback before error:\n%s", stack[:off])
   221  				t.Logf("traceback after error:\n%s", stack[off:])
   222  			}
   223  		})
   224  	}
   225  }
   226  
   227  // tteStack creates a stack of n logical frames and sends the traceback to
   228  // stack. It cycles through 5 logical frames per physical frame to make it
   229  // unlikely that any part of the traceback will end on a physical boundary.
   230  func tteStack(n int, stack chan<- string) {
   231  	n-- // Account for this frame
   232  	// This is basically a Duff's device for starting the inline stack in the
   233  	// right place so we wind up at tteN when n%5=N.
   234  	switch n % 5 {
   235  	case 0:
   236  		stack <- tte0(n)
   237  	case 1:
   238  		stack <- tte1(n)
   239  	case 2:
   240  		stack <- tte2(n)
   241  	case 3:
   242  		stack <- tte3(n)
   243  	case 4:
   244  		stack <- tte4(n)
   245  	default:
   246  		panic("unreachable")
   247  	}
   248  }
   249  func tte0(n int) string {
   250  	return tte4(n - 1)
   251  }
   252  func tte1(n int) string {
   253  	return tte0(n - 1)
   254  }
   255  func tte2(n int) string {
   256  	// tte2 opens n%5 == 2 frames. It's also the base case of the recursion,
   257  	// since we can open no fewer than two frames to call debug.Stack().
   258  	if n < 2 {
   259  		panic("bad n")
   260  	}
   261  	if n == 2 {
   262  		return string(debug.Stack())
   263  	}
   264  	return tte1(n - 1)
   265  }
   266  func tte3(n int) string {
   267  	return tte2(n - 1)
   268  }
   269  func tte4(n int) string {
   270  	return tte3(n - 1)
   271  }
   272  
   273  func TestTracebackArgs(t *testing.T) {
   274  	if *flagQuick {
   275  		t.Skip("-quick")
   276  	}
   277  	optimized := !testenv.OptimizationOff()
   278  	abiSel := func(x, y string) string {
   279  		// select expected output based on ABI
   280  		// In noopt build we always spill arguments so the output is the same as stack ABI.
   281  		if optimized && abi.IntArgRegs > 0 {
   282  			return x
   283  		}
   284  		return y
   285  	}
   286  
   287  	tests := []struct {
   288  		fn     func() int
   289  		expect string
   290  	}{
   291  		// simple ints
   292  		{
   293  			func() int { return testTracebackArgs1(1, 2, 3, 4, 5) },
   294  			"testTracebackArgs1(0x1, 0x2, 0x3, 0x4, 0x5)",
   295  		},
   296  		// some aggregates
   297  		{
   298  			func() int {
   299  				return testTracebackArgs2(false, struct {
   300  					a, b, c int
   301  					x       [2]int
   302  				}{1, 2, 3, [2]int{4, 5}}, [0]int{}, [3]byte{6, 7, 8})
   303  			},
   304  			"testTracebackArgs2(0x0, {0x1, 0x2, 0x3, {0x4, 0x5}}, {}, {0x6, 0x7, 0x8})",
   305  		},
   306  		{
   307  			func() int { return testTracebackArgs3([3]byte{1, 2, 3}, 4, 5, 6, [3]byte{7, 8, 9}) },
   308  			"testTracebackArgs3({0x1, 0x2, 0x3}, 0x4, 0x5, 0x6, {0x7, 0x8, 0x9})",
   309  		},
   310  		// too deeply nested type
   311  		{
   312  			func() int { return testTracebackArgs4(false, [1][1][1][1][1][1][1][1][1][1]int{}) },
   313  			"testTracebackArgs4(0x0, {{{{{...}}}}})",
   314  		},
   315  		// a lot of zero-sized type
   316  		{
   317  			func() int {
   318  				z := [0]int{}
   319  				return testTracebackArgs5(false, struct {
   320  					x int
   321  					y [0]int
   322  					z [2][0]int
   323  				}{1, z, [2][0]int{}}, z, z, z, z, z, z, z, z, z, z, z, z)
   324  			},
   325  			"testTracebackArgs5(0x0, {0x1, {}, {{}, {}}}, {}, {}, {}, {}, {}, ...)",
   326  		},
   327  
   328  		// edge cases for ...
   329  		// no ... for 10 args
   330  		{
   331  			func() int { return testTracebackArgs6a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) },
   332  			"testTracebackArgs6a(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa)",
   333  		},
   334  		// has ... for 11 args
   335  		{
   336  			func() int { return testTracebackArgs6b(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) },
   337  			"testTracebackArgs6b(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)",
   338  		},
   339  		// no ... for aggregates with 10 words
   340  		{
   341  			func() int { return testTracebackArgs7a([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) },
   342  			"testTracebackArgs7a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa})",
   343  		},
   344  		// has ... for aggregates with 11 words
   345  		{
   346  			func() int { return testTracebackArgs7b([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) },
   347  			"testTracebackArgs7b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...})",
   348  		},
   349  		// no ... for aggregates, but with more args
   350  		{
   351  			func() int { return testTracebackArgs7c([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 11) },
   352  			"testTracebackArgs7c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, ...)",
   353  		},
   354  		// has ... for aggregates and also for more args
   355  		{
   356  			func() int { return testTracebackArgs7d([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 12) },
   357  			"testTracebackArgs7d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...}, ...)",
   358  		},
   359  		// nested aggregates, no ...
   360  		{
   361  			func() int { return testTracebackArgs8a(testArgsType8a{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}}) },
   362  			"testTracebackArgs8a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}})",
   363  		},
   364  		// nested aggregates, ... in inner but not outer
   365  		{
   366  			func() int { return testTracebackArgs8b(testArgsType8b{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}}) },
   367  			"testTracebackArgs8b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}})",
   368  		},
   369  		// nested aggregates, ... in outer but not inner
   370  		{
   371  			func() int { return testTracebackArgs8c(testArgsType8c{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}, 11}) },
   372  			"testTracebackArgs8c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}, ...})",
   373  		},
   374  		// nested aggregates, ... in both inner and outer
   375  		{
   376  			func() int { return testTracebackArgs8d(testArgsType8d{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}, 12}) },
   377  			"testTracebackArgs8d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}, ...})",
   378  		},
   379  
   380  		// Register argument liveness.
   381  		// 1, 3 are used and live, 2, 4 are dead (in register ABI).
   382  		// Address-taken (7) and stack ({5, 6}) args are always live.
   383  		{
   384  			func() int {
   385  				poisonStack() // poison arg area to make output deterministic
   386  				return testTracebackArgs9(1, 2, 3, 4, [2]int{5, 6}, 7)
   387  			},
   388  			abiSel(
   389  				"testTracebackArgs9(0x1, 0xffffffff?, 0x3, 0xff?, {0x5, 0x6}, 0x7)",
   390  				"testTracebackArgs9(0x1, 0x2, 0x3, 0x4, {0x5, 0x6}, 0x7)"),
   391  		},
   392  		// No live.
   393  		// (Note: this assume at least 5 int registers if register ABI is used.)
   394  		{
   395  			func() int {
   396  				poisonStack() // poison arg area to make output deterministic
   397  				return testTracebackArgs10(1, 2, 3, 4, 5)
   398  			},
   399  			abiSel(
   400  				"testTracebackArgs10(0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?)",
   401  				"testTracebackArgs10(0x1, 0x2, 0x3, 0x4, 0x5)"),
   402  		},
   403  		// Conditional spills.
   404  		// Spill in conditional, not executed.
   405  		{
   406  			func() int {
   407  				poisonStack() // poison arg area to make output deterministic
   408  				return testTracebackArgs11a(1, 2, 3)
   409  			},
   410  			abiSel(
   411  				"testTracebackArgs11a(0xffffffff?, 0xffffffff?, 0xffffffff?)",
   412  				"testTracebackArgs11a(0x1, 0x2, 0x3)"),
   413  		},
   414  		// 2 spills in conditional, not executed; 3 spills in conditional, executed, but not statically known.
   415  		// So print 0x3?.
   416  		{
   417  			func() int {
   418  				poisonStack() // poison arg area to make output deterministic
   419  				return testTracebackArgs11b(1, 2, 3, 4)
   420  			},
   421  			abiSel(
   422  				"testTracebackArgs11b(0xffffffff?, 0xffffffff?, 0x3?, 0x4)",
   423  				"testTracebackArgs11b(0x1, 0x2, 0x3, 0x4)"),
   424  		},
   425  		// Make sure spilled slice data pointers are spilled to the right location
   426  		// to ensure we see it listed without a ?.
   427  		// See issue 64414.
   428  		{
   429  			func() int {
   430  				poisonStack()
   431  				return testTracebackArgsSlice(testTracebackArgsSliceBackingStore[:])
   432  			},
   433  			// Note: capacity of the slice might be junk, as it is not used.
   434  			fmt.Sprintf("testTracebackArgsSlice({%p, 0x2, ", &testTracebackArgsSliceBackingStore[0]),
   435  		},
   436  	}
   437  	for _, test := range tests {
   438  		n := test.fn()
   439  		got := testTracebackArgsBuf[:n]
   440  		if !bytes.Contains(got, []byte(test.expect)) {
   441  			t.Errorf("traceback does not contain expected string: want %q, got\n%s", test.expect, got)
   442  		}
   443  	}
   444  }
   445  
   446  //go:noinline
   447  func testTracebackArgs1(a, b, c, d, e int) int {
   448  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   449  	if a < 0 {
   450  		// use in-reg args to keep them alive
   451  		return a + b + c + d + e
   452  	}
   453  	return n
   454  }
   455  
   456  //go:noinline
   457  func testTracebackArgs2(a bool, b struct {
   458  	a, b, c int
   459  	x       [2]int
   460  }, _ [0]int, d [3]byte) int {
   461  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   462  	if a {
   463  		// use in-reg args to keep them alive
   464  		return b.a + b.b + b.c + b.x[0] + b.x[1] + int(d[0]) + int(d[1]) + int(d[2])
   465  	}
   466  	return n
   467  }
   468  
   469  //go:noinline
   470  //go:registerparams
   471  func testTracebackArgs3(x [3]byte, a, b, c int, y [3]byte) int {
   472  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   473  	if a < 0 {
   474  		// use in-reg args to keep them alive
   475  		return int(x[0]) + int(x[1]) + int(x[2]) + a + b + c + int(y[0]) + int(y[1]) + int(y[2])
   476  	}
   477  	return n
   478  }
   479  
   480  //go:noinline
   481  func testTracebackArgs4(a bool, x [1][1][1][1][1][1][1][1][1][1]int) int {
   482  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   483  	if a {
   484  		panic(x) // use args to keep them alive
   485  	}
   486  	return n
   487  }
   488  
   489  //go:noinline
   490  func testTracebackArgs5(a bool, x struct {
   491  	x int
   492  	y [0]int
   493  	z [2][0]int
   494  }, _, _, _, _, _, _, _, _, _, _, _, _ [0]int) int {
   495  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   496  	if a {
   497  		panic(x) // use args to keep them alive
   498  	}
   499  	return n
   500  }
   501  
   502  //go:noinline
   503  func testTracebackArgs6a(a, b, c, d, e, f, g, h, i, j int) int {
   504  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   505  	if a < 0 {
   506  		// use in-reg args to keep them alive
   507  		return a + b + c + d + e + f + g + h + i + j
   508  	}
   509  	return n
   510  }
   511  
   512  //go:noinline
   513  func testTracebackArgs6b(a, b, c, d, e, f, g, h, i, j, k int) int {
   514  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   515  	if a < 0 {
   516  		// use in-reg args to keep them alive
   517  		return a + b + c + d + e + f + g + h + i + j + k
   518  	}
   519  	return n
   520  }
   521  
   522  //go:noinline
   523  func testTracebackArgs7a(a [10]int) int {
   524  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   525  	if a[0] < 0 {
   526  		// use in-reg args to keep them alive
   527  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9]
   528  	}
   529  	return n
   530  }
   531  
   532  //go:noinline
   533  func testTracebackArgs7b(a [11]int) int {
   534  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   535  	if a[0] < 0 {
   536  		// use in-reg args to keep them alive
   537  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10]
   538  	}
   539  	return n
   540  }
   541  
   542  //go:noinline
   543  func testTracebackArgs7c(a [10]int, b int) int {
   544  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   545  	if a[0] < 0 {
   546  		// use in-reg args to keep them alive
   547  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + b
   548  	}
   549  	return n
   550  }
   551  
   552  //go:noinline
   553  func testTracebackArgs7d(a [11]int, b int) int {
   554  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   555  	if a[0] < 0 {
   556  		// use in-reg args to keep them alive
   557  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10] + b
   558  	}
   559  	return n
   560  }
   561  
   562  type testArgsType8a struct {
   563  	a, b, c, d, e, f, g, h int
   564  	i                      [2]int
   565  }
   566  type testArgsType8b struct {
   567  	a, b, c, d, e, f, g, h int
   568  	i                      [3]int
   569  }
   570  type testArgsType8c struct {
   571  	a, b, c, d, e, f, g, h int
   572  	i                      [2]int
   573  	j                      int
   574  }
   575  type testArgsType8d struct {
   576  	a, b, c, d, e, f, g, h int
   577  	i                      [3]int
   578  	j                      int
   579  }
   580  
   581  //go:noinline
   582  func testTracebackArgs8a(a testArgsType8a) int {
   583  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   584  	if a.a < 0 {
   585  		// use in-reg args to keep them alive
   586  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1]
   587  	}
   588  	return n
   589  }
   590  
   591  //go:noinline
   592  func testTracebackArgs8b(a testArgsType8b) int {
   593  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   594  	if a.a < 0 {
   595  		// use in-reg args to keep them alive
   596  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2]
   597  	}
   598  	return n
   599  }
   600  
   601  //go:noinline
   602  func testTracebackArgs8c(a testArgsType8c) int {
   603  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   604  	if a.a < 0 {
   605  		// use in-reg args to keep them alive
   606  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.j
   607  	}
   608  	return n
   609  }
   610  
   611  //go:noinline
   612  func testTracebackArgs8d(a testArgsType8d) int {
   613  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   614  	if a.a < 0 {
   615  		// use in-reg args to keep them alive
   616  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2] + a.j
   617  	}
   618  	return n
   619  }
   620  
   621  // nosplit to avoid preemption or morestack spilling registers.
   622  //
   623  //go:nosplit
   624  //go:noinline
   625  func testTracebackArgs9(a int64, b int32, c int16, d int8, x [2]int, y int) int {
   626  	if a < 0 {
   627  		println(&y) // take address, make y live, even if no longer used at traceback
   628  	}
   629  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   630  	if a < 0 {
   631  		// use half of in-reg args to keep them alive, the other half are dead
   632  		return int(a) + int(c)
   633  	}
   634  	return n
   635  }
   636  
   637  // nosplit to avoid preemption or morestack spilling registers.
   638  //
   639  //go:nosplit
   640  //go:noinline
   641  func testTracebackArgs10(a, b, c, d, e int32) int {
   642  	// no use of any args
   643  	return runtime.Stack(testTracebackArgsBuf[:], false)
   644  }
   645  
   646  // norace to avoid race instrumentation changing spill locations.
   647  // nosplit to avoid preemption or morestack spilling registers.
   648  //
   649  //go:norace
   650  //go:nosplit
   651  //go:noinline
   652  func testTracebackArgs11a(a, b, c int32) int {
   653  	if a < 0 {
   654  		println(a, b, c) // spill in a conditional, may not execute
   655  	}
   656  	if b < 0 {
   657  		return int(a + b + c)
   658  	}
   659  	return runtime.Stack(testTracebackArgsBuf[:], false)
   660  }
   661  
   662  // norace to avoid race instrumentation changing spill locations.
   663  // nosplit to avoid preemption or morestack spilling registers.
   664  //
   665  //go:norace
   666  //go:nosplit
   667  //go:noinline
   668  func testTracebackArgs11b(a, b, c, d int32) int {
   669  	var x int32
   670  	if a < 0 {
   671  		print() // spill b in a conditional
   672  		x = b
   673  	} else {
   674  		print() // spill c in a conditional
   675  		x = c
   676  	}
   677  	if d < 0 { // d is always needed
   678  		return int(x + d)
   679  	}
   680  	return runtime.Stack(testTracebackArgsBuf[:], false)
   681  }
   682  
   683  // norace to avoid race instrumentation changing spill locations.
   684  // nosplit to avoid preemption or morestack spilling registers.
   685  //
   686  //go:norace
   687  //go:nosplit
   688  //go:noinline
   689  func testTracebackArgsSlice(a []int) int {
   690  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   691  	return a[1] + n
   692  }
   693  
   694  var testTracebackArgsSliceBackingStore [2]int
   695  
   696  // Poison the arg area with deterministic values.
   697  //
   698  //go:noinline
   699  func poisonStack() [20]int {
   700  	return [20]int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
   701  }
   702  
   703  func TestTracebackParentChildGoroutines(t *testing.T) {
   704  	parent := fmt.Sprintf("goroutine %d", runtime.Goid())
   705  	var wg sync.WaitGroup
   706  	wg.Add(1)
   707  	go func() {
   708  		defer wg.Done()
   709  		buf := make([]byte, 1<<10)
   710  		// We collect the stack only for this goroutine (by passing
   711  		// false to runtime.Stack). We expect to see the current
   712  		// goroutine ID, and the parent goroutine ID in a message like
   713  		// "created by ... in goroutine N".
   714  		stack := string(buf[:runtime.Stack(buf, false)])
   715  		child := fmt.Sprintf("goroutine %d", runtime.Goid())
   716  		if !strings.Contains(stack, parent) || !strings.Contains(stack, child) {
   717  			t.Errorf("did not see parent (%s) and child (%s) IDs in stack, got %s", parent, child, stack)
   718  		}
   719  	}()
   720  	wg.Wait()
   721  }
   722  
   723  type traceback struct {
   724  	frames    []*tbFrame
   725  	createdBy *tbFrame // no args
   726  }
   727  
   728  type tbFrame struct {
   729  	funcName string
   730  	args     string
   731  	inlined  bool
   732  
   733  	// elided is set to the number of frames elided, and the other fields are
   734  	// set to the zero value.
   735  	elided int
   736  
   737  	off int // byte offset in the traceback text of this frame
   738  }
   739  
   740  // parseTraceback parses a printed traceback to make it easier for tests to
   741  // check the result.
   742  func parseTraceback(t *testing.T, tb string) []*traceback {
   743  	//lines := strings.Split(tb, "\n")
   744  	//nLines := len(lines)
   745  	off := 0
   746  	lineNo := 0
   747  	fatal := func(f string, args ...any) {
   748  		msg := fmt.Sprintf(f, args...)
   749  		t.Fatalf("%s (line %d):\n%s", msg, lineNo, tb)
   750  	}
   751  	parseFrame := func(funcName, args string) *tbFrame {
   752  		// Consume file/line/etc
   753  		if !strings.HasPrefix(tb, "\t") {
   754  			fatal("missing source line")
   755  		}
   756  		_, tb, _ = strings.Cut(tb, "\n")
   757  		lineNo++
   758  		inlined := args == "..."
   759  		return &tbFrame{funcName: funcName, args: args, inlined: inlined, off: off}
   760  	}
   761  	var elidedRe = regexp.MustCompile(`^\.\.\.([0-9]+) frames elided\.\.\.$`)
   762  	var tbs []*traceback
   763  	var cur *traceback
   764  	tbLen := len(tb)
   765  	for len(tb) > 0 {
   766  		var line string
   767  		off = tbLen - len(tb)
   768  		line, tb, _ = strings.Cut(tb, "\n")
   769  		lineNo++
   770  		switch {
   771  		case strings.HasPrefix(line, "goroutine "):
   772  			cur = &traceback{}
   773  			tbs = append(tbs, cur)
   774  		case line == "":
   775  			// Separator between goroutines
   776  			cur = nil
   777  		case line[0] == '\t':
   778  			fatal("unexpected indent")
   779  		case strings.HasPrefix(line, "created by "):
   780  			funcName := line[len("created by "):]
   781  			cur.createdBy = parseFrame(funcName, "")
   782  		case strings.HasSuffix(line, ")"):
   783  			line = line[:len(line)-1] // Trim trailing ")"
   784  			funcName, args, found := strings.Cut(line, "(")
   785  			if !found {
   786  				fatal("missing (")
   787  			}
   788  			frame := parseFrame(funcName, args)
   789  			cur.frames = append(cur.frames, frame)
   790  		case elidedRe.MatchString(line):
   791  			// "...N frames elided..."
   792  			nStr := elidedRe.FindStringSubmatch(line)
   793  			n, _ := strconv.Atoi(nStr[1])
   794  			frame := &tbFrame{elided: n}
   795  			cur.frames = append(cur.frames, frame)
   796  		}
   797  	}
   798  	return tbs
   799  }
   800  
   801  // parseTraceback1 is like parseTraceback, but expects tb to contain exactly one
   802  // goroutine.
   803  func parseTraceback1(t *testing.T, tb string) *traceback {
   804  	tbs := parseTraceback(t, tb)
   805  	if len(tbs) != 1 {
   806  		t.Fatalf("want 1 goroutine, got %d:\n%s", len(tbs), tb)
   807  	}
   808  	return tbs[0]
   809  }
   810  
   811  //go:noinline
   812  func testTracebackGenericFn[T any](buf []byte) int {
   813  	return runtime.Stack(buf[:], false)
   814  }
   815  
   816  func testTracebackGenericFnInlined[T any](buf []byte) int {
   817  	return runtime.Stack(buf[:], false)
   818  }
   819  
   820  type testTracebackGenericTyp[P any] struct{ x P }
   821  
   822  //go:noinline
   823  func (t testTracebackGenericTyp[P]) M(buf []byte) int {
   824  	return runtime.Stack(buf[:], false)
   825  }
   826  
   827  func (t testTracebackGenericTyp[P]) Inlined(buf []byte) int {
   828  	return runtime.Stack(buf[:], false)
   829  }
   830  
   831  func TestTracebackGeneric(t *testing.T) {
   832  	if *flagQuick {
   833  		t.Skip("-quick")
   834  	}
   835  	var x testTracebackGenericTyp[int]
   836  	tests := []struct {
   837  		fn     func([]byte) int
   838  		expect string
   839  	}{
   840  		// function, not inlined
   841  		{
   842  			testTracebackGenericFn[int],
   843  			"testTracebackGenericFn[...](",
   844  		},
   845  		// function, inlined
   846  		{
   847  			func(buf []byte) int { return testTracebackGenericFnInlined[int](buf) },
   848  			"testTracebackGenericFnInlined[...](",
   849  		},
   850  		// method, not inlined
   851  		{
   852  			x.M,
   853  			"testTracebackGenericTyp[...].M(",
   854  		},
   855  		// method, inlined
   856  		{
   857  			func(buf []byte) int { return x.Inlined(buf) },
   858  			"testTracebackGenericTyp[...].Inlined(",
   859  		},
   860  	}
   861  	var buf [1000]byte
   862  	for _, test := range tests {
   863  		n := test.fn(buf[:])
   864  		got := buf[:n]
   865  		if !bytes.Contains(got, []byte(test.expect)) {
   866  			t.Errorf("traceback does not contain expected string: want %q, got\n%s", test.expect, got)
   867  		}
   868  		if bytes.Contains(got, []byte("shape")) { // should not contain shape name
   869  			t.Errorf("traceback contains shape name: got\n%s", got)
   870  		}
   871  	}
   872  }
   873  
   874  func TestSetCgoTracebackNoCgo(t *testing.T) {
   875  	if asan.Enabled || msan.Enabled || race.Enabled {
   876  		t.Skip("skipped test: sanitizer builds use cgo")
   877  	}
   878  
   879  	output := runTestProg(t, "testprog", "SetCgoTracebackNoCgo")
   880  	want := "OK\n"
   881  	if output != want {
   882  		t.Fatalf("want %s, got %s\n", want, output)
   883  	}
   884  }
   885  

View as plain text