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