Source file
src/runtime/callers_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "runtime"
9 "slices"
10 "strings"
11 "testing"
12 )
13
14 func f1(pan bool) []uintptr {
15 return f2(pan)
16 }
17
18 func f2(pan bool) []uintptr {
19 return f3(pan)
20 }
21
22 func f3(pan bool) []uintptr {
23 if pan {
24 panic("f3")
25 }
26 ret := make([]uintptr, 20)
27 return ret[:runtime.Callers(0, ret)]
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 !slices.Equal(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
94
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
113
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
135
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
164
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
177
178
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
207 panic("panic2")
208 }()
209 panic("panic1")
210 }()
211 }()
212 }
213
214 func TestCallersNilPointerPanic(t *testing.T) {
215
216
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
237
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
258
259
260
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
280
281 state = 2
282 }
283
284
285
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
309
310 state = 2
311 }
312
313
314
315
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
346 callersCached(b, 100)
347 })
348 b.Run("inlined", func(b *testing.B) {
349
350 callersInlined(b, 100)
351 })
352 b.Run("no-cache", func(b *testing.B) {
353
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
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 }
453
454 func TestFPUnwindAfterRecovery(t *testing.T) {
455 if !runtime.FramePointerEnabled {
456 t.Skip("frame pointers not supported for this architecture")
457 }
458
459
460
461
462
463
464
465
466
467 defer func() {
468 pcs := make([]uintptr, 32)
469 for i := range pcs {
470
471
472
473
474
475
476
477
478 pcs[i] = 10
479 }
480 runtime.FPCallers(pcs)
481 t.Logf("%v", pcs)
482 }()
483 defer func() {
484 if recover() == nil {
485 t.Fatal("did not recover from panic")
486 }
487 }()
488 panic(1)
489 }
490
View as plain text