Source file src/runtime/callers_test.go

     1  // Copyright 2016 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  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  func f1(pan bool) []uintptr {
    15  	return f2(pan) // line 15
    16  }
    17  
    18  func f2(pan bool) []uintptr {
    19  	return f3(pan) // line 19
    20  }
    21  
    22  func f3(pan bool) []uintptr {
    23  	if pan {
    24  		panic("f3") // line 24
    25  	}
    26  	ret := make([]uintptr, 20)
    27  	return ret[:runtime.Callers(0, ret)] // line 27
    28  }
    29  
    30  func testCallers(t *testing.T, pcs []uintptr, pan bool) {
    31  	m := make(map[string]int, len(pcs))
    32  	frames := runtime.CallersFrames(pcs)
    33  	for {
    34  		frame, more := frames.Next()
    35  		if frame.Function != "" {
    36  			m[frame.Function] = frame.Line
    37  		}
    38  		if !more {
    39  			break
    40  		}
    41  	}
    42  
    43  	var seen []string
    44  	for k := range m {
    45  		seen = append(seen, k)
    46  	}
    47  	t.Logf("functions seen: %s", strings.Join(seen, " "))
    48  
    49  	var f3Line int
    50  	if pan {
    51  		f3Line = 24
    52  	} else {
    53  		f3Line = 27
    54  	}
    55  	want := []struct {
    56  		name string
    57  		line int
    58  	}{
    59  		{"f1", 15},
    60  		{"f2", 19},
    61  		{"f3", f3Line},
    62  	}
    63  	for _, w := range want {
    64  		if got := m["runtime_test."+w.name]; got != w.line {
    65  			t.Errorf("%s is line %d, want %d", w.name, got, w.line)
    66  		}
    67  	}
    68  }
    69  
    70  func testCallersEqual(t *testing.T, pcs []uintptr, want []string) {
    71  	t.Helper()
    72  
    73  	got := make([]string, 0, len(want))
    74  
    75  	frames := runtime.CallersFrames(pcs)
    76  	for {
    77  		frame, more := frames.Next()
    78  		if !more || len(got) >= len(want) {
    79  			break
    80  		}
    81  		got = append(got, frame.Function)
    82  	}
    83  	if !reflect.DeepEqual(want, got) {
    84  		t.Fatalf("wanted %v, got %v", want, got)
    85  	}
    86  }
    87  
    88  func TestCallers(t *testing.T) {
    89  	testCallers(t, f1(false), false)
    90  }
    91  
    92  func TestCallersPanic(t *testing.T) {
    93  	// Make sure we don't have any extra frames on the stack (due to
    94  	// open-coded defer processing)
    95  	want := []string{"runtime.Callers", "runtime_test.TestCallersPanic.func1",
    96  		"runtime.gopanic", "runtime_test.f3", "runtime_test.f2", "runtime_test.f1",
    97  		"runtime_test.TestCallersPanic"}
    98  
    99  	defer func() {
   100  		if r := recover(); r == nil {
   101  			t.Fatal("did not panic")
   102  		}
   103  		pcs := make([]uintptr, 20)
   104  		pcs = pcs[:runtime.Callers(0, pcs)]
   105  		testCallers(t, pcs, true)
   106  		testCallersEqual(t, pcs, want)
   107  	}()
   108  	f1(true)
   109  }
   110  
   111  func TestCallersDoublePanic(t *testing.T) {
   112  	// Make sure we don't have any extra frames on the stack (due to
   113  	// open-coded defer processing)
   114  	want := []string{"runtime.Callers", "runtime_test.TestCallersDoublePanic.func1.1",
   115  		"runtime.gopanic", "runtime_test.TestCallersDoublePanic.func1", "runtime.gopanic", "runtime_test.TestCallersDoublePanic"}
   116  
   117  	defer func() {
   118  		defer func() {
   119  			pcs := make([]uintptr, 20)
   120  			pcs = pcs[:runtime.Callers(0, pcs)]
   121  			if recover() == nil {
   122  				t.Fatal("did not panic")
   123  			}
   124  			testCallersEqual(t, pcs, want)
   125  		}()
   126  		if recover() == nil {
   127  			t.Fatal("did not panic")
   128  		}
   129  		panic(2)
   130  	}()
   131  	panic(1)
   132  }
   133  
   134  // Test that a defer after a successful recovery looks like it is called directly
   135  // from the function with the defers.
   136  func TestCallersAfterRecovery(t *testing.T) {
   137  	want := []string{"runtime.Callers", "runtime_test.TestCallersAfterRecovery.func1", "runtime_test.TestCallersAfterRecovery"}
   138  
   139  	defer func() {
   140  		pcs := make([]uintptr, 20)
   141  		pcs = pcs[:runtime.Callers(0, pcs)]
   142  		testCallersEqual(t, pcs, want)
   143  	}()
   144  	defer func() {
   145  		if recover() == nil {
   146  			t.Fatal("did not recover from panic")
   147  		}
   148  	}()
   149  	panic(1)
   150  }
   151  
   152  func TestCallersAbortedPanic(t *testing.T) {
   153  	want := []string{"runtime.Callers", "runtime_test.TestCallersAbortedPanic.func2", "runtime_test.TestCallersAbortedPanic"}
   154  
   155  	defer func() {
   156  		r := recover()
   157  		if r != nil {
   158  			t.Fatalf("should be no panic remaining to recover")
   159  		}
   160  	}()
   161  
   162  	defer func() {
   163  		// panic2 was aborted/replaced by panic1, so when panic2 was
   164  		// recovered, there is no remaining panic on the stack.
   165  		pcs := make([]uintptr, 20)
   166  		pcs = pcs[:runtime.Callers(0, pcs)]
   167  		testCallersEqual(t, pcs, want)
   168  	}()
   169  	defer func() {
   170  		r := recover()
   171  		if r != "panic2" {
   172  			t.Fatalf("got %v, wanted %v", r, "panic2")
   173  		}
   174  	}()
   175  	defer func() {
   176  		// panic2 aborts/replaces panic1, because it is a recursive panic
   177  		// that is not recovered within the defer function called by
   178  		// panic1 panicking sequence
   179  		panic("panic2")
   180  	}()
   181  	panic("panic1")
   182  }
   183  
   184  func TestCallersAbortedPanic2(t *testing.T) {
   185  	want := []string{"runtime.Callers", "runtime_test.TestCallersAbortedPanic2.func2", "runtime_test.TestCallersAbortedPanic2"}
   186  	defer func() {
   187  		r := recover()
   188  		if r != nil {
   189  			t.Fatalf("should be no panic remaining to recover")
   190  		}
   191  	}()
   192  	defer func() {
   193  		pcs := make([]uintptr, 20)
   194  		pcs = pcs[:runtime.Callers(0, pcs)]
   195  		testCallersEqual(t, pcs, want)
   196  	}()
   197  	func() {
   198  		defer func() {
   199  			r := recover()
   200  			if r != "panic2" {
   201  				t.Fatalf("got %v, wanted %v", r, "panic2")
   202  			}
   203  		}()
   204  		func() {
   205  			defer func() {
   206  				// Again, panic2 aborts/replaces panic1
   207  				panic("panic2")
   208  			}()
   209  			panic("panic1")
   210  		}()
   211  	}()
   212  }
   213  
   214  func TestCallersNilPointerPanic(t *testing.T) {
   215  	// Make sure we don't have any extra frames on the stack (due to
   216  	// open-coded defer processing)
   217  	want := []string{"runtime.Callers", "runtime_test.TestCallersNilPointerPanic.func1",
   218  		"runtime.gopanic", "runtime.panicmem", "runtime.sigpanic",
   219  		"runtime_test.TestCallersNilPointerPanic"}
   220  
   221  	defer func() {
   222  		if r := recover(); r == nil {
   223  			t.Fatal("did not panic")
   224  		}
   225  		pcs := make([]uintptr, 20)
   226  		pcs = pcs[:runtime.Callers(0, pcs)]
   227  		testCallersEqual(t, pcs, want)
   228  	}()
   229  	var p *int
   230  	if *p == 3 {
   231  		t.Fatal("did not see nil pointer panic")
   232  	}
   233  }
   234  
   235  func TestCallersDivZeroPanic(t *testing.T) {
   236  	// Make sure we don't have any extra frames on the stack (due to
   237  	// open-coded defer processing)
   238  	want := []string{"runtime.Callers", "runtime_test.TestCallersDivZeroPanic.func1",
   239  		"runtime.gopanic", "runtime.panicdivide",
   240  		"runtime_test.TestCallersDivZeroPanic"}
   241  
   242  	defer func() {
   243  		if r := recover(); r == nil {
   244  			t.Fatal("did not panic")
   245  		}
   246  		pcs := make([]uintptr, 20)
   247  		pcs = pcs[:runtime.Callers(0, pcs)]
   248  		testCallersEqual(t, pcs, want)
   249  	}()
   250  	var n int
   251  	if 5/n == 1 {
   252  		t.Fatal("did not see divide-by-sizer panic")
   253  	}
   254  }
   255  
   256  func TestCallersDeferNilFuncPanic(t *testing.T) {
   257  	// Make sure we don't have any extra frames on the stack. We cut off the check
   258  	// at runtime.sigpanic, because non-open-coded defers (which may be used in
   259  	// non-opt or race checker mode) include an extra 'deferreturn' frame (which is
   260  	// where the nil pointer deref happens).
   261  	state := 1
   262  	want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanic.func1",
   263  		"runtime.gopanic", "runtime.panicmem", "runtime.sigpanic"}
   264  
   265  	defer func() {
   266  		if r := recover(); r == nil {
   267  			t.Fatal("did not panic")
   268  		}
   269  		pcs := make([]uintptr, 20)
   270  		pcs = pcs[:runtime.Callers(0, pcs)]
   271  		testCallersEqual(t, pcs, want)
   272  		if state == 1 {
   273  			t.Fatal("nil defer func panicked at defer time rather than function exit time")
   274  		}
   275  
   276  	}()
   277  	var f func()
   278  	defer f()
   279  	// Use the value of 'state' to make sure nil defer func f causes panic at
   280  	// function exit, rather than at the defer statement.
   281  	state = 2
   282  }
   283  
   284  // Same test, but forcing non-open-coded defer by putting the defer in a loop.  See
   285  // issue #36050
   286  func TestCallersDeferNilFuncPanicWithLoop(t *testing.T) {
   287  	state := 1
   288  	want := []string{"runtime.Callers", "runtime_test.TestCallersDeferNilFuncPanicWithLoop.func1",
   289  		"runtime.gopanic", "runtime.panicmem", "runtime.sigpanic", "runtime.deferreturn", "runtime_test.TestCallersDeferNilFuncPanicWithLoop"}
   290  
   291  	defer func() {
   292  		if r := recover(); r == nil {
   293  			t.Fatal("did not panic")
   294  		}
   295  		pcs := make([]uintptr, 20)
   296  		pcs = pcs[:runtime.Callers(0, pcs)]
   297  		testCallersEqual(t, pcs, want)
   298  		if state == 1 {
   299  			t.Fatal("nil defer func panicked at defer time rather than function exit time")
   300  		}
   301  
   302  	}()
   303  
   304  	for i := 0; i < 1; i++ {
   305  		var f func()
   306  		defer f()
   307  	}
   308  	// Use the value of 'state' to make sure nil defer func f causes panic at
   309  	// function exit, rather than at the defer statement.
   310  	state = 2
   311  }
   312  
   313  // issue #51988
   314  // Func.Endlineno was lost when instantiating generic functions, leading to incorrect
   315  // stack trace positions.
   316  func TestCallersEndlineno(t *testing.T) {
   317  	testNormalEndlineno(t)
   318  	testGenericEndlineno[int](t)
   319  }
   320  
   321  func testNormalEndlineno(t *testing.T) {
   322  	defer testCallerLine(t, callerLine(t, 0)+1)
   323  }
   324  
   325  func testGenericEndlineno[_ any](t *testing.T) {
   326  	defer testCallerLine(t, callerLine(t, 0)+1)
   327  }
   328  
   329  func testCallerLine(t *testing.T, want int) {
   330  	if have := callerLine(t, 1); have != want {
   331  		t.Errorf("callerLine(1) returned %d, but want %d\n", have, want)
   332  	}
   333  }
   334  
   335  func callerLine(t *testing.T, skip int) int {
   336  	_, _, line, ok := runtime.Caller(skip + 1)
   337  	if !ok {
   338  		t.Fatalf("runtime.Caller(%d) failed", skip+1)
   339  	}
   340  	return line
   341  }
   342  

View as plain text