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