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