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	"context"
10	"errors"
11	"flag"
12	"fmt"
13	"internal/testenv"
14	traceparse "internal/trace"
15	"io"
16	"log"
17	"os"
18	"os/exec"
19	"path/filepath"
20	"regexp"
21	"runtime"
22	"runtime/trace"
23	"strings"
24	"sync"
25	"testing"
26	"time"
27)
28
29var toRemove []string
30
31const entrypointVar = "RUNTIME_TEST_ENTRYPOINT"
32
33func TestMain(m *testing.M) {
34	switch entrypoint := os.Getenv(entrypointVar); entrypoint {
35	case "crash":
36		crash()
37		panic("unreachable")
38	default:
39		log.Fatalf("invalid %s: %q", entrypointVar, entrypoint)
40	case "":
41		// fall through to normal behavior
42	}
43
44	_, coreErrBefore := os.Stat("core")
45
46	status := m.Run()
47	for _, file := range toRemove {
48		os.RemoveAll(file)
49	}
50
51	_, coreErrAfter := os.Stat("core")
52	if coreErrBefore != nil && coreErrAfter == nil {
53		fmt.Fprintln(os.Stderr, "runtime.test: some test left a core file behind")
54		if status == 0 {
55			status = 1
56		}
57	}
58
59	os.Exit(status)
60}
61
62var testprog struct {
63	sync.Mutex
64	dir    string
65	target map[string]*buildexe
66}
67
68type buildexe struct {
69	once sync.Once
70	exe  string
71	err  error
72}
73
74func runTestProg(t *testing.T, binary, name string, env ...string) string {
75	if *flagQuick {
76		t.Skip("-quick")
77	}
78
79	testenv.MustHaveGoBuild(t)
80	t.Helper()
81
82	exe, err := buildTestProg(t, binary)
83	if err != nil {
84		t.Fatal(err)
85	}
86
87	return runBuiltTestProg(t, exe, name, env...)
88}
89
90func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string {
91	t.Helper()
92
93	if *flagQuick {
94		t.Skip("-quick")
95	}
96
97	start := time.Now()
98
99	cmd := testenv.CleanCmdEnv(testenv.Command(t, exe, name))
100	cmd.Env = append(cmd.Env, env...)
101	if testing.Short() {
102		cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1")
103	}
104	out, err := cmd.CombinedOutput()
105	if err == nil {
106		t.Logf("%v (%v): ok", cmd, time.Since(start))
107	} else {
108		if _, ok := err.(*exec.ExitError); ok {
109			t.Logf("%v: %v", cmd, err)
110		} else if errors.Is(err, exec.ErrWaitDelay) {
111			t.Fatalf("%v: %v", cmd, err)
112		} else {
113			t.Fatalf("%v failed to start: %v", cmd, err)
114		}
115	}
116	return string(out)
117}
118
119var serializeBuild = make(chan bool, 2)
120
121func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) {
122	if *flagQuick {
123		t.Skip("-quick")
124	}
125	testenv.MustHaveGoBuild(t)
126
127	testprog.Lock()
128	if testprog.dir == "" {
129		dir, err := os.MkdirTemp("", "go-build")
130		if err != nil {
131			t.Fatalf("failed to create temp directory: %v", err)
132		}
133		testprog.dir = dir
134		toRemove = append(toRemove, dir)
135	}
136
137	if testprog.target == nil {
138		testprog.target = make(map[string]*buildexe)
139	}
140	name := binary
141	if len(flags) > 0 {
142		name += "_" + strings.Join(flags, "_")
143	}
144	target, ok := testprog.target[name]
145	if !ok {
146		target = &buildexe{}
147		testprog.target[name] = target
148	}
149
150	dir := testprog.dir
151
152	// Unlock testprog while actually building, so that other
153	// tests can look up executables that were already built.
154	testprog.Unlock()
155
156	target.once.Do(func() {
157		// Only do two "go build"'s at a time,
158		// to keep load from getting too high.
159		serializeBuild <- true
160		defer func() { <-serializeBuild }()
161
162		// Don't get confused if testenv.GoToolPath calls t.Skip.
163		target.err = errors.New("building test called t.Skip")
164
165		exe := filepath.Join(dir, name+".exe")
166
167		start := time.Now()
168		cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...)
169		t.Logf("running %v", cmd)
170		cmd.Dir = "testdata/" + binary
171		cmd = testenv.CleanCmdEnv(cmd)
172
173		// Add the rangefunc GOEXPERIMENT unconditionally since some tests depend on it.
174		// TODO(61405): Remove this once it's enabled by default.
175		edited := false
176		for i := range cmd.Env {
177			e := cmd.Env[i]
178			if _, vars, ok := strings.Cut(e, "GOEXPERIMENT="); ok {
179				cmd.Env[i] = "GOEXPERIMENT=" + vars + ",rangefunc"
180				edited = true
181			}
182		}
183		if !edited {
184			cmd.Env = append(cmd.Env, "GOEXPERIMENT=rangefunc")
185		}
186
187		out, err := cmd.CombinedOutput()
188		if err != nil {
189			target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out)
190		} else {
191			t.Logf("built %v in %v", name, time.Since(start))
192			target.exe = exe
193			target.err = nil
194		}
195	})
196
197	return target.exe, target.err
198}
199
200func TestVDSO(t *testing.T) {
201	t.Parallel()
202	output := runTestProg(t, "testprog", "SignalInVDSO")
203	want := "success\n"
204	if output != want {
205		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
206	}
207}
208
209func testCrashHandler(t *testing.T, cgo bool) {
210	type crashTest struct {
211		Cgo bool
212	}
213	var output string
214	if cgo {
215		output = runTestProg(t, "testprogcgo", "Crash")
216	} else {
217		output = runTestProg(t, "testprog", "Crash")
218	}
219	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n"
220	if output != want {
221		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want)
222	}
223}
224
225func TestCrashHandler(t *testing.T) {
226	testCrashHandler(t, false)
227}
228
229func testDeadlock(t *testing.T, name string) {
230	// External linking brings in cgo, causing deadlock detection not working.
231	testenv.MustInternalLink(t, false)
232
233	output := runTestProg(t, "testprog", name)
234	want := "fatal error: all goroutines are asleep - deadlock!\n"
235	if !strings.HasPrefix(output, want) {
236		t.Fatalf("output does not start with %q:\n%s", want, output)
237	}
238}
239
240func TestSimpleDeadlock(t *testing.T) {
241	testDeadlock(t, "SimpleDeadlock")
242}
243
244func TestInitDeadlock(t *testing.T) {
245	testDeadlock(t, "InitDeadlock")
246}
247
248func TestLockedDeadlock(t *testing.T) {
249	testDeadlock(t, "LockedDeadlock")
250}
251
252func TestLockedDeadlock2(t *testing.T) {
253	testDeadlock(t, "LockedDeadlock2")
254}
255
256func TestGoexitDeadlock(t *testing.T) {
257	// External linking brings in cgo, causing deadlock detection not working.
258	testenv.MustInternalLink(t, false)
259
260	output := runTestProg(t, "testprog", "GoexitDeadlock")
261	want := "no goroutines (main called runtime.Goexit) - deadlock!"
262	if !strings.Contains(output, want) {
263		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
264	}
265}
266
267func TestStackOverflow(t *testing.T) {
268	output := runTestProg(t, "testprog", "StackOverflow")
269	want := []string{
270		"runtime: goroutine stack exceeds 1474560-byte limit\n",
271		"fatal error: stack overflow",
272		// information about the current SP and stack bounds
273		"runtime: sp=",
274		"stack=[",
275	}
276	if !strings.HasPrefix(output, want[0]) {
277		t.Errorf("output does not start with %q", want[0])
278	}
279	for _, s := range want[1:] {
280		if !strings.Contains(output, s) {
281			t.Errorf("output does not contain %q", s)
282		}
283	}
284	if t.Failed() {
285		t.Logf("output:\n%s", output)
286	}
287}
288
289func TestThreadExhaustion(t *testing.T) {
290	output := runTestProg(t, "testprog", "ThreadExhaustion")
291	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
292	if !strings.HasPrefix(output, want) {
293		t.Fatalf("output does not start with %q:\n%s", want, output)
294	}
295}
296
297func TestRecursivePanic(t *testing.T) {
298	output := runTestProg(t, "testprog", "RecursivePanic")
299	want := `wrap: bad
300panic: again
301
302`
303	if !strings.HasPrefix(output, want) {
304		t.Fatalf("output does not start with %q:\n%s", want, output)
305	}
306
307}
308
309func TestRecursivePanic2(t *testing.T) {
310	output := runTestProg(t, "testprog", "RecursivePanic2")
311	want := `first panic
312second panic
313panic: third panic
314
315`
316	if !strings.HasPrefix(output, want) {
317		t.Fatalf("output does not start with %q:\n%s", want, output)
318	}
319
320}
321
322func TestRecursivePanic3(t *testing.T) {
323	output := runTestProg(t, "testprog", "RecursivePanic3")
324	want := `panic: first panic
325
326`
327	if !strings.HasPrefix(output, want) {
328		t.Fatalf("output does not start with %q:\n%s", want, output)
329	}
330
331}
332
333func TestRecursivePanic4(t *testing.T) {
334	output := runTestProg(t, "testprog", "RecursivePanic4")
335	want := `panic: first panic [recovered]
336	panic: second panic
337`
338	if !strings.HasPrefix(output, want) {
339		t.Fatalf("output does not start with %q:\n%s", want, output)
340	}
341
342}
343
344func TestRecursivePanic5(t *testing.T) {
345	output := runTestProg(t, "testprog", "RecursivePanic5")
346	want := `first panic
347second panic
348panic: third panic
349`
350	if !strings.HasPrefix(output, want) {
351		t.Fatalf("output does not start with %q:\n%s", want, output)
352	}
353
354}
355
356func TestGoexitCrash(t *testing.T) {
357	// External linking brings in cgo, causing deadlock detection not working.
358	testenv.MustInternalLink(t, false)
359
360	output := runTestProg(t, "testprog", "GoexitExit")
361	want := "no goroutines (main called runtime.Goexit) - deadlock!"
362	if !strings.Contains(output, want) {
363		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
364	}
365}
366
367func TestGoexitDefer(t *testing.T) {
368	c := make(chan struct{})
369	go func() {
370		defer func() {
371			r := recover()
372			if r != nil {
373				t.Errorf("non-nil recover during Goexit")
374			}
375			c <- struct{}{}
376		}()
377		runtime.Goexit()
378	}()
379	// Note: if the defer fails to run, we will get a deadlock here
380	<-c
381}
382
383func TestGoNil(t *testing.T) {
384	output := runTestProg(t, "testprog", "GoNil")
385	want := "go of nil func value"
386	if !strings.Contains(output, want) {
387		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
388	}
389}
390
391func TestMainGoroutineID(t *testing.T) {
392	output := runTestProg(t, "testprog", "MainGoroutineID")
393	want := "panic: test\n\ngoroutine 1 [running]:\n"
394	if !strings.HasPrefix(output, want) {
395		t.Fatalf("output does not start with %q:\n%s", want, output)
396	}
397}
398
399func TestNoHelperGoroutines(t *testing.T) {
400	output := runTestProg(t, "testprog", "NoHelperGoroutines")
401	matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1)
402	if len(matches) != 1 || matches[0][0] != "goroutine 1 [" {
403		t.Fatalf("want to see only goroutine 1, see:\n%s", output)
404	}
405}
406
407func TestBreakpoint(t *testing.T) {
408	output := runTestProg(t, "testprog", "Breakpoint")
409	// If runtime.Breakpoint() is inlined, then the stack trace prints
410	// "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()".
411	want := "runtime.Breakpoint("
412	if !strings.Contains(output, want) {
413		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
414	}
415}
416
417func TestGoexitInPanic(t *testing.T) {
418	// External linking brings in cgo, causing deadlock detection not working.
419	testenv.MustInternalLink(t, false)
420
421	// see issue 8774: this code used to trigger an infinite recursion
422	output := runTestProg(t, "testprog", "GoexitInPanic")
423	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
424	if !strings.HasPrefix(output, want) {
425		t.Fatalf("output does not start with %q:\n%s", want, output)
426	}
427}
428
429// Issue 14965: Runtime panics should be of type runtime.Error
430func TestRuntimePanicWithRuntimeError(t *testing.T) {
431	testCases := [...]func(){
432		0: func() {
433			var m map[uint64]bool
434			m[1234] = true
435		},
436		1: func() {
437			ch := make(chan struct{})
438			close(ch)
439			close(ch)
440		},
441		2: func() {
442			var ch = make(chan struct{})
443			close(ch)
444			ch <- struct{}{}
445		},
446		3: func() {
447			var s = make([]int, 2)
448			_ = s[2]
449		},
450		4: func() {
451			n := -1
452			_ = make(chan bool, n)
453		},
454		5: func() {
455			close((chan bool)(nil))
456		},
457	}
458
459	for i, fn := range testCases {
460		got := panicValue(fn)
461		if _, ok := got.(runtime.Error); !ok {
462			t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got)
463		}
464	}
465}
466
467func panicValue(fn func()) (recovered any) {
468	defer func() {
469		recovered = recover()
470	}()
471	fn()
472	return
473}
474
475func TestPanicAfterGoexit(t *testing.T) {
476	// an uncaught panic should still work after goexit
477	output := runTestProg(t, "testprog", "PanicAfterGoexit")
478	want := "panic: hello"
479	if !strings.HasPrefix(output, want) {
480		t.Fatalf("output does not start with %q:\n%s", want, output)
481	}
482}
483
484func TestRecoveredPanicAfterGoexit(t *testing.T) {
485	// External linking brings in cgo, causing deadlock detection not working.
486	testenv.MustInternalLink(t, false)
487
488	output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit")
489	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
490	if !strings.HasPrefix(output, want) {
491		t.Fatalf("output does not start with %q:\n%s", want, output)
492	}
493}
494
495func TestRecoverBeforePanicAfterGoexit(t *testing.T) {
496	// External linking brings in cgo, causing deadlock detection not working.
497	testenv.MustInternalLink(t, false)
498
499	t.Parallel()
500	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit")
501	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
502	if !strings.HasPrefix(output, want) {
503		t.Fatalf("output does not start with %q:\n%s", want, output)
504	}
505}
506
507func TestRecoverBeforePanicAfterGoexit2(t *testing.T) {
508	// External linking brings in cgo, causing deadlock detection not working.
509	testenv.MustInternalLink(t, false)
510
511	t.Parallel()
512	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2")
513	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!"
514	if !strings.HasPrefix(output, want) {
515		t.Fatalf("output does not start with %q:\n%s", want, output)
516	}
517}
518
519func TestNetpollDeadlock(t *testing.T) {
520	t.Parallel()
521	output := runTestProg(t, "testprognet", "NetpollDeadlock")
522	want := "done\n"
523	if !strings.HasSuffix(output, want) {
524		t.Fatalf("output does not start with %q:\n%s", want, output)
525	}
526}
527
528func TestPanicTraceback(t *testing.T) {
529	t.Parallel()
530	output := runTestProg(t, "testprog", "PanicTraceback")
531	want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n"
532	if !strings.HasPrefix(output, want) {
533		t.Fatalf("output does not start with %q:\n%s", want, output)
534	}
535
536	// Check functions in the traceback.
537	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"}
538	for _, fn := range fns {
539		re := regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`)
540		idx := re.FindStringIndex(output)
541		if idx == nil {
542			t.Fatalf("expected %q function in traceback:\n%s", fn, output)
543		}
544		output = output[idx[1]:]
545	}
546}
547
548func testPanicDeadlock(t *testing.T, name string, want string) {
549	// test issue 14432
550	output := runTestProg(t, "testprog", name)
551	if !strings.HasPrefix(output, want) {
552		t.Fatalf("output does not start with %q:\n%s", want, output)
553	}
554}
555
556func TestPanicDeadlockGosched(t *testing.T) {
557	testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n")
558}
559
560func TestPanicDeadlockSyscall(t *testing.T) {
561	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n")
562}
563
564func TestPanicLoop(t *testing.T) {
565	output := runTestProg(t, "testprog", "PanicLoop")
566	if want := "panic while printing panic value"; !strings.Contains(output, want) {
567		t.Errorf("output does not contain %q:\n%s", want, output)
568	}
569}
570
571func TestMemPprof(t *testing.T) {
572	testenv.MustHaveGoRun(t)
573
574	exe, err := buildTestProg(t, "testprog")
575	if err != nil {
576		t.Fatal(err)
577	}
578
579	got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput()
580	if err != nil {
581		t.Fatalf("testprog failed: %s, output:\n%s", err, got)
582	}
583	fn := strings.TrimSpace(string(got))
584	defer os.Remove(fn)
585
586	for try := 0; try < 2; try++ {
587		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top"))
588		// Check that pprof works both with and without explicit executable on command line.
589		if try == 0 {
590			cmd.Args = append(cmd.Args, exe, fn)
591		} else {
592			cmd.Args = append(cmd.Args, fn)
593		}
594		found := false
595		for i, e := range cmd.Env {
596			if strings.HasPrefix(e, "PPROF_TMPDIR=") {
597				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir()
598				found = true
599				break
600			}
601		}
602		if !found {
603			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
604		}
605
606		top, err := cmd.CombinedOutput()
607		t.Logf("%s:\n%s", cmd.Args, top)
608		if err != nil {
609			t.Error(err)
610		} else if !bytes.Contains(top, []byte("MemProf")) {
611			t.Error("missing MemProf in pprof output")
612		}
613	}
614}
615
616var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests")
617
618func TestConcurrentMapWrites(t *testing.T) {
619	if !*concurrentMapTest {
620		t.Skip("skipping without -run_concurrent_map_tests")
621	}
622	testenv.MustHaveGoRun(t)
623	output := runTestProg(t, "testprog", "concurrentMapWrites")
624	want := "fatal error: concurrent map writes"
625	if !strings.HasPrefix(output, want) {
626		t.Fatalf("output does not start with %q:\n%s", want, output)
627	}
628}
629func TestConcurrentMapReadWrite(t *testing.T) {
630	if !*concurrentMapTest {
631		t.Skip("skipping without -run_concurrent_map_tests")
632	}
633	testenv.MustHaveGoRun(t)
634	output := runTestProg(t, "testprog", "concurrentMapReadWrite")
635	want := "fatal error: concurrent map read and map write"
636	if !strings.HasPrefix(output, want) {
637		t.Fatalf("output does not start with %q:\n%s", want, output)
638	}
639}
640func TestConcurrentMapIterateWrite(t *testing.T) {
641	if !*concurrentMapTest {
642		t.Skip("skipping without -run_concurrent_map_tests")
643	}
644	testenv.MustHaveGoRun(t)
645	output := runTestProg(t, "testprog", "concurrentMapIterateWrite")
646	want := "fatal error: concurrent map iteration and map write"
647	if !strings.HasPrefix(output, want) {
648		t.Fatalf("output does not start with %q:\n%s", want, output)
649	}
650}
651
652type point struct {
653	x, y *int
654}
655
656func (p *point) negate() {
657	*p.x = *p.x * -1
658	*p.y = *p.y * -1
659}
660
661// Test for issue #10152.
662func TestPanicInlined(t *testing.T) {
663	defer func() {
664		r := recover()
665		if r == nil {
666			t.Fatalf("recover failed")
667		}
668		buf := make([]byte, 2048)
669		n := runtime.Stack(buf, false)
670		buf = buf[:n]
671		if !bytes.Contains(buf, []byte("(*point).negate(")) {
672			t.Fatalf("expecting stack trace to contain call to (*point).negate()")
673		}
674	}()
675
676	pt := new(point)
677	pt.negate()
678}
679
680// Test for issues #3934 and #20018.
681// We want to delay exiting until a panic print is complete.
682func TestPanicRace(t *testing.T) {
683	testenv.MustHaveGoRun(t)
684
685	exe, err := buildTestProg(t, "testprog")
686	if err != nil {
687		t.Fatal(err)
688	}
689
690	// The test is intentionally racy, and in my testing does not
691	// produce the expected output about 0.05% of the time.
692	// So run the program in a loop and only fail the test if we
693	// get the wrong output ten times in a row.
694	const tries = 10
695retry:
696	for i := 0; i < tries; i++ {
697		got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput()
698		if err == nil {
699			t.Logf("try %d: program exited successfully, should have failed", i+1)
700			continue
701		}
702
703		if i > 0 {
704			t.Logf("try %d:\n", i+1)
705		}
706		t.Logf("%s\n", got)
707
708		wants := []string{
709			"panic: crash",
710			"PanicRace",
711			"created by ",
712		}
713		for _, want := range wants {
714			if !bytes.Contains(got, []byte(want)) {
715				t.Logf("did not find expected string %q", want)
716				continue retry
717			}
718		}
719
720		// Test generated expected output.
721		return
722	}
723	t.Errorf("test ran %d times without producing expected output", tries)
724}
725
726func TestBadTraceback(t *testing.T) {
727	output := runTestProg(t, "testprog", "BadTraceback")
728	for _, want := range []string{
729		"unexpected return pc",
730		"called from 0xbad",
731		"00000bad",    // Smashed LR in hex dump
732		"<main.badLR", // Symbolization in hex dump (badLR1 or badLR2)
733	} {
734		if !strings.Contains(output, want) {
735			t.Errorf("output does not contain %q:\n%s", want, output)
736		}
737	}
738}
739
740func TestTimePprof(t *testing.T) {
741	// This test is unreliable on any system in which nanotime
742	// calls into libc.
743	switch runtime.GOOS {
744	case "aix", "darwin", "illumos", "openbsd", "solaris":
745		t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS)
746	}
747
748	// Pass GOTRACEBACK for issue #41120 to try to get more
749	// information on timeout.
750	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash")
751	fn = strings.TrimSpace(fn)
752	defer os.Remove(fn)
753
754	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn))
755	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir())
756	top, err := cmd.CombinedOutput()
757	t.Logf("%s", top)
758	if err != nil {
759		t.Error(err)
760	} else if bytes.Contains(top, []byte("ExternalCode")) {
761		t.Error("profiler refers to ExternalCode")
762	}
763}
764
765// Test that runtime.abort does so.
766func TestAbort(t *testing.T) {
767	// Pass GOTRACEBACK to ensure we get runtime frames.
768	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system")
769	if want := "runtime.abort"; !strings.Contains(output, want) {
770		t.Errorf("output does not contain %q:\n%s", want, output)
771	}
772	if strings.Contains(output, "BAD") {
773		t.Errorf("output contains BAD:\n%s", output)
774	}
775	// Check that it's a signal traceback.
776	want := "PC="
777	// For systems that use a breakpoint, check specifically for that.
778	switch runtime.GOARCH {
779	case "386", "amd64":
780		switch runtime.GOOS {
781		case "plan9":
782			want = "sys: breakpoint"
783		case "windows":
784			want = "Exception 0x80000003"
785		default:
786			want = "SIGTRAP"
787		}
788	}
789	if !strings.Contains(output, want) {
790		t.Errorf("output does not contain %q:\n%s", want, output)
791	}
792}
793
794// For TestRuntimePanic: test a panic in the runtime package without
795// involving the testing harness.
796func init() {
797	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" {
798		defer func() {
799			if r := recover(); r != nil {
800				// We expect to crash, so exit 0
801				// to indicate failure.
802				os.Exit(0)
803			}
804		}()
805		runtime.PanicForTesting(nil, 1)
806		// We expect to crash, so exit 0 to indicate failure.
807		os.Exit(0)
808	}
809	if os.Getenv("GO_TEST_RUNTIME_NPE_READMEMSTATS") == "1" {
810		runtime.ReadMemStats(nil)
811		os.Exit(0)
812	}
813	if os.Getenv("GO_TEST_RUNTIME_NPE_FUNCMETHOD") == "1" {
814		var f *runtime.Func
815		_ = f.Entry()
816		os.Exit(0)
817	}
818
819}
820
821func TestRuntimePanic(t *testing.T) {
822	testenv.MustHaveExec(t)
823	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=^TestRuntimePanic$"))
824	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1")
825	out, err := cmd.CombinedOutput()
826	t.Logf("%s", out)
827	if err == nil {
828		t.Error("child process did not fail")
829	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) {
830		t.Errorf("output did not contain expected string %q", want)
831	}
832}
833
834func TestTracebackRuntimeFunction(t *testing.T) {
835	testenv.MustHaveExec(t)
836	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeFunction"))
837	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_READMEMSTATS=1")
838	out, err := cmd.CombinedOutput()
839	t.Logf("%s", out)
840	if err == nil {
841		t.Error("child process did not fail")
842	} else if want := "runtime.ReadMemStats"; !bytes.Contains(out, []byte(want)) {
843		t.Errorf("output did not contain expected string %q", want)
844	}
845}
846
847func TestTracebackRuntimeMethod(t *testing.T) {
848	testenv.MustHaveExec(t)
849	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestTracebackRuntimeMethod"))
850	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_NPE_FUNCMETHOD=1")
851	out, err := cmd.CombinedOutput()
852	t.Logf("%s", out)
853	if err == nil {
854		t.Error("child process did not fail")
855	} else if want := "runtime.(*Func).Entry"; !bytes.Contains(out, []byte(want)) {
856		t.Errorf("output did not contain expected string %q", want)
857	}
858}
859
860// Test that g0 stack overflows are handled gracefully.
861func TestG0StackOverflow(t *testing.T) {
862	testenv.MustHaveExec(t)
863
864	if runtime.GOOS == "ios" {
865		testenv.SkipFlaky(t, 62671)
866	}
867
868	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" {
869		cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0], "-test.run=^TestG0StackOverflow$", "-test.v"))
870		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1")
871		out, err := cmd.CombinedOutput()
872		t.Logf("output:\n%s", out)
873		// Don't check err since it's expected to crash.
874		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 {
875			t.Fatalf("%s\n(exit status %v)", out, err)
876		}
877		if runtime.CrashStackImplemented {
878			// check for a stack trace
879			want := "runtime.stackOverflow"
880			if n := strings.Count(string(out), want); n < 5 {
881				t.Errorf("output does not contain %q at least 5 times:\n%s", want, out)
882			}
883			return // it's not a signal-style traceback
884		}
885		// Check that it's a signal-style traceback.
886		if runtime.GOOS != "windows" {
887			if want := "PC="; !strings.Contains(string(out), want) {
888				t.Errorf("output does not contain %q:\n%s", want, out)
889			}
890		}
891		return
892	}
893
894	runtime.G0StackOverflow()
895}
896
897// For TestCrashWhileTracing: test a panic without involving the testing
898// harness, as we rely on stdout only containing trace output.
899func init() {
900	if os.Getenv("TEST_CRASH_WHILE_TRACING") == "1" {
901		trace.Start(os.Stdout)
902		trace.Log(context.Background(), "xyzzy-cat", "xyzzy-msg")
903		panic("yzzyx")
904	}
905}
906
907func TestCrashWhileTracing(t *testing.T) {
908	testenv.MustHaveExec(t)
909
910	cmd := testenv.CleanCmdEnv(testenv.Command(t, os.Args[0]))
911	cmd.Env = append(cmd.Env, "TEST_CRASH_WHILE_TRACING=1")
912	stdOut, err := cmd.StdoutPipe()
913	var errOut bytes.Buffer
914	cmd.Stderr = &errOut
915
916	if err := cmd.Start(); err != nil {
917		t.Fatalf("could not start subprocess: %v", err)
918	}
919	r, err := traceparse.NewReader(stdOut)
920	if err != nil {
921		t.Fatalf("could not create trace.NewReader: %v", err)
922	}
923	var seen, seenSync bool
924	i := 1
925loop:
926	for ; ; i++ {
927		ev, err := r.ReadEvent()
928		if err != nil {
929			// We may have a broken tail to the trace -- that's OK.
930			// We'll make sure we saw at least one complete generation.
931			if err != io.EOF {
932				t.Logf("error at event %d: %v", i, err)
933			}
934			break loop
935		}
936		switch ev.Kind() {
937		case traceparse.EventSync:
938			seenSync = true
939		case traceparse.EventLog:
940			v := ev.Log()
941			if v.Category == "xyzzy-cat" && v.Message == "xyzzy-msg" {
942				// Should we already stop reading here? More events may come, but
943				// we're not guaranteeing a fully unbroken trace until the last
944				// byte...
945				seen = true
946			}
947		}
948	}
949	if err := cmd.Wait(); err == nil {
950		t.Error("the process should have panicked")
951	}
952	if !seenSync {
953		t.Errorf("expected at least one full generation to have been emitted before the trace was considered broken")
954	}
955	if !seen {
956		t.Errorf("expected one matching log event matching, but none of the %d received trace events match", i)
957	}
958	t.Logf("stderr output:\n%s", errOut.String())
959	needle := "yzzyx\n"
960	if n := strings.Count(errOut.String(), needle); n != 1 {
961		t.Fatalf("did not find expected panic message %q\n(exit status %v)", needle, err)
962	}
963}
964
965// Test that panic message is not clobbered.
966// See issue 30150.
967func TestDoublePanic(t *testing.T) {
968	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1")
969	wants := []string{"panic: XXX", "panic: YYY"}
970	for _, want := range wants {
971		if !strings.Contains(output, want) {
972			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want)
973		}
974	}
975}
976
977// Test that panic while panicking discards error message
978// See issue 52257
979func TestPanicWhilePanicking(t *testing.T) {
980	tests := []struct {
981		Want string
982		Func string
983	}{
984		{
985			"panic while printing panic value: important multi-line\n\terror message",
986			"ErrorPanic",
987		},
988		{
989			"panic while printing panic value: important multi-line\n\tstringer message",
990			"StringerPanic",
991		},
992		{
993			"panic while printing panic value: type",
994			"DoubleErrorPanic",
995		},
996		{
997			"panic while printing panic value: type",
998			"DoubleStringerPanic",
999		},
1000		{
1001			"panic while printing panic value: type",
1002			"CircularPanic",
1003		},
1004		{
1005			"important multi-line\n\tstring message",
1006			"StringPanic",
1007		},
1008		{
1009			"nil",
1010			"NilPanic",
1011		},
1012	}
1013	for _, x := range tests {
1014		output := runTestProg(t, "testprog", x.Func)
1015		if !strings.Contains(output, x.Want) {
1016			t.Errorf("output does not contain %q:\n%s", x.Want, output)
1017		}
1018	}
1019}
1020
1021func TestPanicOnUnsafeSlice(t *testing.T) {
1022	output := runTestProg(t, "testprog", "panicOnNilAndEleSizeIsZero")
1023	want := "panic: runtime error: unsafe.Slice: ptr is nil and len is not zero"
1024	if !strings.Contains(output, want) {
1025		t.Errorf("output does not contain %q:\n%s", want, output)
1026	}
1027}
1028
1029func TestNetpollWaiters(t *testing.T) {
1030	t.Parallel()
1031	output := runTestProg(t, "testprognet", "NetpollWaiters")
1032	want := "OK\n"
1033	if output != want {
1034		t.Fatalf("output is not %q\n%s", want, output)
1035	}
1036}
1037