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 }