github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/defer_test.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "fmt" 9 "reflect" 10 "runtime" 11 "testing" 12 ) 13 14 // Make sure open-coded defer exit code is not lost, even when there is an 15 // unconditional panic (hence no return from the function) 16 func TestUnconditionalPanic(t *testing.T) { 17 defer func() { 18 if recover() != "testUnconditional" { 19 t.Fatal("expected unconditional panic") 20 } 21 }() 22 panic("testUnconditional") 23 } 24 25 var glob int = 3 26 27 // Test an open-coded defer and non-open-coded defer - make sure both defers run 28 // and call recover() 29 func TestOpenAndNonOpenDefers(t *testing.T) { 30 for { 31 // Non-open defer because in a loop 32 defer func(n int) { 33 if recover() != "testNonOpenDefer" { 34 t.Fatal("expected testNonOpen panic") 35 } 36 }(3) 37 if glob > 2 { 38 break 39 } 40 } 41 testOpen(t, 47) 42 panic("testNonOpenDefer") 43 } 44 45 //go:noinline 46 func testOpen(t *testing.T, arg int) { 47 defer func(n int) { 48 if recover() != "testOpenDefer" { 49 t.Fatal("expected testOpen panic") 50 } 51 }(4) 52 if arg > 2 { 53 panic("testOpenDefer") 54 } 55 } 56 57 // Test a non-open-coded defer and an open-coded defer - make sure both defers run 58 // and call recover() 59 func TestNonOpenAndOpenDefers(t *testing.T) { 60 testOpen(t, 47) 61 for { 62 // Non-open defer because in a loop 63 defer func(n int) { 64 if recover() != "testNonOpenDefer" { 65 t.Fatal("expected testNonOpen panic") 66 } 67 }(3) 68 if glob > 2 { 69 break 70 } 71 } 72 panic("testNonOpenDefer") 73 } 74 75 var list []int 76 77 // Make sure that conditional open-coded defers are activated correctly and run in 78 // the correct order. 79 func TestConditionalDefers(t *testing.T) { 80 list = make([]int, 0, 10) 81 82 defer func() { 83 if recover() != "testConditional" { 84 t.Fatal("expected panic") 85 } 86 want := []int{4, 2, 1} 87 if !reflect.DeepEqual(want, list) { 88 t.Fatal(fmt.Sprintf("wanted %v, got %v", want, list)) 89 } 90 91 }() 92 testConditionalDefers(8) 93 } 94 95 func testConditionalDefers(n int) { 96 doappend := func(i int) { 97 list = append(list, i) 98 } 99 100 defer doappend(1) 101 if n > 5 { 102 defer doappend(2) 103 if n > 8 { 104 defer doappend(3) 105 } else { 106 defer doappend(4) 107 } 108 } 109 panic("testConditional") 110 } 111 112 // Test that there is no compile-time or run-time error if an open-coded defer 113 // call is removed by constant propagation and dead-code elimination. 114 func TestDisappearingDefer(t *testing.T) { 115 switch runtime.GOOS { 116 case "invalidOS": 117 defer func() { 118 t.Fatal("Defer shouldn't run") 119 }() 120 } 121 } 122 123 // This tests an extra recursive panic behavior that is only specified in the 124 // code. Suppose a first panic P1 happens and starts processing defer calls. If a 125 // second panic P2 happens while processing defer call D in frame F, then defer 126 // call processing is restarted (with some potentially new defer calls created by 127 // D or its callees). If the defer processing reaches the started defer call D 128 // again in the defer stack, then the original panic P1 is aborted and cannot 129 // continue panic processing or be recovered. If the panic P2 does a recover at 130 // some point, it will naturally remove the original panic P1 from the stack 131 // (since the original panic had to be in frame F or a descendant of F). 132 func TestAbortedPanic(t *testing.T) { 133 defer func() { 134 r := recover() 135 if r != nil { 136 t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r)) 137 } 138 }() 139 defer func() { 140 r := recover() 141 if r != "panic2" { 142 t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic2", r)) 143 } 144 }() 145 defer func() { 146 panic("panic2") 147 }() 148 panic("panic1") 149 } 150 151 // This tests that recover() does not succeed unless it is called directly from a 152 // defer function that is directly called by the panic. Here, we first call it 153 // from a defer function that is created by the defer function called directly by 154 // the panic. In 155 func TestRecoverMatching(t *testing.T) { 156 defer func() { 157 r := recover() 158 if r != "panic1" { 159 t.Fatal(fmt.Sprintf("wanted %v, got %v", "panic1", r)) 160 } 161 }() 162 defer func() { 163 defer func() { 164 // Shouldn't succeed, even though it is called directly 165 // from a defer function, since this defer function was 166 // not directly called by the panic. 167 r := recover() 168 if r != nil { 169 t.Fatal(fmt.Sprintf("wanted nil recover, got %v", r)) 170 } 171 }() 172 }() 173 panic("panic1") 174 } 175 176 type nonSSAable [128]byte 177 178 type bigStruct struct { 179 x, y, z, w, p, q int64 180 } 181 182 type containsBigStruct struct { 183 element bigStruct 184 } 185 186 func mknonSSAable() nonSSAable { 187 globint1++ 188 return nonSSAable{0, 0, 0, 0, 5} 189 } 190 191 var globint1, globint2, globint3 int 192 193 //go:noinline 194 func sideeffect(n int64) int64 { 195 globint2++ 196 return n 197 } 198 199 func sideeffect2(in containsBigStruct) containsBigStruct { 200 globint3++ 201 return in 202 } 203 204 // Test that nonSSAable arguments to defer are handled correctly and only evaluated once. 205 func TestNonSSAableArgs(t *testing.T) { 206 globint1 = 0 207 globint2 = 0 208 globint3 = 0 209 var save1 byte 210 var save2 int64 211 var save3 int64 212 var save4 int64 213 214 defer func() { 215 if globint1 != 1 { 216 t.Fatal(fmt.Sprintf("globint1: wanted: 1, got %v", globint1)) 217 } 218 if save1 != 5 { 219 t.Fatal(fmt.Sprintf("save1: wanted: 5, got %v", save1)) 220 } 221 if globint2 != 1 { 222 t.Fatal(fmt.Sprintf("globint2: wanted: 1, got %v", globint2)) 223 } 224 if save2 != 2 { 225 t.Fatal(fmt.Sprintf("save2: wanted: 2, got %v", save2)) 226 } 227 if save3 != 4 { 228 t.Fatal(fmt.Sprintf("save3: wanted: 4, got %v", save3)) 229 } 230 if globint3 != 1 { 231 t.Fatal(fmt.Sprintf("globint3: wanted: 1, got %v", globint3)) 232 } 233 if save4 != 4 { 234 t.Fatal(fmt.Sprintf("save1: wanted: 4, got %v", save4)) 235 } 236 }() 237 238 // Test function returning a non-SSAable arg 239 defer func(n nonSSAable) { 240 save1 = n[4] 241 }(mknonSSAable()) 242 // Test composite literal that is not SSAable 243 defer func(b bigStruct) { 244 save2 = b.y 245 }(bigStruct{1, 2, 3, 4, 5, sideeffect(6)}) 246 247 // Test struct field reference that is non-SSAable 248 foo := containsBigStruct{} 249 foo.element.z = 4 250 defer func(element bigStruct) { 251 save3 = element.z 252 }(foo.element) 253 defer func(element bigStruct) { 254 save4 = element.z 255 }(sideeffect2(foo).element) 256 } 257 258 //go:noinline 259 func doPanic() { 260 panic("Test panic") 261 } 262 263 func TestDeferForFuncWithNoExit(t *testing.T) { 264 cond := 1 265 defer func() { 266 if cond != 2 { 267 t.Fatal(fmt.Sprintf("cond: wanted 2, got %v", cond)) 268 } 269 if recover() != "Test panic" { 270 t.Fatal("Didn't find expected panic") 271 } 272 }() 273 x := 0 274 // Force a stack copy, to make sure that the &cond pointer passed to defer 275 // function is properly updated. 276 growStackIter(&x, 1000) 277 cond = 2 278 doPanic() 279 280 // This function has no exit/return, since it ends with an infinite loop 281 for { 282 } 283 } 284 285 // Test case approximating issue #37664, where a recursive function (interpreter) 286 // may do repeated recovers/re-panics until it reaches the frame where the panic 287 // can actually be handled. The recurseFnPanicRec() function is testing that there 288 // are no stale defer structs on the defer chain after the interpreter() sequence, 289 // by writing a bunch of 0xffffffffs into several recursive stack frames, and then 290 // doing a single panic-recover which would invoke any such stale defer structs. 291 func TestDeferWithRepeatedRepanics(t *testing.T) { 292 interpreter(0, 6, 2) 293 recurseFnPanicRec(0, 10) 294 interpreter(0, 5, 1) 295 recurseFnPanicRec(0, 10) 296 interpreter(0, 6, 3) 297 recurseFnPanicRec(0, 10) 298 } 299 300 func interpreter(level int, maxlevel int, rec int) { 301 defer func() { 302 e := recover() 303 if e == nil { 304 return 305 } 306 if level != e.(int) { 307 //fmt.Fprintln(os.Stderr, "re-panicing, level", level) 308 panic(e) 309 } 310 //fmt.Fprintln(os.Stderr, "Recovered, level", level) 311 }() 312 if level+1 < maxlevel { 313 interpreter(level+1, maxlevel, rec) 314 } else { 315 //fmt.Fprintln(os.Stderr, "Initiating panic") 316 panic(rec) 317 } 318 } 319 320 func recurseFnPanicRec(level int, maxlevel int) { 321 defer func() { 322 recover() 323 }() 324 recurseFn(level, maxlevel) 325 } 326 327 var saveInt uint32 328 329 func recurseFn(level int, maxlevel int) { 330 a := [40]uint32{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff} 331 if level+1 < maxlevel { 332 // Make sure a array is referenced, so it is not optimized away 333 saveInt = a[4] 334 recurseFn(level+1, maxlevel) 335 } else { 336 panic("recurseFn panic") 337 } 338 } 339 340 // Try to reproduce issue #37688, where a pointer to an open-coded defer struct is 341 // mistakenly held, and that struct keeps a pointer to a stack-allocated defer 342 // struct, and that stack-allocated struct gets overwritten or the stack gets 343 // moved, so a memory error happens on GC. 344 func TestIssue37688(t *testing.T) { 345 for j := 0; j < 10; j++ { 346 g2() 347 g3() 348 } 349 } 350 351 type foo struct { 352 } 353 354 //go:noinline 355 func (f *foo) method1() { 356 } 357 358 //go:noinline 359 func (f *foo) method2() { 360 } 361 362 func g2() { 363 var a foo 364 ap := &a 365 // The loop forces this defer to be heap-allocated and the remaining two 366 // to be stack-allocated. 367 for i := 0; i < 1; i++ { 368 defer ap.method1() 369 } 370 defer ap.method2() 371 defer ap.method1() 372 ff1(ap, 1, 2, 3, 4, 5, 6, 7, 8, 9) 373 // Try to get the stack to be moved by growing it too large, so 374 // existing stack-allocated defer becomes invalid. 375 rec1(2000) 376 } 377 378 func g3() { 379 // Mix up the stack layout by adding in an extra function frame 380 g2() 381 } 382 383 var globstruct struct { 384 a, b, c, d, e, f, g, h, i int 385 } 386 387 func ff1(ap *foo, a, b, c, d, e, f, g, h, i int) { 388 defer ap.method1() 389 390 // Make a defer that has a very large set of args, hence big size for the 391 // defer record for the open-coded frame (which means it won't use the 392 // defer pool) 393 defer func(ap *foo, a, b, c, d, e, f, g, h, i int) { 394 if v := recover(); v != nil { 395 } 396 globstruct.a = a 397 globstruct.b = b 398 globstruct.c = c 399 globstruct.d = d 400 globstruct.e = e 401 globstruct.f = f 402 globstruct.g = g 403 globstruct.h = h 404 }(ap, a, b, c, d, e, f, g, h, i) 405 panic("ff1 panic") 406 } 407 408 func rec1(max int) { 409 if max > 0 { 410 rec1(max - 1) 411 } 412 } 413 414 func TestIssue43921(t *testing.T) { 415 defer func() { 416 expect(t, 1, recover()) 417 }() 418 func() { 419 // Prevent open-coded defers 420 for { 421 defer func() {}() 422 break 423 } 424 425 defer func() { 426 defer func() { 427 expect(t, 4, recover()) 428 }() 429 panic(4) 430 }() 431 panic(1) 432 433 }() 434 } 435 436 func expect(t *testing.T, n int, err any) { 437 if n != err { 438 t.Fatalf("have %v, want %v", err, n) 439 } 440 } 441 442 func TestIssue43920(t *testing.T) { 443 var steps int 444 445 defer func() { 446 expect(t, 1, recover()) 447 }() 448 defer func() { 449 defer func() { 450 defer func() { 451 expect(t, 5, recover()) 452 }() 453 defer panic(5) 454 func() { 455 panic(4) 456 }() 457 }() 458 defer func() { 459 expect(t, 3, recover()) 460 }() 461 defer panic(3) 462 }() 463 func() { 464 defer step(t, &steps, 1) 465 panic(1) 466 }() 467 } 468 469 func step(t *testing.T, steps *int, want int) { 470 *steps++ 471 if *steps != want { 472 t.Fatalf("have %v, want %v", *steps, want) 473 } 474 } 475 476 func TestIssue43941(t *testing.T) { 477 var steps int = 7 478 defer func() { 479 step(t, &steps, 14) 480 expect(t, 4, recover()) 481 }() 482 func() { 483 func() { 484 defer func() { 485 defer func() { 486 expect(t, 3, recover()) 487 }() 488 defer panic(3) 489 panic(2) 490 }() 491 defer func() { 492 expect(t, 1, recover()) 493 }() 494 defer panic(1) 495 }() 496 defer func() {}() 497 defer func() {}() 498 defer step(t, &steps, 10) 499 defer step(t, &steps, 9) 500 step(t, &steps, 8) 501 }() 502 func() { 503 defer step(t, &steps, 13) 504 defer step(t, &steps, 12) 505 func() { 506 defer step(t, &steps, 11) 507 panic(4) 508 }() 509 510 // Code below isn't executed, 511 // but removing it breaks the test case. 512 defer func() {}() 513 defer panic(-1) 514 defer step(t, &steps, -1) 515 defer step(t, &steps, -1) 516 defer func() {}() 517 }() 518 }