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