github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/callers_test.go (about) 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 343 func BenchmarkCallers(b *testing.B) { 344 b.Run("cached", func(b *testing.B) { 345 // Very pcvalueCache-friendly, no inlining. 346 callersCached(b, 100) 347 }) 348 b.Run("inlined", func(b *testing.B) { 349 // Some inlining, still pretty cache-friendly. 350 callersInlined(b, 100) 351 }) 352 b.Run("no-cache", func(b *testing.B) { 353 // Cache-hostile 354 callersNoCache(b, 100) 355 }) 356 } 357 358 func callersCached(b *testing.B, n int) int { 359 if n <= 0 { 360 pcs := make([]uintptr, 32) 361 b.ResetTimer() 362 for i := 0; i < b.N; i++ { 363 runtime.Callers(0, pcs) 364 } 365 b.StopTimer() 366 return 0 367 } 368 return 1 + callersCached(b, n-1) 369 } 370 371 func callersInlined(b *testing.B, n int) int { 372 if n <= 0 { 373 pcs := make([]uintptr, 32) 374 b.ResetTimer() 375 for i := 0; i < b.N; i++ { 376 runtime.Callers(0, pcs) 377 } 378 b.StopTimer() 379 return 0 380 } 381 return 1 + callersInlined1(b, n-1) 382 } 383 func callersInlined1(b *testing.B, n int) int { return callersInlined2(b, n) } 384 func callersInlined2(b *testing.B, n int) int { return callersInlined3(b, n) } 385 func callersInlined3(b *testing.B, n int) int { return callersInlined4(b, n) } 386 func callersInlined4(b *testing.B, n int) int { return callersInlined(b, n) } 387 388 func callersNoCache(b *testing.B, n int) int { 389 if n <= 0 { 390 pcs := make([]uintptr, 32) 391 b.ResetTimer() 392 for i := 0; i < b.N; i++ { 393 runtime.Callers(0, pcs) 394 } 395 b.StopTimer() 396 return 0 397 } 398 switch n % 16 { 399 case 0: 400 return 1 + callersNoCache(b, n-1) 401 case 1: 402 return 1 + callersNoCache(b, n-1) 403 case 2: 404 return 1 + callersNoCache(b, n-1) 405 case 3: 406 return 1 + callersNoCache(b, n-1) 407 case 4: 408 return 1 + callersNoCache(b, n-1) 409 case 5: 410 return 1 + callersNoCache(b, n-1) 411 case 6: 412 return 1 + callersNoCache(b, n-1) 413 case 7: 414 return 1 + callersNoCache(b, n-1) 415 case 8: 416 return 1 + callersNoCache(b, n-1) 417 case 9: 418 return 1 + callersNoCache(b, n-1) 419 case 10: 420 return 1 + callersNoCache(b, n-1) 421 case 11: 422 return 1 + callersNoCache(b, n-1) 423 case 12: 424 return 1 + callersNoCache(b, n-1) 425 case 13: 426 return 1 + callersNoCache(b, n-1) 427 case 14: 428 return 1 + callersNoCache(b, n-1) 429 default: 430 return 1 + callersNoCache(b, n-1) 431 } 432 } 433 434 func BenchmarkFPCallers(b *testing.B) { 435 b.Run("cached", func(b *testing.B) { 436 // Very pcvalueCache-friendly, no inlining. 437 fpCallersCached(b, 100) 438 }) 439 } 440 441 func fpCallersCached(b *testing.B, n int) int { 442 if n <= 0 { 443 pcs := make([]uintptr, 32) 444 b.ResetTimer() 445 for i := 0; i < b.N; i++ { 446 runtime.FPCallers(pcs) 447 } 448 b.StopTimer() 449 return 0 450 } 451 return 1 + fpCallersCached(b, n-1) 452 }