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