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