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