Source file
src/runtime/crash_test.go
Documentation: runtime
1
2
3
4
5 package runtime_test
6
7 import (
8 "bytes"
9 "flag"
10 "fmt"
11 "internal/testenv"
12 "os"
13 "os/exec"
14 "path/filepath"
15 "regexp"
16 "runtime"
17 "strconv"
18 "strings"
19 "sync"
20 "testing"
21 "time"
22 )
23
24 var toRemove []string
25
26 func TestMain(m *testing.M) {
27 status := m.Run()
28 for _, file := range toRemove {
29 os.RemoveAll(file)
30 }
31 os.Exit(status)
32 }
33
34 var testprog struct {
35 sync.Mutex
36 dir string
37 target map[string]buildexe
38 }
39
40 type buildexe struct {
41 exe string
42 err error
43 }
44
45 func runTestProg(t *testing.T, binary, name string, env ...string) string {
46 if *flagQuick {
47 t.Skip("-quick")
48 }
49
50 testenv.MustHaveGoBuild(t)
51
52 exe, err := buildTestProg(t, binary)
53 if err != nil {
54 t.Fatal(err)
55 }
56
57 return runBuiltTestProg(t, exe, name, env...)
58 }
59
60 func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
61 if *flagQuick {
62 t.Skip("-quick")
63 }
64
65 testenv.MustHaveGoBuild(t)
66
67 cmd := testenv.CleanCmdEnv(exec.Command(exe, name))
68 cmd.Env = append(cmd.Env, env...)
69 if testing.Short() {
70 cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
71 }
72 var b bytes.Buffer
73 cmd.Stdout = &b
74 cmd.Stderr = &b
75 if err := cmd.Start(); err != nil {
76 t.Fatalf("starting %s %s: %v", exe, name, err)
77 }
78
79
80
81 p := cmd.Process
82 done := make(chan bool)
83 go func() {
84 scale := 1
85
86
87 if runtime.GOARCH == "arm" || runtime.GOOS == "windows" {
88 scale = 2
89 }
90 if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" {
91 if sc, err := strconv.Atoi(s); err == nil {
92 scale = sc
93 }
94 }
95
96 select {
97 case <-done:
98 case <-time.After(time.Duration(scale) * time.Minute):
99 p.Signal(sigquit)
100 }
101 }()
102
103 if err := cmd.Wait(); err != nil {
104 t.Logf("%s %s exit status: %v", exe, name, err)
105 }
106 close(done)
107
108 return b.String()
109 }
110
111 func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
112 if *flagQuick {
113 t.Skip("-quick")
114 }
115
116 testprog.Lock()
117 defer testprog.Unlock()
118 if testprog.dir == "" {
119 dir, err := os.MkdirTemp("", "go-build")
120 if err != nil {
121 t.Fatalf("failed to create temp directory: %v", err)
122 }
123 testprog.dir = dir
124 toRemove = append(toRemove, dir)
125 }
126
127 if testprog.target == nil {
128 testprog.target = make(map[string]buildexe)
129 }
130 name := binary
131 if len(flags) > 0 {
132 name += "_" + strings.Join(flags, "_")
133 }
134 target, ok := testprog.target[name]
135 if ok {
136 return target.exe, target.err
137 }
138
139 exe := filepath.Join(testprog.dir, name+".exe")
140 cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
141 cmd.Dir = "testdata/" + binary
142 out, err := testenv.CleanCmdEnv(cmd).CombinedOutput()
143 if err != nil {
144 target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
145 testprog.target[name] = target
146 return "", target.err
147 }
148 target.exe = exe
149 testprog.target[name] = target
150 return exe, nil
151 }
152
153 func TestVDSO(t *testing.T) {
154 t.Parallel()
155 output := runTestProg(t, "testprog", "SignalInVDSO")
156 want := "success\n"
157 if output != want {
158 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
159 }
160 }
161
162 func testCrashHandler(t *testing.T, cgo bool) {
163 type crashTest struct {
164 Cgo bool
165 }
166 var output string
167 if cgo {
168 output = runTestProg(t, "testprogcgo", "Crash")
169 } else {
170 output = runTestProg(t, "testprog", "Crash")
171 }
172 want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
173 if output != want {
174 t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
175 }
176 }
177
178 func TestCrashHandler(t *testing.T) {
179 testCrashHandler(t, false)
180 }
181
182 func testDeadlock(t *testing.T, name string) {
183
184 testenv.MustInternalLink(t)
185
186 output := runTestProg(t, "testprog", name)
187 want := "fatal error: all goroutines are asleep - deadlock!\n"
188 if !strings.HasPrefix(output, want) {
189 t.Fatalf("output does not start with %q:\n%s", want, output)
190 }
191 }
192
193 func TestSimpleDeadlock(t *testing.T) {
194 testDeadlock(t, "SimpleDeadlock")
195 }
196
197 func TestInitDeadlock(t *testing.T) {
198 testDeadlock(t, "InitDeadlock")
199 }
200
201 func TestLockedDeadlock(t *testing.T) {
202 testDeadlock(t, "LockedDeadlock")
203 }
204
205 func TestLockedDeadlock2(t *testing.T) {
206 testDeadlock(t, "LockedDeadlock2")
207 }
208
209 func TestGoexitDeadlock(t *testing.T) {
210
211 testenv.MustInternalLink(t)
212
213 output := runTestProg(t, "testprog", "GoexitDeadlock")
214 want := "no goroutines (main called runtime.Goexit) - deadlock!"
215 if !strings.Contains(output, want) {
216 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
217 }
218 }
219
220 func TestStackOverflow(t *testing.T) {
221 output := runTestProg(t, "testprog", "StackOverflow")
222 want := []string{
223 "runtime: goroutine stack exceeds 1474560-byte limit\n",
224 "fatal error: stack overflow",
225
226 "runtime: sp=",
227 "stack=[",
228 }
229 if !strings.HasPrefix(output, want[0]) {
230 t.Errorf("output does not start with %q", want[0])
231 }
232 for _, s := range want[1:] {
233 if !strings.Contains(output, s) {
234 t.Errorf("output does not contain %q", s)
235 }
236 }
237 if t.Failed() {
238 t.Logf("output:\n%s", output)
239 }
240 }
241
242 func TestThreadExhaustion(t *testing.T) {
243 output := runTestProg(t, "testprog", "ThreadExhaustion")
244 want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
245 if !strings.HasPrefix(output, want) {
246 t.Fatalf("output does not start with %q:\n%s", want, output)
247 }
248 }
249
250 func TestRecursivePanic(t *testing.T) {
251 output := runTestProg(t, "testprog", "RecursivePanic")
252 want := `wrap: bad
253 panic: again
254
255 `
256 if !strings.HasPrefix(output, want) {
257 t.Fatalf("output does not start with %q:\n%s", want, output)
258 }
259
260 }
261
262 func TestRecursivePanic2(t *testing.T) {
263 output := runTestProg(t, "testprog", "RecursivePanic2")
264 want := `first panic
265 second panic
266 panic: third panic
267
268 `
269 if !strings.HasPrefix(output, want) {
270 t.Fatalf("output does not start with %q:\n%s", want, output)
271 }
272
273 }
274
275 func TestRecursivePanic3(t *testing.T) {
276 output := runTestProg(t, "testprog", "RecursivePanic3")
277 want := `panic: first panic
278
279 `
280 if !strings.HasPrefix(output, want) {
281 t.Fatalf("output does not start with %q:\n%s", want, output)
282 }
283
284 }
285
286 func TestRecursivePanic4(t *testing.T) {
287 output := runTestProg(t, "testprog", "RecursivePanic4")
288 want := `panic: first panic [recovered]
289 panic: second panic
290 `
291 if !strings.HasPrefix(output, want) {
292 t.Fatalf("output does not start with %q:\n%s", want, output)
293 }
294
295 }
296
297 func TestRecursivePanic5(t *testing.T) {
298 output := runTestProg(t, "testprog", "RecursivePanic5")
299 want := `first panic
300 second panic
301 panic: third panic
302 `
303 if !strings.HasPrefix(output, want) {
304 t.Fatalf("output does not start with %q:\n%s", want, output)
305 }
306
307 }
308
309 func TestGoexitCrash(t *testing.T) {
310
311 testenv.MustInternalLink(t)
312
313 output := runTestProg(t, "testprog", "GoexitExit")
314 want := "no goroutines (main called runtime.Goexit) - deadlock!"
315 if !strings.Contains(output, want) {
316 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
317 }
318 }
319
320 func TestGoexitDefer(t *testing.T) {
321 c := make(chan struct{})
322 go func() {
323 defer func() {
324 r := recover()
325 if r != nil {
326 t.Errorf("non-nil recover during Goexit")
327 }
328 c <- struct{}{}
329 }()
330 runtime.Goexit()
331 }()
332
333 <-c
334 }
335
336 func TestGoNil(t *testing.T) {
337 output := runTestProg(t, "testprog", "GoNil")
338 want := "go of nil func value"
339 if !strings.Contains(output, want) {
340 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
341 }
342 }
343
344 func TestMainGoroutineID(t *testing.T) {
345 output := runTestProg(t, "testprog", "MainGoroutineID")
346 want := "panic: test\n\ngoroutine 1 [running]:\n"
347 if !strings.HasPrefix(output, want) {
348 t.Fatalf("output does not start with %q:\n%s", want, output)
349 }
350 }
351
352 func TestNoHelperGoroutines(t *testing.T) {
353 output := runTestProg(t, "testprog", "NoHelperGoroutines")
354 matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
355 if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
356 t.Fatalf("want to see only goroutine 1, see:\n%s", output)
357 }
358 }
359
360 func TestBreakpoint(t *testing.T) {
361 output := runTestProg(t, "testprog", "Breakpoint")
362
363
364 want := "runtime.Breakpoint("
365 if !strings.Contains(output, want) {
366 t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
367 }
368 }
369
370 func TestGoexitInPanic(t *testing.T) {
371
372 testenv.MustInternalLink(t)
373
374
375 output := runTestProg(t, "testprog", "GoexitInPanic")
376 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
377 if !strings.HasPrefix(output, want) {
378 t.Fatalf("output does not start with %q:\n%s", want, output)
379 }
380 }
381
382
383 func TestRuntimePanicWithRuntimeError(t *testing.T) {
384 testCases := [...]func(){
385 0: func() {
386 var m map[uint64]bool
387 m[1234] = true
388 },
389 1: func() {
390 ch := make(chan struct{})
391 close(ch)
392 close(ch)
393 },
394 2: func() {
395 var ch = make(chan struct{})
396 close(ch)
397 ch <- struct{}{}
398 },
399 3: func() {
400 var s = make([]int, 2)
401 _ = s[2]
402 },
403 4: func() {
404 n := -1
405 _ = make(chan bool, n)
406 },
407 5: func() {
408 close((chan bool)(nil))
409 },
410 }
411
412 for i, fn := range testCases {
413 got := panicValue(fn)
414 if _, ok := got.(runtime.Error); !ok {
415 t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
416 }
417 }
418 }
419
420 func panicValue(fn func()) (recovered interface{}) {
421 defer func() {
422 recovered = recover()
423 }()
424 fn()
425 return
426 }
427
428 func TestPanicAfterGoexit(t *testing.T) {
429
430 output := runTestProg(t, "testprog", "PanicAfterGoexit")
431 want := "panic: hello"
432 if !strings.HasPrefix(output, want) {
433 t.Fatalf("output does not start with %q:\n%s", want, output)
434 }
435 }
436
437 func TestRecoveredPanicAfterGoexit(t *testing.T) {
438
439 testenv.MustInternalLink(t)
440
441 output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
442 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
443 if !strings.HasPrefix(output, want) {
444 t.Fatalf("output does not start with %q:\n%s", want, output)
445 }
446 }
447
448 func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
449
450 testenv.MustInternalLink(t)
451
452 t.Parallel()
453 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
454 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
455 if !strings.HasPrefix(output, want) {
456 t.Fatalf("output does not start with %q:\n%s", want, output)
457 }
458 }
459
460 func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
461
462 testenv.MustInternalLink(t)
463
464 t.Parallel()
465 output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
466 want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
467 if !strings.HasPrefix(output, want) {
468 t.Fatalf("output does not start with %q:\n%s", want, output)
469 }
470 }
471
472 func TestNetpollDeadlock(t *testing.T) {
473 t.Parallel()
474 output := runTestProg(t, "testprognet", "NetpollDeadlock")
475 want := "done\n"
476 if !strings.HasSuffix(output, want) {
477 t.Fatalf("output does not start with %q:\n%s", want, output)
478 }
479 }
480
481 func TestPanicTraceback(t *testing.T) {
482 t.Parallel()
483 output := runTestProg(t, "testprog", "PanicTraceback")
484 want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
485 if !strings.HasPrefix(output, want) {
486 t.Fatalf("output does not start with %q:\n%s", want, output)
487 }
488
489
490 fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
491 for _, fn := range fns {
492 re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
493 idx := re.FindStringIndex(output)
494 if idx == nil {
495 t.Fatalf("expected %q function in traceback:\n%s", fn, output)
496 }
497 output = output[idx[1]:]
498 }
499 }
500
501 func testPanicDeadlock(t *testing.T, name string, want string) {
502
503 output := runTestProg(t, "testprog", name)
504 if !strings.HasPrefix(output, want) {
505 t.Fatalf("output does not start with %q:\n%s", want, output)
506 }
507 }
508
509 func TestPanicDeadlockGosched(t *testing.T) {
510 testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
511 }
512
513 func TestPanicDeadlockSyscall(t *testing.T) {
514 testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
515 }
516
517 func TestPanicLoop(t *testing.T) {
518 output := runTestProg(t, "testprog", "PanicLoop")
519 if want := "panic while printing panic value"; !strings.Contains(output, want) {
520 t.Errorf("output does not contain %q:\n%s", want, output)
521 }
522 }
523
524 func TestMemPprof(t *testing.T) {
525 testenv.MustHaveGoRun(t)
526
527 exe, err := buildTestProg(t, "testprog")
528 if err != nil {
529 t.Fatal(err)
530 }
531
532 got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
533 if err != nil {
534 t.Fatal(err)
535 }
536 fn := strings.TrimSpace(string(got))
537 defer os.Remove(fn)
538
539 for try := 0; try < 2; try++ {
540 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
541
542 if try == 0 {
543 cmd.Args = append(cmd.Args, exe, fn)
544 } else {
545 cmd.Args = append(cmd.Args, fn)
546 }
547 found := false
548 for i, e := range cmd.Env {
549 if strings.HasPrefix(e, "PPROF_TMPDIR=") {
550 cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
551 found = true
552 break
553 }
554 }
555 if !found {
556 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
557 }
558
559 top, err := cmd.CombinedOutput()
560 t.Logf("%s:\n%s", cmd.Args, top)
561 if err != nil {
562 t.Error(err)
563 } else if !bytes.Contains(top, []byte("MemProf")) {
564 t.Error("missing MemProf in pprof output")
565 }
566 }
567 }
568
569 var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
570
571 func TestConcurrentMapWrites(t *testing.T) {
572 if !*concurrentMapTest {
573 t.Skip("skipping without -run_concurrent_map_tests")
574 }
575 testenv.MustHaveGoRun(t)
576 output := runTestProg(t, "testprog", "concurrentMapWrites")
577 want := "fatal error: concurrent map writes"
578 if !strings.HasPrefix(output, want) {
579 t.Fatalf("output does not start with %q:\n%s", want, output)
580 }
581 }
582 func TestConcurrentMapReadWrite(t *testing.T) {
583 if !*concurrentMapTest {
584 t.Skip("skipping without -run_concurrent_map_tests")
585 }
586 testenv.MustHaveGoRun(t)
587 output := runTestProg(t, "testprog", "concurrentMapReadWrite")
588 want := "fatal error: concurrent map read and map write"
589 if !strings.HasPrefix(output, want) {
590 t.Fatalf("output does not start with %q:\n%s", want, output)
591 }
592 }
593 func TestConcurrentMapIterateWrite(t *testing.T) {
594 if !*concurrentMapTest {
595 t.Skip("skipping without -run_concurrent_map_tests")
596 }
597 testenv.MustHaveGoRun(t)
598 output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
599 want := "fatal error: concurrent map iteration and map write"
600 if !strings.HasPrefix(output, want) {
601 t.Fatalf("output does not start with %q:\n%s", want, output)
602 }
603 }
604
605 type point struct {
606 x, y *int
607 }
608
609 func (p *point) negate() {
610 *p.x = *p.x * -1
611 *p.y = *p.y * -1
612 }
613
614
615 func TestPanicInlined(t *testing.T) {
616 defer func() {
617 r := recover()
618 if r == nil {
619 t.Fatalf("recover failed")
620 }
621 buf := make([]byte, 2048)
622 n := runtime.Stack(buf, false)
623 buf = buf[:n]
624 if !bytes.Contains(buf, []byte("(*point).negate(")) {
625 t.Fatalf("expecting stack trace to contain call to (*point).negate()")
626 }
627 }()
628
629 pt := new(point)
630 pt.negate()
631 }
632
633
634
635 func TestPanicRace(t *testing.T) {
636 testenv.MustHaveGoRun(t)
637
638 exe, err := buildTestProg(t, "testprog")
639 if err != nil {
640 t.Fatal(err)
641 }
642
643
644
645
646
647 const tries = 10
648 retry:
649 for i := 0; i < tries; i++ {
650 got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
651 if err == nil {
652 t.Logf("try %d: program exited successfully, should have failed", i+1)
653 continue
654 }
655
656 if i > 0 {
657 t.Logf("try %d:\n", i+1)
658 }
659 t.Logf("%s\n", got)
660
661 wants := []string{
662 "panic: crash",
663 "PanicRace",
664 "created by ",
665 }
666 for _, want := range wants {
667 if !bytes.Contains(got, []byte(want)) {
668 t.Logf("did not find expected string %q", want)
669 continue retry
670 }
671 }
672
673
674 return
675 }
676 t.Errorf("test ran %d times without producing expected output", tries)
677 }
678
679 func TestBadTraceback(t *testing.T) {
680 output := runTestProg(t, "testprog", "BadTraceback")
681 for _, want := range []string{
682 "runtime: unexpected return pc",
683 "called from 0xbad",
684 "00000bad",
685 "<main.badLR",
686 } {
687 if !strings.Contains(output, want) {
688 t.Errorf("output does not contain %q:\n%s", want, output)
689 }
690 }
691 }
692
693 func TestTimePprof(t *testing.T) {
694
695
696 fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
697 fn = strings.TrimSpace(fn)
698 defer os.Remove(fn)
699
700 cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
701 cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
702 top, err := cmd.CombinedOutput()
703 t.Logf("%s", top)
704 if err != nil {
705 t.Error(err)
706 } else if bytes.Contains(top, []byte("ExternalCode")) {
707 t.Error("profiler refers to ExternalCode")
708 }
709 }
710
711
712 func TestAbort(t *testing.T) {
713
714 output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
715 if want := "runtime.abort"; !strings.Contains(output, want) {
716 t.Errorf("output does not contain %q:\n%s", want, output)
717 }
718 if strings.Contains(output, "BAD") {
719 t.Errorf("output contains BAD:\n%s", output)
720 }
721
722 want := "PC="
723
724 switch runtime.GOARCH {
725 case "386", "amd64":
726 switch runtime.GOOS {
727 case "plan9":
728 want = "sys: breakpoint"
729 case "windows":
730 want = "Exception 0x80000003"
731 default:
732 want = "SIGTRAP"
733 }
734 }
735 if !strings.Contains(output, want) {
736 t.Errorf("output does not contain %q:\n%s", want, output)
737 }
738 }
739
740
741
742 func init() {
743 if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
744 defer func() {
745 if r := recover(); r != nil {
746
747
748 os.Exit(0)
749 }
750 }()
751 runtime.PanicForTesting(nil, 1)
752
753 os.Exit(0)
754 }
755 }
756
757 func TestRuntimePanic(t *testing.T) {
758 testenv.MustHaveExec(t)
759 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestRuntimePanic"))
760 cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
761 out, err := cmd.CombinedOutput()
762 t.Logf("%s", out)
763 if err == nil {
764 t.Error("child process did not fail")
765 } else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
766 t.Errorf("output did not contain expected string %q", want)
767 }
768 }
769
770
771 func TestG0StackOverflow(t *testing.T) {
772 testenv.MustHaveExec(t)
773
774 switch runtime.GOOS {
775 case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android":
776 t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)")
777 }
778
779 if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
780 cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v"))
781 cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
782 out, err := cmd.CombinedOutput()
783
784 if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
785 t.Fatalf("%s\n(exit status %v)", out, err)
786 }
787
788 if runtime.GOOS != "windows" {
789 if want := "PC="; !strings.Contains(string(out), want) {
790 t.Errorf("output does not contain %q:\n%s", want, out)
791 }
792 }
793 return
794 }
795
796 runtime.G0StackOverflow()
797 }
798
799
800
801 func TestDoublePanic(t *testing.T) {
802 output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
803 wants := []string{"panic: XXX", "panic: YYY"}
804 for _, want := range wants {
805 if !strings.Contains(output, want) {
806 t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
807 }
808 }
809 }
810
View as plain text