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