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