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