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 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\n"
625
626
627 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
628 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
629 t.Fatalf("output does not start with %q:\n%s", want, output)
630 }
631 }
632 func TestConcurrentMapReadWrite(t *testing.T) {
633 if !*concurrentMapTest {
634 t.Skip("skipping without -run_concurrent_map_tests")
635 }
636 testenv.MustHaveGoRun(t)
637 output := runTestProg(t, "testprog", "concurrentMapReadWrite")
638 want := "fatal error: concurrent map read and map write\n"
639
640
641 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
642 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
643 t.Fatalf("output does not start with %q:\n%s", want, output)
644 }
645 }
646 func TestConcurrentMapIterateWrite(t *testing.T) {
647 if !*concurrentMapTest {
648 t.Skip("skipping without -run_concurrent_map_tests")
649 }
650 testenv.MustHaveGoRun(t)
651 output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
652 want := "fatal error: concurrent map iteration and map write\n"
653
654
655 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
656 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
657 t.Fatalf("output does not start with %q:\n%s", want, output)
658 }
659 }
660
661 func TestConcurrentMapWritesIssue69447(t *testing.T) {
662 testenv.MustHaveGoRun(t)
663 exe, err := buildTestProg(t, "testprog")
664 if err != nil {
665 t.Fatal(err)
666 }
667 for i := 0; i < 200; i++ {
668 output := runBuiltTestProg(t, exe, "concurrentMapWrites")
669 if output == "" {
670
671
672
673
674
675
676 continue
677 }
678 want := "fatal error: concurrent map writes\n"
679
680
681 want2 := "fatal error: small map with no empty slot (concurrent map writes?)\n"
682 if !strings.HasPrefix(output, want) && !strings.HasPrefix(output, want2) {
683 t.Fatalf("output does not start with %q:\n%s", want, output)
684 }
685 }
686 }
687
688 type point struct {
689 x, y *int
690 }
691
692 func (p *point) negate() {
693 *p.x = *p.x * -1
694 *p.y = *p.y * -1
695 }
696
697
698 func TestPanicInlined(t *testing.T) {
699 defer func() {
700 r := recover()
701 if r == nil {
702 t.Fatalf("recover failed")
703 }
704 buf := make([]byte, 2048)
705 n := runtime.Stack(buf, false)
706 buf = buf[:n]
707 if !bytes.Contains(buf, []byte("(*point).negate(")) {
708 t.Fatalf("expecting stack trace to contain call to (*point).negate()")
709 }
710 }()
711
712 pt := new(point)
713 pt.negate()
714 }
715
716
717
718 func TestPanicRace(t *testing.T) {
719 testenv.MustHaveGoRun(t)
720
721 exe, err := buildTestProg(t, "testprog")
722 if err != nil {
723 t.Fatal(err)
724 }
725
726
727
728
729
730 const tries = 10
731 retry:
732 for i := 0; i < tries; i++ {
733 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
734 if err == nil {
735 t.Logf("try %d: program exited successfully, should have failed", i+1)
736 continue
737 }
738
739 if i > 0 {
740 t.Logf("try %d:\n", i+1)
741 }
742 t.Logf("%s\n", got)
743
744 wants := []string{
745 "panic: crash",
746 "PanicRace",
747 "created by ",
748 }
749 for _, want := range wants {
750 if !bytes.Contains(got, []byte(want)) {
751 t.Logf("did not find expected string %q", want)
752 continue retry
753 }
754 }
755
756
757 return
758 }
759 t.Errorf("test ran %d times without producing expected output", tries)
760 }
761
762 func TestBadTraceback(t *testing.T) {
763 output := runTestProg(t, "testprog", "BadTraceback")
764 for _, want := range []string{
765 "unexpected return pc",
766 "called from 0xbad",
767 "00000bad",
768 "<main.badLR",
769 } {
770 if !strings.Contains(output, want) {
771 t.Errorf("output does not contain %q:\n%s", want, output)
772 }
773 }
774 }
775
776 func TestTimePprof(t *testing.T) {
777
778
779 switch runtime.GOOS {
780 case "aix", "darwin", "illumos", "openbsd", "solaris":
781 t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
782 }
783
784
785
786 fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
787 fn = strings.TrimSpace(fn)
788 defer os.Remove(fn)
789
790 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
791 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
792 top, err := cmd.CombinedOutput()
793 t.Logf("%s", top)
794 if err != nil {
795 t.Error(err)
796 } else if bytes.Contains(top, []byte("ExternalCode")) {
797 t.Error("profiler refers to ExternalCode")
798 }
799 }
800
801
802 func TestAbort(t *testing.T) {
803
804 output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
805 if want := "runtime.abort"; !strings.Contains(output, want) {
806 t.Errorf("output does not contain %q:\n%s", want, output)
807 }
808 if strings.Contains(output, "BAD") {
809 t.Errorf("output contains BAD:\n%s", output)
810 }
811
812 want := "PC="
813
814 switch runtime.GOARCH {
815 case "386", "amd64":
816 switch runtime.GOOS {
817 case "plan9":
818 want = "sys: breakpoint"
819 case "windows":
820 want = "Exception 0x80000003"
821 default:
822 want = "SIGTRAP"
823 }
824 }
825 if !strings.Contains(output, want) {
826 t.Errorf("output does not contain %q:\n%s", want, output)
827 }
828 }
829
830
831
832 func init() {
833 if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
834 defer func() {
835 if r := recover(); r != nil {
836
837
838 os.Exit(0)
839 }
840 }()
841 runtime.PanicForTesting(nil, 1)
842
843 os.Exit(0)
844 }
845 if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
846 runtime.ReadMemStats(nil)
847 os.Exit(0)
848 }
849 if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
850 var f *runtime.Func
851 _ = f.Entry()
852 os.Exit(0)
853 }
854
855 }
856
857 func TestRuntimePanic(t *testing.T) {
858 testenv.MustHaveExec(t)
859 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=^TestRuntimePanic$"))
860 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
861 out, err := cmd.CombinedOutput()
862 t.Logf("%s", out)
863 if err == nil {
864 t.Error("child process did not fail")
865 } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
866 t.Errorf("output did not contain expected string %q", want)
867 }
868 }
869
870 func TestTracebackRuntimeFunction(t *testing.T) {
871 testenv.MustHaveExec(t)
872 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeFunction"))
873 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
874 out, err := cmd.CombinedOutput()
875 t.Logf("%s", out)
876 if err == nil {
877 t.Error("child process did not fail")
878 } else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
879 t.Errorf("output did not contain expected string %q", want)
880 }
881 }
882
883 func TestTracebackRuntimeMethod(t *testing.T) {
884 testenv.MustHaveExec(t)
885 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeMethod"))
886 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
887 out, err := cmd.CombinedOutput()
888 t.Logf("%s", out)
889 if err == nil {
890 t.Error("child process did not fail")
891 } else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
892 t.Errorf("output did not contain expected string %q", want)
893 }
894 }
895
896
897 func TestG0StackOverflow(t *testing.T) {
898 testenv.MustHaveExec(t)
899
900 if runtime.GOOS == "ios" {
901 testenv.SkipFlaky(t, 62671)
902 }
903
904 if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
905 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v"))
906 cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
907 out, err := cmd.CombinedOutput()
908 t.Logf("output:\n%s", out)
909
910 if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
911 t.Fatalf("%s\n(exit status %v)", out, err)
912 }
913 if runtime.CrashStackImplemented {
914
915 want := "runtime.stackOverflow"
916 if n := strings.Count(string(out), want); n < 5 {
917 t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
918 }
919 return
920 }
921
922 if runtime.GOOS != "windows" {
923 if want := "PC="; !strings.Contains(string(out), want) {
924 t.Errorf("output does not contain %q:\n%s", want, out)
925 }
926 }
927 return
928 }
929
930 runtime.G0StackOverflow()
931 }
932
933
934
935 func init() {
936 if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
937 trace.Start(os.Stdout)
938 trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
939 panic("yzzyx")
940 }
941 }
942
943 func TestCrashWhileTracing(t *testing.T) {
944 testenv.MustHaveExec(t)
945
946 cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0]))
947 cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
948 stdOut, err := cmd.StdoutPipe()
949 var errOut bytes.Buffer
950 cmd.Stderr = &errOut
951
952 if err := cmd.Start(); err != nil {
953 t.Fatalf("could not start subprocess: %v", err)
954 }
955 r, err := traceparse.NewReader(stdOut)
956 if err != nil {
957 t.Fatalf("could not create trace.NewReader: %v", err)
958 }
959 var seen, seenSync bool
960 i := 1
961 loop:
962 for ; ; i++ {
963 ev, err := r.ReadEvent()
964 if err != nil {
965
966
967 if err != io.EOF {
968 t.Logf("error at event %d: %v", i, err)
969 }
970 break loop
971 }
972 switch ev.Kind() {
973 case traceparse.EventSync:
974 seenSync = true
975 case traceparse.EventLog:
976 v := ev.Log()
977 if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
978
979
980
981 seen = true
982 }
983 }
984 }
985 if err := cmd.Wait(); err == nil {
986 t.Error("the process should have panicked")
987 }
988 if !seenSync {
989 t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
990 }
991 if !seen {
992 t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
993 }
994 t.Logf("stderr output:\n%s", errOut.String())
995 needle := "yzzyx\n"
996 if n := strings.Count(errOut.String(), needle); n != 1 {
997 t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
998 }
999 }
1000
1001
1002
1003 func TestDoublePanic(t *testing.T) {
1004 output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
1005 wants := []string{"panic: XXX", "panic: YYY"}
1006 for _, want := range wants {
1007 if !strings.Contains(output, want) {
1008 t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
1009 }
1010 }
1011 }
1012
1013
1014
1015 func TestPanicWhilePanicking(t *testing.T) {
1016 tests := []struct {
1017 Want string
1018 Func string
1019 }{
1020 {
1021 "panic while printing panic value: important multi-line\n\terror message",
1022 "ErrorPanic",
1023 },
1024 {
1025 "panic while printing panic value: important multi-line\n\tstringer message",
1026 "StringerPanic",
1027 },
1028 {
1029 "panic while printing panic value: type",
1030 "DoubleErrorPanic",
1031 },
1032 {
1033 "panic while printing panic value: type",
1034 "DoubleStringerPanic",
1035 },
1036 {
1037 "panic while printing panic value: type",
1038 "CircularPanic",
1039 },
1040 {
1041 "important multi-line\n\tstring message",
1042 "StringPanic",
1043 },
1044 {
1045 "nil",
1046 "NilPanic",
1047 },
1048 }
1049 for _, x := range tests {
1050 output := runTestProg(t, "testprog", x.Func)
1051 if !strings.Contains(output, x.Want) {
1052 t.Errorf("output does not contain %q:\n%s", x.Want, output)
1053 }
1054 }
1055 }
1056
1057 func TestPanicOnUnsafeSlice(t *testing.T) {
1058 output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
1059 want := "panic: runtime error: unsafe.Slice: ptr is nil and len is not zero"
1060 if !strings.Contains(output, want) {
1061 t.Errorf("output does not contain %q:\n%s", want, output)
1062 }
1063 }
1064
1065 func TestNetpollWaiters(t *testing.T) {
1066 t.Parallel()
1067 output := runTestProg(t, "testprognet", "NetpollWaiters")
1068 want := "OK\n"
1069 if output != want {
1070 t.Fatalf("output is not %q\n%s", want, output)
1071 }
1072 }
1073
View as plain text