Source file
src/runtime/traceback_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "bytes"
9 "context"
10 "fmt"
11 "internal/abi"
12 "internal/asan"
13 "internal/msan"
14 "internal/race"
15 "internal/testenv"
16 "regexp"
17 "runtime"
18 "runtime/debug"
19 "runtime/pprof"
20 "strconv"
21 "strings"
22 "sync"
23 "testing"
24 _ "unsafe"
25 )
26
27
28 func TestTracebackInlined(t *testing.T) {
29 testenv.SkipIfOptimizationOff(t)
30 check := func(t *testing.T, r *ttiResult, funcs ...string) {
31 t.Helper()
32
33
34 frames := parseTraceback1(t, r.printed).frames
35 t.Log(r.printed)
36
37 for len(frames) > 0 && frames[0].funcName != "runtime_test.ttiLeaf" {
38 frames = frames[1:]
39 }
40 if len(frames) == 0 {
41 t.Errorf("missing runtime_test.ttiLeaf")
42 return
43 }
44 frames = frames[1:]
45
46 for i, want := range funcs {
47 got := "<end>"
48 if i < len(frames) {
49 got = frames[i].funcName
50 if strings.HasSuffix(want, ")") {
51 got += "(" + frames[i].args + ")"
52 }
53 }
54 if got != want {
55 t.Errorf("got %s, want %s", got, want)
56 return
57 }
58 }
59 }
60
61 t.Run("simple", func(t *testing.T) {
62
63 r := ttiSimple1()
64 check(t, r, "runtime_test.ttiSimple3(...)", "runtime_test.ttiSimple2(...)", "runtime_test.ttiSimple1()")
65 })
66
67 t.Run("sigpanic", func(t *testing.T) {
68
69 r := ttiSigpanic1()
70 check(t, r, "runtime_test.ttiSigpanic1.func1()", "panic", "runtime_test.ttiSigpanic3(...)", "runtime_test.ttiSigpanic2(...)", "runtime_test.ttiSigpanic1()")
71 })
72
73 t.Run("wrapper", func(t *testing.T) {
74
75 r := ttiWrapper1()
76 check(t, r, "runtime_test.ttiWrapper.m1(...)", "runtime_test.ttiWrapper1()")
77 })
78
79 t.Run("excluded", func(t *testing.T) {
80
81
82 r := ttiExcluded1()
83 check(t, r, "runtime_test.ttiExcluded3(...)", "runtime_test.ttiExcluded1()")
84 })
85 }
86
87 type ttiResult struct {
88 printed string
89 }
90
91
92 func ttiLeaf() *ttiResult {
93
94 printed := string(debug.Stack())
95 return &ttiResult{printed}
96 }
97
98
99 func ttiSimple1() *ttiResult {
100 return ttiSimple2()
101 }
102 func ttiSimple2() *ttiResult {
103 return ttiSimple3()
104 }
105 func ttiSimple3() *ttiResult {
106 return ttiLeaf()
107 }
108
109
110 func ttiSigpanic1() (res *ttiResult) {
111 defer func() {
112 res = ttiLeaf()
113 recover()
114 }()
115 ttiSigpanic2()
116
117
118
119 if alwaysTrue {
120 panic("did not panic")
121 }
122 return nil
123 }
124 func ttiSigpanic2() {
125 ttiSigpanic3()
126 }
127 func ttiSigpanic3() {
128 var p *int
129 *p = 3
130 }
131
132 var alwaysTrue = true
133
134
135 func ttiWrapper1() *ttiResult {
136 var w ttiWrapper
137 m := (*ttiWrapper).m1
138 return m(&w)
139 }
140
141 type ttiWrapper struct{}
142
143 func (w ttiWrapper) m1() *ttiResult {
144 return ttiLeaf()
145 }
146
147
148 func ttiExcluded1() *ttiResult {
149 return ttiExcluded2()
150 }
151
152
153
154
155
156
157
158
159 func ttiExcluded2() *ttiResult {
160 return ttiExcluded3()
161 }
162 func ttiExcluded3() *ttiResult {
163 return ttiLeaf()
164 }
165
166 var testTracebackArgsBuf [1000]byte
167
168 func TestTracebackElision(t *testing.T) {
169
170
171
172
173 for _, elided := range []int{0, 1, 10} {
174 t.Run(fmt.Sprintf("elided=%d", elided), func(t *testing.T) {
175 n := elided + runtime.TracebackInnerFrames + runtime.TracebackOuterFrames
176
177
178 stackChan := make(chan string)
179 go tteStack(n, stackChan)
180 stack := <-stackChan
181 tb := parseTraceback1(t, stack)
182
183
184 i := 0
185 for i < n {
186 if len(tb.frames) == 0 {
187 t.Errorf("traceback ended early")
188 break
189 }
190 fr := tb.frames[0]
191 if i == runtime.TracebackInnerFrames && elided > 0 {
192
193 if fr.elided != elided {
194 t.Errorf("want %d frames elided", elided)
195 break
196 }
197 i += fr.elided
198 } else {
199 want := fmt.Sprintf("runtime_test.tte%d", (i+1)%5)
200 if i == 0 {
201 want = "runtime/debug.Stack"
202 } else if i == n-1 {
203 want = "runtime_test.tteStack"
204 }
205 if fr.funcName != want {
206 t.Errorf("want %s, got %s", want, fr.funcName)
207 break
208 }
209 i++
210 }
211 tb.frames = tb.frames[1:]
212 }
213 if !t.Failed() && len(tb.frames) > 0 {
214 t.Errorf("got %d more frames than expected", len(tb.frames))
215 }
216 if t.Failed() {
217 t.Logf("traceback diverged at frame %d", i)
218 off := len(stack)
219 if len(tb.frames) > 0 {
220 off = tb.frames[0].off
221 }
222 t.Logf("traceback before error:\n%s", stack[:off])
223 t.Logf("traceback after error:\n%s", stack[off:])
224 }
225 })
226 }
227 }
228
229
230
231
232 func tteStack(n int, stack chan<- string) {
233 n--
234
235
236 switch n % 5 {
237 case 0:
238 stack <- tte0(n)
239 case 1:
240 stack <- tte1(n)
241 case 2:
242 stack <- tte2(n)
243 case 3:
244 stack <- tte3(n)
245 case 4:
246 stack <- tte4(n)
247 default:
248 panic("unreachable")
249 }
250 }
251 func tte0(n int) string {
252 return tte4(n - 1)
253 }
254 func tte1(n int) string {
255 return tte0(n - 1)
256 }
257 func tte2(n int) string {
258
259
260 if n < 2 {
261 panic("bad n")
262 }
263 if n == 2 {
264 return string(debug.Stack())
265 }
266 return tte1(n - 1)
267 }
268 func tte3(n int) string {
269 return tte2(n - 1)
270 }
271 func tte4(n int) string {
272 return tte3(n - 1)
273 }
274
275 func TestTracebackArgs(t *testing.T) {
276 if *flagQuick {
277 t.Skip("-quick")
278 }
279 optimized := !testenv.OptimizationOff()
280 abiSel := func(x, y string) string {
281
282
283 if optimized && abi.IntArgRegs > 0 {
284 return x
285 }
286 return y
287 }
288
289 tests := []struct {
290 fn func() int
291 expect string
292 }{
293
294 {
295 func() int { return testTracebackArgs1(1, 2, 3, 4, 5) },
296 "testTracebackArgs1(0x1, 0x2, 0x3, 0x4, 0x5)",
297 },
298
299 {
300 func() int {
301 return testTracebackArgs2(false, struct {
302 a, b, c int
303 x [2]int
304 }{1, 2, 3, [2]int{4, 5}}, [0]int{}, [3]byte{6, 7, 8})
305 },
306 "testTracebackArgs2(0x0, {0x1, 0x2, 0x3, {0x4, 0x5}}, {}, {0x6, 0x7, 0x8})",
307 },
308 {
309 func() int { return testTracebackArgs3([3]byte{1, 2, 3}, 4, 5, 6, [3]byte{7, 8, 9}) },
310 "testTracebackArgs3({0x1, 0x2, 0x3}, 0x4, 0x5, 0x6, {0x7, 0x8, 0x9})",
311 },
312
313 {
314 func() int { return testTracebackArgs4(false, [1][1][1][1][1][1][1][1][1][1]int{}) },
315 "testTracebackArgs4(0x0, {{{{{...}}}}})",
316 },
317
318 {
319 func() int {
320 z := [0]int{}
321 return testTracebackArgs5(false, struct {
322 x int
323 y [0]int
324 z [2][0]int
325 }{1, z, [2][0]int{}}, z, z, z, z, z, z, z, z, z, z, z, z)
326 },
327 "testTracebackArgs5(0x0, {0x1, {}, {{}, {}}}, {}, {}, {}, {}, {}, ...)",
328 },
329
330
331
332 {
333 func() int { return testTracebackArgs6a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) },
334 "testTracebackArgs6a(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa)",
335 },
336
337 {
338 func() int { return testTracebackArgs6b(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) },
339 "testTracebackArgs6b(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...)",
340 },
341
342 {
343 func() int { return testTracebackArgs7a([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) },
344 "testTracebackArgs7a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa})",
345 },
346
347 {
348 func() int { return testTracebackArgs7b([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}) },
349 "testTracebackArgs7b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...})",
350 },
351
352 {
353 func() int { return testTracebackArgs7c([10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 11) },
354 "testTracebackArgs7c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}, ...)",
355 },
356
357 {
358 func() int { return testTracebackArgs7d([11]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 12) },
359 "testTracebackArgs7d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, ...}, ...)",
360 },
361
362 {
363 func() int { return testTracebackArgs8a(testArgsType8a{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}}) },
364 "testTracebackArgs8a({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}})",
365 },
366
367 {
368 func() int { return testTracebackArgs8b(testArgsType8b{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}}) },
369 "testTracebackArgs8b({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}})",
370 },
371
372 {
373 func() int { return testTracebackArgs8c(testArgsType8c{1, 2, 3, 4, 5, 6, 7, 8, [2]int{9, 10}, 11}) },
374 "testTracebackArgs8c({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa}, ...})",
375 },
376
377 {
378 func() int { return testTracebackArgs8d(testArgsType8d{1, 2, 3, 4, 5, 6, 7, 8, [3]int{9, 10, 11}, 12}) },
379 "testTracebackArgs8d({0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, {0x9, 0xa, ...}, ...})",
380 },
381
382
383
384
385 {
386 func() int {
387 poisonStack()
388 return testTracebackArgs9(1, 2, 3, 4, [2]int{5, 6}, 7)
389 },
390 abiSel(
391 "testTracebackArgs9(0x1, 0xffffffff?, 0x3, 0xff?, {0x5, 0x6}, 0x7)",
392 "testTracebackArgs9(0x1, 0x2, 0x3, 0x4, {0x5, 0x6}, 0x7)"),
393 },
394
395
396 {
397 func() int {
398 poisonStack()
399 return testTracebackArgs10(1, 2, 3, 4, 5)
400 },
401 abiSel(
402 "testTracebackArgs10(0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?, 0xffffffff?)",
403 "testTracebackArgs10(0x1, 0x2, 0x3, 0x4, 0x5)"),
404 },
405
406
407 {
408 func() int {
409 poisonStack()
410 return testTracebackArgs11a(1, 2, 3)
411 },
412 abiSel(
413 "testTracebackArgs11a(0xffffffff?, 0xffffffff?, 0xffffffff?)",
414 "testTracebackArgs11a(0x1, 0x2, 0x3)"),
415 },
416
417
418 {
419 func() int {
420 poisonStack()
421 return testTracebackArgs11b(1, 2, 3, 4)
422 },
423 abiSel(
424 "testTracebackArgs11b(0xffffffff?, 0xffffffff?, 0x3?, 0x4)",
425 "testTracebackArgs11b(0x1, 0x2, 0x3, 0x4)"),
426 },
427
428
429
430 {
431 func() int {
432 poisonStack()
433 return testTracebackArgsSlice(testTracebackArgsSliceBackingStore[:])
434 },
435
436 fmt.Sprintf("testTracebackArgsSlice({%p, 0x2, ", &testTracebackArgsSliceBackingStore[0]),
437 },
438 }
439 for _, test := range tests {
440 n := test.fn()
441 got := testTracebackArgsBuf[:n]
442 if !bytes.Contains(got, []byte(test.expect)) {
443 t.Errorf("traceback does not contain expected string: want %q, got\n%s", test.expect, got)
444 }
445 }
446 }
447
448
449 func testTracebackArgs1(a, b, c, d, e int) int {
450 n := runtime.Stack(testTracebackArgsBuf[:], false)
451 if a < 0 {
452
453 return a + b + c + d + e
454 }
455 return n
456 }
457
458
459 func testTracebackArgs2(a bool, b struct {
460 a, b, c int
461 x [2]int
462 }, _ [0]int, d [3]byte) int {
463 n := runtime.Stack(testTracebackArgsBuf[:], false)
464 if a {
465
466 return b.a + b.b + b.c + b.x[0] + b.x[1] + int(d[0]) + int(d[1]) + int(d[2])
467 }
468 return n
469 }
470
471
472
473 func testTracebackArgs3(x [3]byte, a, b, c int, y [3]byte) int {
474 n := runtime.Stack(testTracebackArgsBuf[:], false)
475 if a < 0 {
476
477 return int(x[0]) + int(x[1]) + int(x[2]) + a + b + c + int(y[0]) + int(y[1]) + int(y[2])
478 }
479 return n
480 }
481
482
483 func testTracebackArgs4(a bool, x [1][1][1][1][1][1][1][1][1][1]int) int {
484 n := runtime.Stack(testTracebackArgsBuf[:], false)
485 if a {
486 panic(x)
487 }
488 return n
489 }
490
491
492 func testTracebackArgs5(a bool, x struct {
493 x int
494 y [0]int
495 z [2][0]int
496 }, _, _, _, _, _, _, _, _, _, _, _, _ [0]int) int {
497 n := runtime.Stack(testTracebackArgsBuf[:], false)
498 if a {
499 panic(x)
500 }
501 return n
502 }
503
504
505 func testTracebackArgs6a(a, b, c, d, e, f, g, h, i, j int) int {
506 n := runtime.Stack(testTracebackArgsBuf[:], false)
507 if a < 0 {
508
509 return a + b + c + d + e + f + g + h + i + j
510 }
511 return n
512 }
513
514
515 func testTracebackArgs6b(a, b, c, d, e, f, g, h, i, j, k int) int {
516 n := runtime.Stack(testTracebackArgsBuf[:], false)
517 if a < 0 {
518
519 return a + b + c + d + e + f + g + h + i + j + k
520 }
521 return n
522 }
523
524
525 func testTracebackArgs7a(a [10]int) int {
526 n := runtime.Stack(testTracebackArgsBuf[:], false)
527 if a[0] < 0 {
528
529 return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9]
530 }
531 return n
532 }
533
534
535 func testTracebackArgs7b(a [11]int) int {
536 n := runtime.Stack(testTracebackArgsBuf[:], false)
537 if a[0] < 0 {
538
539 return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10]
540 }
541 return n
542 }
543
544
545 func testTracebackArgs7c(a [10]int, b int) int {
546 n := runtime.Stack(testTracebackArgsBuf[:], false)
547 if a[0] < 0 {
548
549 return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + b
550 }
551 return n
552 }
553
554
555 func testTracebackArgs7d(a [11]int, b int) int {
556 n := runtime.Stack(testTracebackArgsBuf[:], false)
557 if a[0] < 0 {
558
559 return a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8] + a[9] + a[10] + b
560 }
561 return n
562 }
563
564 type testArgsType8a struct {
565 a, b, c, d, e, f, g, h int
566 i [2]int
567 }
568 type testArgsType8b struct {
569 a, b, c, d, e, f, g, h int
570 i [3]int
571 }
572 type testArgsType8c struct {
573 a, b, c, d, e, f, g, h int
574 i [2]int
575 j int
576 }
577 type testArgsType8d struct {
578 a, b, c, d, e, f, g, h int
579 i [3]int
580 j int
581 }
582
583
584 func testTracebackArgs8a(a testArgsType8a) int {
585 n := runtime.Stack(testTracebackArgsBuf[:], false)
586 if a.a < 0 {
587
588 return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1]
589 }
590 return n
591 }
592
593
594 func testTracebackArgs8b(a testArgsType8b) int {
595 n := runtime.Stack(testTracebackArgsBuf[:], false)
596 if a.a < 0 {
597
598 return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2]
599 }
600 return n
601 }
602
603
604 func testTracebackArgs8c(a testArgsType8c) int {
605 n := runtime.Stack(testTracebackArgsBuf[:], false)
606 if a.a < 0 {
607
608 return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.j
609 }
610 return n
611 }
612
613
614 func testTracebackArgs8d(a testArgsType8d) int {
615 n := runtime.Stack(testTracebackArgsBuf[:], false)
616 if a.a < 0 {
617
618 return a.b + a.c + a.d + a.e + a.f + a.g + a.h + a.i[0] + a.i[1] + a.i[2] + a.j
619 }
620 return n
621 }
622
623
624
625
626
627 func testTracebackArgs9(a int64, b int32, c int16, d int8, x [2]int, y int) int {
628 if a < 0 {
629 println(&y)
630 }
631 n := runtime.Stack(testTracebackArgsBuf[:], false)
632 if a < 0 {
633
634 return int(a) + int(c)
635 }
636 return n
637 }
638
639
640
641
642
643 func testTracebackArgs10(a, b, c, d, e int32) int {
644
645 return runtime.Stack(testTracebackArgsBuf[:], false)
646 }
647
648
649
650
651
652
653
654 func testTracebackArgs11a(a, b, c int32) int {
655 if a < 0 {
656 println(a, b, c)
657 }
658 if b < 0 {
659 return int(a + b + c)
660 }
661 return runtime.Stack(testTracebackArgsBuf[:], false)
662 }
663
664
665
666
667
668
669
670 func testTracebackArgs11b(a, b, c, d int32) int {
671 var x int32
672 if a < 0 {
673 print()
674 x = b
675 } else {
676 print()
677 x = c
678 }
679 if d < 0 {
680 return int(x + d)
681 }
682 return runtime.Stack(testTracebackArgsBuf[:], false)
683 }
684
685
686
687
688
689
690
691 func testTracebackArgsSlice(a []int) int {
692 n := runtime.Stack(testTracebackArgsBuf[:], false)
693 return a[1] + n
694 }
695
696 var testTracebackArgsSliceBackingStore [2]int
697
698
699
700
701 func poisonStack() [20]int {
702 return [20]int{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
703 }
704
705 func TestTracebackParentChildGoroutines(t *testing.T) {
706 parent := fmt.Sprintf("goroutine %d", runtime.Goid())
707 var wg sync.WaitGroup
708 wg.Add(1)
709 go func() {
710 defer wg.Done()
711 buf := make([]byte, 1<<10)
712
713
714
715
716 stack := string(buf[:runtime.Stack(buf, false)])
717 child := fmt.Sprintf("goroutine %d", runtime.Goid())
718 if !strings.Contains(stack, parent) || !strings.Contains(stack, child) {
719 t.Errorf("did not see parent (%s) and child (%s) IDs in stack, got %s", parent, child, stack)
720 }
721 }()
722 wg.Wait()
723 }
724
725 type traceback struct {
726 frames []*tbFrame
727 createdBy *tbFrame
728 }
729
730 type tbFrame struct {
731 funcName string
732 args string
733 inlined bool
734
735
736
737 elided int
738
739 off int
740 }
741
742
743
744 func parseTraceback(t *testing.T, tb string) []*traceback {
745
746
747 off := 0
748 lineNo := 0
749 fatal := func(f string, args ...any) {
750 msg := fmt.Sprintf(f, args...)
751 t.Fatalf("%s (line %d):\n%s", msg, lineNo, tb)
752 }
753 parseFrame := func(funcName, args string) *tbFrame {
754
755 if !strings.HasPrefix(tb, "\t") {
756 fatal("missing source line")
757 }
758 _, tb, _ = strings.Cut(tb, "\n")
759 lineNo++
760 inlined := args == "..."
761 return &tbFrame{funcName: funcName, args: args, inlined: inlined, off: off}
762 }
763 var elidedRe = regexp.MustCompile(`^\.\.\.([0-9]+) frames elided\.\.\.$`)
764 var tbs []*traceback
765 var cur *traceback
766 tbLen := len(tb)
767 for len(tb) > 0 {
768 var line string
769 off = tbLen - len(tb)
770 line, tb, _ = strings.Cut(tb, "\n")
771 lineNo++
772 switch {
773 case strings.HasPrefix(line, "goroutine "):
774 cur = &traceback{}
775 tbs = append(tbs, cur)
776 case line == "":
777
778 cur = nil
779 case line[0] == '\t':
780 fatal("unexpected indent")
781 case strings.HasPrefix(line, "created by "):
782 funcName := line[len("created by "):]
783 cur.createdBy = parseFrame(funcName, "")
784 case strings.HasSuffix(line, ")"):
785 line = line[:len(line)-1]
786 funcName, args, found := strings.Cut(line, "(")
787 if !found {
788 fatal("missing (")
789 }
790 frame := parseFrame(funcName, args)
791 cur.frames = append(cur.frames, frame)
792 case elidedRe.MatchString(line):
793
794 nStr := elidedRe.FindStringSubmatch(line)
795 n, _ := strconv.Atoi(nStr[1])
796 frame := &tbFrame{elided: n}
797 cur.frames = append(cur.frames, frame)
798 }
799 }
800 return tbs
801 }
802
803
804
805 func parseTraceback1(t *testing.T, tb string) *traceback {
806 tbs := parseTraceback(t, tb)
807 if len(tbs) != 1 {
808 t.Fatalf("want 1 goroutine, got %d:\n%s", len(tbs), tb)
809 }
810 return tbs[0]
811 }
812
813
814 func testTracebackGenericFn[T any](buf []byte) int {
815 return runtime.Stack(buf[:], false)
816 }
817
818 func testTracebackGenericFnInlined[T any](buf []byte) int {
819 return runtime.Stack(buf[:], false)
820 }
821
822 type testTracebackGenericTyp[P any] struct{ x P }
823
824
825 func (t testTracebackGenericTyp[P]) M(buf []byte) int {
826 return runtime.Stack(buf[:], false)
827 }
828
829 func (t testTracebackGenericTyp[P]) Inlined(buf []byte) int {
830 return runtime.Stack(buf[:], false)
831 }
832
833 func TestTracebackGeneric(t *testing.T) {
834 if *flagQuick {
835 t.Skip("-quick")
836 }
837 var x testTracebackGenericTyp[int]
838 tests := []struct {
839 fn func([]byte) int
840 expect string
841 }{
842
843 {
844 testTracebackGenericFn[int],
845 "testTracebackGenericFn[...](",
846 },
847
848 {
849 func(buf []byte) int { return testTracebackGenericFnInlined[int](buf) },
850 "testTracebackGenericFnInlined[...](",
851 },
852
853 {
854 x.M,
855 "testTracebackGenericTyp[...].M(",
856 },
857
858 {
859 func(buf []byte) int { return x.Inlined(buf) },
860 "testTracebackGenericTyp[...].Inlined(",
861 },
862 }
863 var buf [1000]byte
864 for _, test := range tests {
865 n := test.fn(buf[:])
866 got := buf[:n]
867 if !bytes.Contains(got, []byte(test.expect)) {
868 t.Errorf("traceback does not contain expected string: want %q, got\n%s", test.expect, got)
869 }
870 if bytes.Contains(got, []byte("shape")) {
871 t.Errorf("traceback contains shape name: got\n%s", got)
872 }
873 }
874 }
875
876 func TestSetCgoTracebackNoCgo(t *testing.T) {
877 if asan.Enabled || msan.Enabled || race.Enabled {
878 t.Skip("skipped test: sanitizer builds use cgo")
879 }
880
881 output := runTestProg(t, "testprog", "SetCgoTracebackNoCgo")
882 want := "OK\n"
883 if output != want {
884 t.Fatalf("want %s, got %s\n", want, output)
885 }
886 }
887
888 func TestTracebackGoroutineLabels(t *testing.T) {
889 t.Setenv("GODEBUG", "tracebacklabels=1")
890 for _, tbl := range []struct {
891 l pprof.LabelSet
892 expTB string
893 }{
894 {l: pprof.Labels("foobar", "baz"), expTB: `{"foobar": "baz"}`},
895
896 {l: pprof.Labels("foobar", "baz", "fizzle", "bit"), expTB: `{"fizzle": "bit", "foobar": "baz"}`},
897
898 {l: pprof.Labels("fizzle", "bit", "foobar", "baz\n"), expTB: `{"fizzle": "bit", "foobar": "baz\n"}`},
899
900 {l: pprof.Labels("fizzle", "b\033it", "foo\"ba\x00r", "baz\n"), expTB: `{"fizzle": "b\x1bit", "foo\"ba\x00r": "baz\n"}`},
901
902 {l: pprof.Labels("fizzle", "\u1234Σ", "fooba\x00r", "baz\n"), expTB: `{"fizzle": "\u1234\u03a3", "fooba\x00r": "baz\n"}`},
903
904 {l: pprof.Labels("fizz\tle", "\U00045678boop", "fooba\x00r", "baz\n"), expTB: `{"fizz\tle": "\U00045678boop", "fooba\x00r": "baz\n"}`},
905
906 {l: pprof.Labels("fiz\\zl\re", "\U00045678boop", "fooba\x00r", "baz\n"), expTB: `{"fiz\\zl\re": "\U00045678boop", "fooba\x00r": "baz\n"}`},
907 } {
908 t.Run(tbl.expTB, func(t *testing.T) {
909 verifyLabels := func() {
910 t.Helper()
911 buf := make([]byte, 1<<10)
912
913
914 stack := string(buf[:runtime.Stack(buf, false)])
915 if !strings.Contains(stack, "labels:"+tbl.expTB) {
916 t.Errorf("failed to find goroutine labels with labels %s (as %s) got:\n%s\n---", tbl.l, tbl.expTB, stack)
917 }
918 }
919
920 lblCtx := pprof.WithLabels(context.Background(), tbl.l)
921 pprof.SetGoroutineLabels(lblCtx)
922 var wg sync.WaitGroup
923
924 wg.Go(verifyLabels)
925
926 verifyLabels()
927 wg.Wait()
928 })
929 }
930 }
931
932 func TestTracebackGoroutineLabelsDisabledGODEBUG(t *testing.T) {
933 t.Setenv("GODEBUG", "tracebacklabels=0")
934 lbls := pprof.Labels("foobar", "baz")
935 verifyLabels := func() {
936 t.Helper()
937 buf := make([]byte, 1<<10)
938
939
940 stack := string(buf[:runtime.Stack(buf, false)])
941 if strings.Contains(stack, "labels:") {
942 t.Errorf("found goroutine labels with labels %s got:\n%s\n---", lbls, stack)
943 }
944 }
945
946 lblCtx := pprof.WithLabels(context.Background(), lbls)
947 pprof.SetGoroutineLabels(lblCtx)
948 var wg sync.WaitGroup
949
950 wg.Go(verifyLabels)
951
952 verifyLabels()
953 wg.Wait()
954 }
955
View as plain text