...

Source file src/runtime/defer_test.go

Documentation: runtime

		 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 interface{}) {
	 437  	if n != err {
	 438  		t.Fatalf("have %v, want %v", err, n)
	 439  	}
	 440  }
	 441  

View as plain text