github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/runtime/traceback_test.go (about)

     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  	"internal/abi"
    10  	"internal/testenv"
    11  	"runtime"
    12  	"testing"
    13  )
    14  
    15  var testTracebackArgsBuf [1000]byte
    16  
    17  func TestTracebackArgs(t *testing.T) {
    18  	if *flagQuick {
    19  		t.Skip("-quick")
    20  	}
    21  	optimized := !testenv.OptimizationOff()
    22  	abiSel := func(x, y string) string {
    23  		// select expected output based on ABI
    24  		// In noopt build we always spill arguments so the output is the same as stack ABI.
    25  		if optimized && abi.IntArgRegs > 0 {
    26  			return x
    27  		}
    28  		return y
    29  	}
    30  
    31  	tests := []struct {
    32  		fn     func() int
    33  		expect string
    34  	}{
    35  		// simple ints
    36  		{
    37  			func() int { return testTracebackArgs1(1, 2, 3, 4, 5) },
    38  			"testTracebackArgs1(0x1, 0x2, 0x3, 0x4, 0x5)",
    39  		},
    40  		// some aggregates
    41  		{
    42  			func() int {
    43  				return testTracebackArgs2(false, struct {
    44  					a, b, c int
    45  					x       [2]int
    46  				}{1, 2, 3, [2]int{4, 5}}, [0]int{}, [3]byte{6, 7, 8})
    47  			},
    48  			"testTracebackArgs2(0x0, {0x1, 0x2, 0x3, {0x4, 0x5}}, {}, {0x6, 0x7, 0x8})",
    49  		},
    50  		{
    51  			func() int { return testTracebackArgs3([3]byte{1, 2, 3}, 4, 5, 6, [3]byte{7, 8, 9}) },
    52  			"testTracebackArgs3({0x1, 0x2, 0x3}, 0x4, 0x5, 0x6, {0x7, 0x8, 0x9})",
    53  		},
    54  		// too deeply nested type
    55  		{
    56  			func() int { return testTracebackArgs4(false, [1][1][1][1][1][1][1][1][1][1]int{}) },
    57  			"testTracebackArgs4(0x0, {{{{{...}}}}})",
    58  		},
    59  		// a lot of zero-sized type
    60  		{
    61  			func() int {
    62  				z := [0]int{}
    63  				return testTracebackArgs5(false, struct {
    64  					x int
    65  					y [0]int
    66  					z [2][0]int
    67  				}{1, z, [2][0]int{}}, z, z, z, z, z, z, z, z, z, z, z, z)
    68  			},
    69  			"testTracebackArgs5(0x0, {0x1, {}, {{}, {}}}, {}, {}, {}, {}, {}, ...)",
    70  		},
    71  
    72  		// edge cases for ...
    73  		// no ... for 10 args
    74  		{
    75  			func() int { return testTracebackArgs6a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) },
    76  			"testTracebackArgs6a(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa)",
    77  		},
    78  		// has ... for 11 args
    79  		{
    80  			func() int { return testTracebackArgs6b(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) },
    81  			"testTracebackArgs6b(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)",
    82  		},
    83  		// no ... for aggregates with 10 words
    84  		{
    85  			func() int { return testTracebackArgs7a([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) },
    86  			"testTracebackArgs7a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa})",
    87  		},
    88  		// has ... for aggregates with 11 words
    89  		{
    90  			func() int { return testTracebackArgs7b([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) },
    91  			"testTracebackArgs7b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...})",
    92  		},
    93  		// no ... for aggregates, but with more args
    94  		{
    95  			func() int { return testTracebackArgs7c([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 11) },
    96  			"testTracebackArgs7c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, ...)",
    97  		},
    98  		// has ... for aggregates and also for more args
    99  		{
   100  			func() int { return testTracebackArgs7d([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 12) },
   101  			"testTracebackArgs7d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...}, ...)",
   102  		},
   103  		// nested aggregates, no ...
   104  		{
   105  			func() int { return testTracebackArgs8a(testArgsType8a{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}}) },
   106  			"testTracebackArgs8a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}})",
   107  		},
   108  		// nested aggregates, ... in inner but not outer
   109  		{
   110  			func() int { return testTracebackArgs8b(testArgsType8b{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}}) },
   111  			"testTracebackArgs8b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}})",
   112  		},
   113  		// nested aggregates, ... in outer but not inner
   114  		{
   115  			func() int { return testTracebackArgs8c(testArgsType8c{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}, 11}) },
   116  			"testTracebackArgs8c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}, ...})",
   117  		},
   118  		// nested aggregates, ... in both inner and outer
   119  		{
   120  			func() int { return testTracebackArgs8d(testArgsType8d{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}, 12}) },
   121  			"testTracebackArgs8d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}, ...})",
   122  		},
   123  
   124  		// Register argument liveness.
   125  		// 1, 3 are used and live, 2, 4 are dead (in register ABI).
   126  		// Address-taken (7) and stack ({5, 6}) args are always live.
   127  		{
   128  			func() int {
   129  				poisonStack() // poison arg area to make output deterministic
   130  				return testTracebackArgs9(1, 2, 3, 4, [2]int{5, 6}, 7)
   131  			},
   132  			abiSel(
   133  				"testTracebackArgs9(0x1, 0xffffffff?, 0x3, 0xff?, {0x5, 0x6}, 0x7)",
   134  				"testTracebackArgs9(0x1, 0x2, 0x3, 0x4, {0x5, 0x6}, 0x7)"),
   135  		},
   136  		// No live.
   137  		// (Note: this assume at least 5 int registers if register ABI is used.)
   138  		{
   139  			func() int {
   140  				poisonStack() // poison arg area to make output deterministic
   141  				return testTracebackArgs10(1, 2, 3, 4, 5)
   142  			},
   143  			abiSel(
   144  				"testTracebackArgs10(0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?)",
   145  				"testTracebackArgs10(0x1, 0x2, 0x3, 0x4, 0x5)"),
   146  		},
   147  		// Conditional spills.
   148  		// Spill in conditional, not executed.
   149  		{
   150  			func() int {
   151  				poisonStack() // poison arg area to make output deterministic
   152  				return testTracebackArgs11a(1, 2, 3)
   153  			},
   154  			abiSel(
   155  				"testTracebackArgs11a(0xffffffff?, 0xffffffff?, 0xffffffff?)",
   156  				"testTracebackArgs11a(0x1, 0x2, 0x3)"),
   157  		},
   158  		// 2 spills in conditional, not executed; 3 spills in conditional, executed, but not statically known.
   159  		// So print 0x3?.
   160  		{
   161  			func() int {
   162  				poisonStack() // poison arg area to make output deterministic
   163  				return testTracebackArgs11b(1, 2, 3, 4)
   164  			},
   165  			abiSel(
   166  				"testTracebackArgs11b(0xffffffff?, 0xffffffff?, 0x3?, 0x4)",
   167  				"testTracebackArgs11b(0x1, 0x2, 0x3, 0x4)"),
   168  		},
   169  	}
   170  	for _, test := range tests {
   171  		n := test.fn()
   172  		got := testTracebackArgsBuf[:n]
   173  		if !bytes.Contains(got, []byte(test.expect)) {
   174  			t.Errorf("traceback does not contain expected string: want %q, got\n%s", test.expect, got)
   175  		}
   176  	}
   177  }
   178  
   179  //go:noinline
   180  func testTracebackArgs1(a, b, c, d, e int) int {
   181  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   182  	if a < 0 {
   183  		// use in-reg args to keep them alive
   184  		return a + b + c + d + e
   185  	}
   186  	return n
   187  }
   188  
   189  //go:noinline
   190  func testTracebackArgs2(a bool, b struct {
   191  	a, b, c int
   192  	x       [2]int
   193  }, _ [0]int, d [3]byte) int {
   194  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   195  	if a {
   196  		// use in-reg args to keep them alive
   197  		return b.a + b.b + b.c + b.x[0] + b.x[1] + int(d[0]) + int(d[1]) + int(d[2])
   198  	}
   199  	return n
   200  
   201  }
   202  
   203  //go:noinline
   204  //go:registerparams
   205  func testTracebackArgs3(x [3]byte, a, b, c int, y [3]byte) int {
   206  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   207  	if a < 0 {
   208  		// use in-reg args to keep them alive
   209  		return int(x[0]) + int(x[1]) + int(x[2]) + a + b + c + int(y[0]) + int(y[1]) + int(y[2])
   210  	}
   211  	return n
   212  }
   213  
   214  //go:noinline
   215  func testTracebackArgs4(a bool, x [1][1][1][1][1][1][1][1][1][1]int) int {
   216  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   217  	if a {
   218  		panic(x) // use args to keep them alive
   219  	}
   220  	return n
   221  }
   222  
   223  //go:noinline
   224  func testTracebackArgs5(a bool, x struct {
   225  	x int
   226  	y [0]int
   227  	z [2][0]int
   228  }, _, _, _, _, _, _, _, _, _, _, _, _ [0]int) int {
   229  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   230  	if a {
   231  		panic(x) // use args to keep them alive
   232  	}
   233  	return n
   234  }
   235  
   236  //go:noinline
   237  func testTracebackArgs6a(a, b, c, d, e, f, g, h, i, j int) int {
   238  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   239  	if a < 0 {
   240  		// use in-reg args to keep them alive
   241  		return a + b + c + d + e + f + g + h + i + j
   242  	}
   243  	return n
   244  }
   245  
   246  //go:noinline
   247  func testTracebackArgs6b(a, b, c, d, e, f, g, h, i, j, k int) int {
   248  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   249  	if a < 0 {
   250  		// use in-reg args to keep them alive
   251  		return a + b + c + d + e + f + g + h + i + j + k
   252  	}
   253  	return n
   254  }
   255  
   256  //go:noinline
   257  func testTracebackArgs7a(a [10]int) int {
   258  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   259  	if a[0] < 0 {
   260  		// use in-reg args to keep them alive
   261  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9]
   262  	}
   263  	return n
   264  }
   265  
   266  //go:noinline
   267  func testTracebackArgs7b(a [11]int) int {
   268  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   269  	if a[0] < 0 {
   270  		// use in-reg args to keep them alive
   271  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10]
   272  	}
   273  	return n
   274  }
   275  
   276  //go:noinline
   277  func testTracebackArgs7c(a [10]int, b int) int {
   278  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   279  	if a[0] < 0 {
   280  		// use in-reg args to keep them alive
   281  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + b
   282  	}
   283  	return n
   284  }
   285  
   286  //go:noinline
   287  func testTracebackArgs7d(a [11]int, b int) int {
   288  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   289  	if a[0] < 0 {
   290  		// use in-reg args to keep them alive
   291  		return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10] + b
   292  	}
   293  	return n
   294  }
   295  
   296  type testArgsType8a struct {
   297  	a, b, c, d, e, f, g, h int
   298  	i                      [2]int
   299  }
   300  type testArgsType8b struct {
   301  	a, b, c, d, e, f, g, h int
   302  	i                      [3]int
   303  }
   304  type testArgsType8c struct {
   305  	a, b, c, d, e, f, g, h int
   306  	i                      [2]int
   307  	j                      int
   308  }
   309  type testArgsType8d struct {
   310  	a, b, c, d, e, f, g, h int
   311  	i                      [3]int
   312  	j                      int
   313  }
   314  
   315  //go:noinline
   316  func testTracebackArgs8a(a testArgsType8a) int {
   317  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   318  	if a.a < 0 {
   319  		// use in-reg args to keep them alive
   320  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1]
   321  	}
   322  	return n
   323  }
   324  
   325  //go:noinline
   326  func testTracebackArgs8b(a testArgsType8b) int {
   327  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   328  	if a.a < 0 {
   329  		// use in-reg args to keep them alive
   330  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2]
   331  	}
   332  	return n
   333  }
   334  
   335  //go:noinline
   336  func testTracebackArgs8c(a testArgsType8c) int {
   337  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   338  	if a.a < 0 {
   339  		// use in-reg args to keep them alive
   340  		return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.j
   341  	}
   342  	return n
   343  }
   344  
   345  //go:noinline
   346  func testTracebackArgs8d(a testArgsType8d) int {
   347  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   348  	if a.a < 0 {
   349  		// use in-reg args to keep them alive
   350  		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
   351  	}
   352  	return n
   353  }
   354  
   355  // nosplit to avoid preemption or morestack spilling registers.
   356  //
   357  //go:nosplit
   358  //go:noinline
   359  func testTracebackArgs9(a int64, b int32, c int16, d int8, x [2]int, y int) int {
   360  	if a < 0 {
   361  		println(&y) // take address, make y live, even if no longer used at traceback
   362  	}
   363  	n := runtime.Stack(testTracebackArgsBuf[:], false)
   364  	if a < 0 {
   365  		// use half of in-reg args to keep them alive, the other half are dead
   366  		return int(a) + int(c)
   367  	}
   368  	return n
   369  }
   370  
   371  // nosplit to avoid preemption or morestack spilling registers.
   372  //
   373  //go:nosplit
   374  //go:noinline
   375  func testTracebackArgs10(a, b, c, d, e int32) int {
   376  	// no use of any args
   377  	return runtime.Stack(testTracebackArgsBuf[:], false)
   378  }
   379  
   380  // norace to avoid race instrumentation changing spill locations.
   381  // nosplit to avoid preemption or morestack spilling registers.
   382  //
   383  //go:norace
   384  //go:nosplit
   385  //go:noinline
   386  func testTracebackArgs11a(a, b, c int32) int {
   387  	if a < 0 {
   388  		println(a, b, c) // spill in a conditional, may not execute
   389  	}
   390  	if b < 0 {
   391  		return int(a + b + c)
   392  	}
   393  	return runtime.Stack(testTracebackArgsBuf[:], false)
   394  }
   395  
   396  // norace to avoid race instrumentation changing spill locations.
   397  // nosplit to avoid preemption or morestack spilling registers.
   398  //
   399  //go:norace
   400  //go:nosplit
   401  //go:noinline
   402  func testTracebackArgs11b(a, b, c, d int32) int {
   403  	var x int32
   404  	if a < 0 {
   405  		print() // spill b in a conditional
   406  		x = b
   407  	} else {
   408  		print() // spill c in a conditional
   409  		x = c
   410  	}
   411  	if d < 0 { // d is always needed
   412  		return int(x + d)
   413  	}
   414  	return runtime.Stack(testTracebackArgsBuf[:], false)
   415  }
   416  
   417  // Poison the arg area with deterministic values.
   418  //
   419  //go:noinline
   420  func poisonStack() [20]int {
   421  	return [20]int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
   422  }