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