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