Black Lives Matter. Support the Equal Justice Initiative.

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