Source file
src/runtime/crash_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "flag"
12 "fmt"
13 "internal/asan"
14 "internal/msan"
15 "internal/profile"
16 "internal/race"
17 "internal/testenv"
18 traceparse "internal/trace"
19 "io"
20 "log"
21 "os"
22 "os/exec"
23 "path/filepath"
24 "regexp"
25 "runtime"
26 "runtime/trace"
27 "strings"
28 "sync"
29 "testing"
30 "time"
31 )
32
33 var toRemove []string
34
35 const entrypointVar = "RUNTIME_TEST_ENTRYPOINT"
36
37 func TestMain(m *testing.M) {
38 switch entrypoint := os.Getenv(entrypointVar); entrypoint {
39 case "panic":
40 crashViaPanic()
41 panic("unreachable")
42 case "trap":
43 crashViaTrap()
44 panic("unreachable")
45 default:
46 log.Fatalf("invalid %s: %q", entrypointVar, entrypoint)
47 case "":
48
49 }
50
51 _, coreErrBefore := os.Stat("core")
52
53 status := m.Run()
54 for _, file := range toRemove {
55 os.RemoveAll(file)
56 }
57
58 _, coreErrAfter := os.Stat("core")
59 if coreErrBefore != nil && coreErrAfter == nil {
60 fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind")
61 if status == 0 {
62 status = 1
63 }
64 }
65
66 os.Exit(status)
67 }
68
69 var testprog struct {
70 sync.Mutex
71 dir string
72 target map[string]*buildexe
73 }
74
75 type buildexe struct {
76 once sync.Once
77 exe string
78 err error
79 }
80
81 func runTestProg(t *testing.T, binary, name string, env ...string) string {
82 if *flagQuick {
83 t.Skip("-quick")
84 }
85
86 testenv.MustHaveGoBuild(t)
87 t.Helper()
88
89 exe, err := buildTestProg(t, binary)
90 if err != nil {
91 t.Fatal(err)
92 }
93
94 return runBuiltTestProg(t, exe, name, env...)
95 }
96
97 func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
98 t.Helper()
99
100 out, _ := runBuiltTestProgErr(t, exe, name, env...)
101 return out
102 }
103
104 func runBuiltTestProgErr(t *testing.T, exe, name string, env ...string) (string, error) {
105 t.Helper()
106
107 if *flagQuick {
108 t.Skip("-quick")
109 }
110
111 start := time.Now()
112
113 cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name))
114 cmd.Env = append(cmd.Env, env...)
115 if testing.Short() {
116 cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
117 }
118 out, err := cmd.CombinedOutput()
119 if err == nil {
120 t.Logf("%v (%v): ok", cmd, time.Since(start))
121 } else {
122 if _, ok := err.(*exec.ExitError); ok {
123 t.Logf("%v: %v", cmd, err)
124 } else if errors.Is(err, exec.ErrWaitDelay) {
125 t.Fatalf("%v: %v", cmd, err)
126 } else {
127 t.Fatalf("%v failed to start: %v", cmd, err)
128 }
129 }
130 return string(out), err
131 }
132
133 var serializeBuild = make(chan bool, 2)
134
135 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
136 if *flagQuick {
137 t.Skip("-quick")
138 }
139 testenv.MustHaveGoBuild(t)
140
141 testprog.Lock()
142 if testprog.dir == "" {
143 dir, err := os.MkdirTemp("", "go-build")
144 if err != nil {
145 t.Fatalf("failed to create temp directory: %v", err)
146 }
147 testprog.dir = dir
148 toRemove = append(toRemove, dir)
149 }
150
151 if testprog.target == nil {
152 testprog.target = make(map[string]*buildexe)
153 }
154 name := binary
155 if len(flags) > 0 {
156 name += "_" + strings.Join(flags, "_")
157 }
158 target, ok := testprog.target[name]
159 if !ok {
160 target = &buildexe{}
161 testprog.target[name] = target
162 }
163
164 dir := testprog.dir
165
166
167
168 testprog.Unlock()
169
170 target.once.Do(func() {
171
172
173 serializeBuild <- true
174 defer func() { <-serializeBuild }()
175
176
177 target.err = errors.New("building test called t.Skip")
178
179 if asan.Enabled {
180 flags = append(flags, "-asan")
181 }
182 if msan.Enabled {
183 flags = append(flags, "-msan")
184 }
185 if race.Enabled {
186 flags = append(flags, "-race")
187 }
188
189 exe := filepath.Join(dir, name+".exe")
190
191 start := time.Now()
192 cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
193 t.Logf("running %v", cmd)
194 cmd.Dir = "testdata/" + binary
195 cmd = testenv.CleanCmdEnv(cmd)
196
197
198
199
200 edited := false
201 for i := range cmd.Env {
202 e := cmd.Env[i]
203 if _, vars, ok := strings.Cut(e, "GOEXPERIMENT="); ok {
204 cmd.Env[i] = "GOEXPERIMENT=" + vars + ",goroutineleakprofile"
205 edited, _ = true, vars
206 }
207 }
208 if !edited {
209 cmd.Env = append(cmd.Env, "GOEXPERIMENT=goroutineleakprofile")
210 }
211
212 out, err := cmd.CombinedOutput()
213 if err != nil {
214 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
215 } else {
216 t.Logf("built %v in %v", name, time.Since(start))
217 target.exe = exe
218 target.err = nil
219 }
220 })
221
222 return target.exe, target.err
223 }
224
225 func TestVDSO(t *testing.T) {
226 t.Parallel()
227 output := runTestProg(t, "testprog", "SignalInVDSO")
228 want := "success\n"
229 if output != want {
230 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
231 }
232 }
233
234 func testCrashHandler(t *testing.T, cgo bool) {
235 type crashTest struct {
236 Cgo bool
237 }
238 var output string
239 if cgo {
240 if runtime.GOOS == "freebsd" && race.Enabled {
241 t.Skipf("race + cgo freebsd not supported. See https://go.dev/issue/73788.")
242 }
243 output = runTestProg(t, "testprogcgo", "Crash")
244 } else {
245 output = runTestProg(t, "testprog", "Crash")
246 }
247 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
248 if output != want {
249 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
250 }
251 }
252
253 func TestCrashHandler(t *testing.T) {
254 testCrashHandler(t, false)
255 }
256
257 var deadlockBuildTypes = testenv.SpecialBuildTypes{
258
259 Cgo: false,
260 Asan: asan.Enabled,
261 Msan: msan.Enabled,
262 Race: race.Enabled,
263 }
264
265 func testDeadlock(t *testing.T, name string) {
266
267 testenv.MustInternalLink(t, deadlockBuildTypes)
268
269 output := runTestProg(t, "testprog", name)
270 want := "fatal error: all goroutines are asleep - deadlock!\n"
271 if !strings.HasPrefix(output, want) {
272 t.Fatalf("output does not start with %q:\n%s", want, output)
273 }
274 }
275
276 func TestSimpleDeadlock(t *testing.T) {
277 testDeadlock(t, "SimpleDeadlock")
278 }
279
280 func TestInitDeadlock(t *testing.T) {
281 testDeadlock(t, "InitDeadlock")
282 }
283
284 func TestLockedDeadlock(t *testing.T) {
285 testDeadlock(t, "LockedDeadlock")
286 }
287
288 func TestLockedDeadlock2(t *testing.T) {
289 testDeadlock(t, "LockedDeadlock2")
290 }
291
292 func TestGoexitDeadlock(t *testing.T) {
293
294 testenv.MustInternalLink(t, deadlockBuildTypes)
295
296 output := runTestProg(t, "testprog", "GoexitDeadlock")
297 want := "no goroutines (main called runtime.Goexit) - deadlock!"
298 if !strings.Contains(output, want) {
299 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
300 }
301 }
302
303 func TestStackOverflow(t *testing.T) {
304 output := runTestProg(t, "testprog", "StackOverflow")
305 want := []string{
306 "runtime: goroutine stack exceeds 1474560-byte limit\n",
307 "fatal error: stack overflow",
308
309 "runtime: sp=",
310 "stack=[",
311 }
312 if !strings.HasPrefix(output, want[0]) {
313 t.Errorf("output does not start with %q", want[0])
314 }
315 for _, s := range want[1:] {
316 if !strings.Contains(output, s) {
317 t.Errorf("output does not contain %q", s)
318 }
319 }
320 if t.Failed() {
321 t.Logf("output:\n%s", output)
322 }
323 }
324
325 func TestThreadExhaustion(t *testing.T) {
326 output := runTestProg(t, "testprog", "ThreadExhaustion")
327 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
328 if !strings.HasPrefix(output, want) {
329 t.Fatalf("output does not start with %q:\n%s", want, output)
330 }
331 }
332
333 func TestRecursivePanic(t *testing.T) {
334 output := runTestProg(t, "testprog", "RecursivePanic")
335 want := `wrap: bad
336 panic: again
337
338 `
339 if !strings.HasPrefix(output, want) {
340 t.Fatalf("output does not start with %q:\n%s", want, output)
341 }
342
343 }
344
345 func TestRecursivePanic2(t *testing.T) {
346 output := runTestProg(t, "testprog", "RecursivePanic2")
347 want := `first panic
348 second panic
349 panic: third panic
350
351 `
352 if !strings.HasPrefix(output, want) {
353 t.Fatalf("output does not start with %q:\n%s", want, output)
354 }
355
356 }
357
358 func TestRecursivePanic3(t *testing.T) {
359 output := runTestProg(t, "testprog", "RecursivePanic3")
360 want := `panic: first panic
361
362 `
363 if !strings.HasPrefix(output, want) {
364 t.Fatalf("output does not start with %q:\n%s", want, output)
365 }
366
367 }
368
369 func TestRecursivePanic4(t *testing.T) {
370 output := runTestProg(t, "testprog", "RecursivePanic4")
371 want := `panic: first panic [recovered]
372 panic: second panic
373 `
374 if !strings.HasPrefix(output, want) {
375 t.Fatalf("output does not start with %q:\n%s", want, output)
376 }
377
378 }
379
380 func TestRecursivePanic5(t *testing.T) {
381 output := runTestProg(t, "testprog", "RecursivePanic5")
382 want := `first panic
383 second panic
384 panic: third panic
385 `
386 if !strings.HasPrefix(output, want) {
387 t.Fatalf("output does not start with %q:\n%s", want, output)
388 }
389
390 }
391
392 func TestRepanickedPanic(t *testing.T) {
393 output := runTestProg(t, "testprog", "RepanickedPanic")
394 want := `panic: message [recovered, repanicked]
395 `
396 if !strings.HasPrefix(output, want) {
397 t.Fatalf("output does not start with %q:\n%s", want, output)
398 }
399 }
400
401 func TestRepanickedMiddlePanic(t *testing.T) {
402 output := runTestProg(t, "testprog", "RepanickedMiddlePanic")
403 want := `panic: inner [recovered]
404 panic: middle [recovered, repanicked]
405 panic: outer
406 `
407 if !strings.HasPrefix(output, want) {
408 t.Fatalf("output does not start with %q:\n%s", want, output)
409 }
410 }
411
412 func TestRepanickedPanicSandwich(t *testing.T) {
413 output := runTestProg(t, "testprog", "RepanickedPanicSandwich")
414 want := `panic: outer [recovered]
415 panic: inner [recovered]
416 panic: outer
417 `
418 if !strings.HasPrefix(output, want) {
419 t.Fatalf("output does not start with %q:\n%s", want, output)
420 }
421 }
422
423 func TestDoublePanicWithSameValue(t *testing.T) {
424 output := runTestProg(t, "testprog", "DoublePanicWithSameValue")
425 want := `panic: message
426 `
427 if !strings.HasPrefix(output, want) {
428 t.Fatalf("output does not start with %q:\n%s", want, output)
429 }
430 }
431
432 func TestGoexitCrash(t *testing.T) {
433
434 testenv.MustInternalLink(t, deadlockBuildTypes)
435
436 output := runTestProg(t, "testprog", "GoexitExit")
437 want := "no goroutines (main called runtime.Goexit) - deadlock!"
438 if !strings.Contains(output, want) {
439 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
440 }
441 }
442
443 func TestGoexitDefer(t *testing.T) {
444 c := make(chan struct{})
445 go func() {
446 defer func() {
447 r := recover()
448 if r != nil {
449 t.Errorf("non-nil recover during Goexit")
450 }
451 c <- struct{}{}
452 }()
453 runtime.Goexit()
454 }()
455
456 <-c
457 }
458
459 func TestGoNil(t *testing.T) {
460 output := runTestProg(t, "testprog", "GoNil")
461 want := "go of nil func value"
462 if !strings.Contains(output, want) {
463 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
464 }
465 }
466
467 func TestMainGoroutineID(t *testing.T) {
468 output := runTestProg(t, "testprog", "MainGoroutineID")
469 want := "panic: test\n\ngoroutine 1 [running]:\n"
470 if !strings.HasPrefix(output, want) {
471 t.Fatalf("output does not start with %q:\n%s", want, output)
472 }
473 }
474
475 func TestNoHelperGoroutines(t *testing.T) {
476 output := runTestProg(t, "testprog", "NoHelperGoroutines")
477 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
478 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
479 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
480 }
481 }
482
483 func TestBreakpoint(t *testing.T) {
484 output := runTestProg(t, "testprog", "Breakpoint")
485
486
487 want := "runtime.Breakpoint("
488 if !strings.Contains(output, want) {
489 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
490 }
491 }
492
493 func TestGoexitInPanic(t *testing.T) {
494
495 testenv.MustInternalLink(t, deadlockBuildTypes)
496
497
498 output := runTestProg(t, "testprog", "GoexitInPanic")
499 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
500 if !strings.HasPrefix(output, want) {
501 t.Fatalf("output does not start with %q:\n%s", want, output)
502 }
503 }
504
505
506 func TestRuntimePanicWithRuntimeError(t *testing.T) {
507 testCases := [...]func(){
508 0: func() {
509 var m map[uint64]bool
510 m[1234] = true
511 },
512 1: func() {
513 ch := make(chan struct{})
514 close(ch)
515 close(ch)
516 },
517 2: func() {
518 var ch = make(chan struct{})
519 close(ch)
520 ch <- struct{}{}
521 },
522 3: func() {
523 var s = make([]int, 2)
524 _ = s[2]
525 },
526 4: func() {
527 n := -1
528 _ = make(chan bool, n)
529 },
530 5: func() {
531 close((chan bool)(nil))
532 },
533 }
534
535 for i, fn := range testCases {
536 got := panicValue(fn)
537 if _, ok := got.(runtime.Error); !ok {
538 t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
539 }
540 }
541 }
542
543 func panicValue(fn func()) (recovered any) {
544 defer func() {
545 recovered = recover()
546 }()
547 fn()
548 return
549 }
550
551 func TestPanicAfterGoexit(t *testing.T) {
552
553 output := runTestProg(t, "testprog", "PanicAfterGoexit")
554 want := "panic: hello"
555 if !strings.HasPrefix(output, want) {
556 t.Fatalf("output does not start with %q:\n%s", want, output)
557 }
558 }
559
560 func TestRecoveredPanicAfterGoexit(t *testing.T) {
561
562 testenv.MustInternalLink(t, deadlockBuildTypes)
563
564 output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
565 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
566 if !strings.HasPrefix(output, want) {
567 t.Fatalf("output does not start with %q:\n%s", want, output)
568 }
569 }
570
571 func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
572
573 testenv.MustInternalLink(t, deadlockBuildTypes)
574
575 t.Parallel()
576 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
577 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
578 if !strings.HasPrefix(output, want) {
579 t.Fatalf("output does not start with %q:\n%s", want, output)
580 }
581 }
582
583 func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
584
585 testenv.MustInternalLink(t, deadlockBuildTypes)
586
587 t.Parallel()
588 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
589 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
590 if !strings.HasPrefix(output, want) {
591 t.Fatalf("output does not start with %q:\n%s", want, output)
592 }
593 }
594
595 func TestNetpollDeadlock(t *testing.T) {
596 t.Parallel()
597 output := runTestProg(t, "testprognet", "NetpollDeadlock")
598 want := "done\n"
599 if !strings.HasSuffix(output, want) {
600 t.Fatalf("output does not start with %q:\n%s", want, output)
601 }
602 }
603
604 func TestPanicTraceback(t *testing.T) {
605 t.Parallel()
606 output := runTestProg(t, "testprog", "PanicTraceback")
607 want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
608 if !strings.HasPrefix(output, want) {
609 t.Fatalf("output does not start with %q:\n%s", want, output)
610 }
611
612
613 fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
614 for _, fn := range fns {
615 re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
616 idx := re.FindStringIndex(output)
617 if idx == nil {
618 t.Fatalf("expected %q function in traceback:\n%s", fn, output)
619 }
620 output = output[idx[1]:]
621 }
622 }
623
624 func testPanicDeadlock(t *testing.T, name string, want string) {
625
626 output := runTestProg(t, "testprog", name)
627 if !strings.HasPrefix(output, want) {
628 t.Fatalf("output does not start with %q:\n%s", want, output)
629 }
630 }
631
632 func TestPanicDeadlockGosched(t *testing.T) {
633 testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
634 }
635
636 func TestPanicDeadlockSyscall(t *testing.T) {
637 testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
638 }
639
640 func TestPanicLoop(t *testing.T) {
641 output := runTestProg(t, "testprog", "PanicLoop")
642 if want := "panic while printing panic value"; !strings.Contains(output, want) {
643 t.Errorf("output does not contain %q:\n%s", want, output)
644 }
645 }
646
647 func TestMemPprof(t *testing.T) {
648 testenv.MustHaveGoRun(t)
649
650 exe, err := buildTestProg(t, "testprog")
651 if err != nil {
652 t.Fatal(err)
653 }
654
655 got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
656 if err != nil {
657 t.Fatalf("testprog failed: %s, output:\n%s", err, got)
658 }
659 fn := strings.TrimSpace(string(got))
660 defer os.Remove(fn)
661
662 for try := 0; try < 2; try++ {
663 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
664
665 if try == 0 {
666 cmd.Args = append(cmd.Args, exe, fn)
667 } else {
668 cmd.Args = append(cmd.Args, fn)
669 }
670 found := false
671 for i, e := range cmd.Env {
672 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
673 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
674 found = true
675 break
676 }
677 }
678 if !found {
679 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
680 }
681
682 top, err := cmd.CombinedOutput()
683 t.Logf("%s:\n%s", cmd.Args, top)
684 if err != nil {
685 t.Error(err)
686 } else if !bytes.Contains(top, []byte("MemProf")) {
687 t.Error("missing MemProf in pprof output")
688 }
689 }
690 }
691
692 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
693
694 func TestConcurrentMapWrites(t *testing.T) {
695 if !*concurrentMapTest {
696 t.Skip("skipping without -run_concurrent_map_tests")
697 }
698 if race.Enabled {
699 t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
700 }
701 testenv.MustHaveGoRun(t)
702 output := runTestProg(t, "testprog", "concurrentMapWrites")
703 want := "fatal error: concurrent map writes\n"
704
705
706 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
707 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
708 t.Fatalf("output does not start with %q:\n%s", want, output)
709 }
710 }
711 func TestConcurrentMapReadWrite(t *testing.T) {
712 if !*concurrentMapTest {
713 t.Skip("skipping without -run_concurrent_map_tests")
714 }
715 if race.Enabled {
716 t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
717 }
718 testenv.MustHaveGoRun(t)
719 output := runTestProg(t, "testprog", "concurrentMapReadWrite")
720 want := "fatal error: concurrent map read and map write\n"
721
722
723 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
724 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
725 t.Fatalf("output does not start with %q:\n%s", want, output)
726 }
727 }
728 func TestConcurrentMapIterateWrite(t *testing.T) {
729 if !*concurrentMapTest {
730 t.Skip("skipping without -run_concurrent_map_tests")
731 }
732 if race.Enabled {
733 t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
734 }
735 testenv.MustHaveGoRun(t)
736 output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
737 want := "fatal error: concurrent map iteration and map write\n"
738
739
740 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
741 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
742 t.Fatalf("output does not start with %q:\n%s", want, output)
743 }
744 }
745
746 func TestConcurrentMapWritesIssue69447(t *testing.T) {
747 testenv.MustHaveGoRun(t)
748 if race.Enabled {
749 t.Skip("skipping test: -race will catch the race, this test is for the built-in race detection")
750 }
751 exe, err := buildTestProg(t, "testprog")
752 if err != nil {
753 t.Fatal(err)
754 }
755 for i := 0; i < 200; i++ {
756 output := runBuiltTestProg(t, exe, "concurrentMapWrites")
757 if output == "" {
758
759
760
761
762
763
764 continue
765 }
766 want := "fatal error: concurrent map writes\n"
767
768
769 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
770 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
771 t.Fatalf("output does not start with %q:\n%s", want, output)
772 }
773 }
774 }
775
776 type point struct {
777 x, y *int
778 }
779
780 func (p *point) negate() {
781 *p.x = *p.x * -1
782 *p.y = *p.y * -1
783 }
784
785
786 func TestPanicInlined(t *testing.T) {
787 defer func() {
788 r := recover()
789 if r == nil {
790 t.Fatalf("recover failed")
791 }
792 buf := make([]byte, 2048)
793 n := runtime.Stack(buf, false)
794 buf = buf[:n]
795 if !bytes.Contains(buf, []byte("(*point).negate(")) {
796 t.Fatalf("expecting stack trace to contain call to (*point).negate()")
797 }
798 }()
799
800 pt := new(point)
801 pt.negate()
802 }
803
804
805
806 func TestPanicRace(t *testing.T) {
807 testenv.MustHaveGoRun(t)
808
809 exe, err := buildTestProg(t, "testprog")
810 if err != nil {
811 t.Fatal(err)
812 }
813
814
815
816
817
818 const tries = 10
819 retry:
820 for i := 0; i < tries; i++ {
821 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
822 if err == nil {
823 t.Logf("try %d: program exited successfully, should have failed", i+1)
824 continue
825 }
826
827 if i > 0 {
828 t.Logf("try %d:\n", i+1)
829 }
830 t.Logf("%s\n", got)
831
832 wants := []string{
833 "panic: crash",
834 "PanicRace",
835 "created by ",
836 }
837 for _, want := range wants {
838 if !bytes.Contains(got, []byte(want)) {
839 t.Logf("did not find expected string %q", want)
840 continue retry
841 }
842 }
843
844
845 return
846 }
847 t.Errorf("test ran %d times without producing expected output", tries)
848 }
849
850 func TestBadTraceback(t *testing.T) {
851 if asan.Enabled || msan.Enabled || race.Enabled {
852 t.Skip("skipped test: checkptr mode catches the corruption")
853 }
854 output := runTestProg(t, "testprog", "BadTraceback")
855 for _, want := range []string{
856 "unexpected return pc",
857 "called from 0xbad",
858 "00000bad",
859 "<main.badLR",
860 } {
861 if !strings.Contains(output, want) {
862 t.Errorf("output does not contain %q:\n%s", want, output)
863 }
864 }
865 }
866
867 func TestTimePprof(t *testing.T) {
868
869
870 switch runtime.GOOS {
871 case "aix", "darwin", "illumos", "openbsd", "solaris":
872 t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
873 }
874 if race.Enabled || asan.Enabled || msan.Enabled {
875 t.Skip("skipping on sanitizers because the sanitizer runtime is external code")
876 }
877
878
879
880 fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
881 fn = strings.TrimSpace(fn)
882 defer os.Remove(fn)
883
884 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
885 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
886 top, err := cmd.CombinedOutput()
887 t.Logf("%s", top)
888 if err != nil {
889 t.Error(err)
890 } else if bytes.Contains(top, []byte("ExternalCode")) {
891 t.Error("profiler refers to ExternalCode")
892 }
893 }
894
895
896 func TestAbort(t *testing.T) {
897
898 output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
899 if want := "runtime.abort"; !strings.Contains(output, want) {
900 t.Errorf("output does not contain %q:\n%s", want, output)
901 }
902 if strings.Contains(output, "BAD") {
903 t.Errorf("output contains BAD:\n%s", output)
904 }
905
906 want := "PC="
907
908 switch runtime.GOARCH {
909 case "386", "amd64":
910 switch runtime.GOOS {
911 case "plan9":
912 want = "sys: breakpoint"
913 case "windows":
914 want = "Exception 0x80000003"
915 default:
916 want = "SIGTRAP"
917 }
918 }
919 if !strings.Contains(output, want) {
920 t.Errorf("output does not contain %q:\n%s", want, output)
921 }
922 }
923
924
925
926 func init() {
927 if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
928 defer func() {
929 if r := recover(); r != nil {
930
931
932 os.Exit(0)
933 }
934 }()
935 runtime.PanicForTesting(nil, 1)
936
937 os.Exit(0)
938 }
939 if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
940 runtime.ReadMemStats(nil)
941 os.Exit(0)
942 }
943 if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
944 var f *runtime.Func
945 _ = f.Entry()
946 os.Exit(0)
947 }
948
949 }
950
951 func TestRuntimePanic(t *testing.T) {
952 cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestRuntimePanic$"))
953 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
954 out, err := cmd.CombinedOutput()
955 t.Logf("%s", out)
956 if err == nil {
957 t.Error("child process did not fail")
958 } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
959 t.Errorf("output did not contain expected string %q", want)
960 }
961 }
962
963 func TestTracebackRuntimeFunction(t *testing.T) {
964 cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeFunction$"))
965 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
966 out, err := cmd.CombinedOutput()
967 t.Logf("%s", out)
968 if err == nil {
969 t.Error("child process did not fail")
970 } else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
971 t.Errorf("output did not contain expected string %q", want)
972 }
973 }
974
975 func TestTracebackRuntimeMethod(t *testing.T) {
976 cmd := testenv.CleanCmdEnv(exec.Command(testenv.Executable(t), "-test.run=^TestTracebackRuntimeMethod$"))
977 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
978 out, err := cmd.CombinedOutput()
979 t.Logf("%s", out)
980 if err == nil {
981 t.Error("child process did not fail")
982 } else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
983 t.Errorf("output did not contain expected string %q", want)
984 }
985 }
986
987
988 func TestG0StackOverflow(t *testing.T) {
989 if runtime.GOOS == "ios" {
990 testenv.SkipFlaky(t, 62671)
991 }
992
993 if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
994 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t), "-test.run=^TestG0StackOverflow$", "-test.v"))
995 cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
996 out, err := cmd.CombinedOutput()
997 t.Logf("output:\n%s", out)
998
999 if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
1000 t.Fatalf("%s\n(exit status %v)", out, err)
1001 }
1002 if runtime.CrashStackImplemented {
1003
1004 want := "runtime.stackOverflow"
1005 if n := strings.Count(string(out), want); n < 5 {
1006 t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
1007 }
1008 return
1009 }
1010
1011 if runtime.GOOS != "windows" {
1012 if want := "PC="; !strings.Contains(string(out), want) {
1013 t.Errorf("output does not contain %q:\n%s", want, out)
1014 }
1015 }
1016 return
1017 }
1018
1019 runtime.G0StackOverflow()
1020 }
1021
1022
1023
1024 func init() {
1025 if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
1026 trace.Start(os.Stdout)
1027 trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
1028 panic("yzzyx")
1029 }
1030 }
1031
1032 func TestCrashWhileTracing(t *testing.T) {
1033 testenv.MustHaveExec(t)
1034
1035 cmd := testenv.CleanCmdEnv(testenv.Command(t, testenv.Executable(t)))
1036 cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
1037 stdOut, err := cmd.StdoutPipe()
1038 var errOut bytes.Buffer
1039 cmd.Stderr = &errOut
1040
1041 if err := cmd.Start(); err != nil {
1042 t.Fatalf("could not start subprocess: %v", err)
1043 }
1044 r, err := traceparse.NewReader(stdOut)
1045 if err != nil {
1046 t.Fatalf("could not create trace.NewReader: %v", err)
1047 }
1048 var seen bool
1049 nSync := 0
1050 i := 1
1051 loop:
1052 for ; ; i++ {
1053 ev, err := r.ReadEvent()
1054 if err != nil {
1055
1056
1057 if err != io.EOF {
1058 t.Logf("error at event %d: %v", i, err)
1059 }
1060 break loop
1061 }
1062 switch ev.Kind() {
1063 case traceparse.EventSync:
1064 nSync = ev.Sync().N
1065 case traceparse.EventLog:
1066 v := ev.Log()
1067 if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
1068
1069
1070
1071 seen = true
1072 }
1073 }
1074 }
1075 if err := cmd.Wait(); err == nil {
1076 t.Error("the process should have panicked")
1077 }
1078 if nSync <= 1 {
1079 t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
1080 }
1081 if !seen {
1082 t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
1083 }
1084 t.Logf("stderr output:\n%s", errOut.String())
1085 needle := "yzzyx\n"
1086 if n := strings.Count(errOut.String(), needle); n != 1 {
1087 t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
1088 }
1089 }
1090
1091
1092
1093 func TestDoublePanic(t *testing.T) {
1094 output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
1095 wants := []string{"panic: XXX", "panic: YYY"}
1096 for _, want := range wants {
1097 if !strings.Contains(output, want) {
1098 t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
1099 }
1100 }
1101 }
1102
1103
1104
1105 func TestPanicWhilePanicking(t *testing.T) {
1106 tests := []struct {
1107 Want string
1108 Func string
1109 }{
1110 {
1111 "panic while printing panic value: important multi-line\n\terror message",
1112 "ErrorPanic",
1113 },
1114 {
1115 "panic while printing panic value: important multi-line\n\tstringer message",
1116 "StringerPanic",
1117 },
1118 {
1119 "panic while printing panic value: type",
1120 "DoubleErrorPanic",
1121 },
1122 {
1123 "panic while printing panic value: type",
1124 "DoubleStringerPanic",
1125 },
1126 {
1127 "panic while printing panic value: type",
1128 "CircularPanic",
1129 },
1130 {
1131 "important multi-line\n\tstring message",
1132 "StringPanic",
1133 },
1134 {
1135 "nil",
1136 "NilPanic",
1137 },
1138 }
1139 for _, x := range tests {
1140 output := runTestProg(t, "testprog", x.Func)
1141 if !strings.Contains(output, x.Want) {
1142 t.Errorf("output does not contain %q:\n%s", x.Want, output)
1143 }
1144 }
1145 }
1146
1147 func TestPanicOnUnsafeSlice(t *testing.T) {
1148 output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
1149
1150
1151 want := "unsafe.Slice: ptr is nil and len is not zero"
1152 if !strings.Contains(output, want) {
1153 t.Errorf("output does not contain %q:\n%s", want, output)
1154 }
1155 }
1156
1157 func TestNetpollWaiters(t *testing.T) {
1158 t.Parallel()
1159 output := runTestProg(t, "testprognet", "NetpollWaiters")
1160 want := "OK\n"
1161 if output != want {
1162 t.Fatalf("output is not %q\n%s", want, output)
1163 }
1164 }
1165
1166 func TestFinalizerOrCleanupDeadlock(t *testing.T) {
1167 t.Parallel()
1168
1169 for _, useCleanup := range []bool{false, true} {
1170 progName := "Finalizer"
1171 want := "runtime.runFinalizers"
1172 if useCleanup {
1173 progName = "Cleanup"
1174 want = "runtime.runCleanups"
1175 }
1176 t.Run(progName, func(t *testing.T) {
1177
1178
1179 t.Run("Panic", func(t *testing.T) {
1180 t.Parallel()
1181 output := runTestProg(t, "testprog", progName+"Deadlock", "GOTRACEBACK=all", "GO_TEST_FINALIZER_DEADLOCK=panic")
1182 want := want + "()"
1183 if !strings.Contains(output, want) {
1184 t.Errorf("output does not contain %q:\n%s", want, output)
1185 }
1186 })
1187
1188
1189
1190 t.Run("Stack", func(t *testing.T) {
1191 t.Parallel()
1192 output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=stack")
1193 want := want + "()"
1194 if !strings.Contains(output, want) {
1195 t.Errorf("output does not contain %q:\n%s", want, output)
1196 }
1197 })
1198
1199
1200
1201 t.Run("PprofProto", func(t *testing.T) {
1202 t.Parallel()
1203 output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_proto")
1204
1205 p, err := profile.Parse(strings.NewReader(output))
1206 if err != nil {
1207
1208
1209 t.Logf("Output: %s", output)
1210 t.Fatalf("Error parsing proto output: %v", err)
1211 }
1212 for _, s := range p.Sample {
1213 for _, loc := range s.Location {
1214 for _, line := range loc.Line {
1215 if line.Function.Name == want {
1216
1217 return
1218 }
1219 }
1220 }
1221 }
1222 t.Errorf("Profile does not contain %q:\n%s", want, p)
1223 })
1224
1225
1226
1227 t.Run("PprofDebug1", func(t *testing.T) {
1228 t.Parallel()
1229 output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug1")
1230 want := want + "+"
1231 if !strings.Contains(output, want) {
1232 t.Errorf("output does not contain %q:\n%s", want, output)
1233 }
1234 })
1235
1236
1237
1238 t.Run("PprofDebug2", func(t *testing.T) {
1239 t.Parallel()
1240 output := runTestProg(t, "testprog", progName+"Deadlock", "GO_TEST_FINALIZER_DEADLOCK=pprof_debug2")
1241 want := want + "()"
1242 if !strings.Contains(output, want) {
1243 t.Errorf("output does not contain %q:\n%s", want, output)
1244 }
1245 })
1246 })
1247 }
1248 }
1249
1250 func TestSynctestCondSignalFromNoBubble(t *testing.T) {
1251 for _, test := range []string{
1252 "SynctestCond/signal/no_bubble",
1253 "SynctestCond/broadcast/no_bubble",
1254 "SynctestCond/signal/other_bubble",
1255 "SynctestCond/broadcast/other_bubble",
1256 } {
1257 t.Run(test, func(t *testing.T) {
1258 output := runTestProg(t, "testprog", test)
1259 want := "fatal error: semaphore wake of synctest goroutine from outside bubble"
1260 if !strings.Contains(output, want) {
1261 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
1262 }
1263 })
1264 }
1265 }
1266
View as plain text