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