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

View as plain text