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