| // Copyright 2012 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package runtime_test | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"errors" | 
 | 	"flag" | 
 | 	"fmt" | 
 | 	"internal/testenv" | 
 | 	"os" | 
 | 	"os/exec" | 
 | 	"path/filepath" | 
 | 	"regexp" | 
 | 	"runtime" | 
 | 	"strings" | 
 | 	"sync" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | var toRemove []string | 
 |  | 
 | func TestMain(m *testing.M) { | 
 | 	status := m.Run() | 
 | 	for _, file := range toRemove { | 
 | 		os.RemoveAll(file) | 
 | 	} | 
 | 	os.Exit(status) | 
 | } | 
 |  | 
 | var testprog struct { | 
 | 	sync.Mutex | 
 | 	dir    string | 
 | 	target map[string]*buildexe | 
 | } | 
 |  | 
 | type buildexe struct { | 
 | 	once sync.Once | 
 | 	exe  string | 
 | 	err  error | 
 | } | 
 |  | 
 | func runTestProg(t *testing.T, binary, name string, env ...string) string { | 
 | 	if *flagQuick { | 
 | 		t.Skip("-quick") | 
 | 	} | 
 |  | 
 | 	testenv.MustHaveGoBuild(t) | 
 |  | 
 | 	exe, err := buildTestProg(t, binary) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 |  | 
 | 	return runBuiltTestProg(t, exe, name, env...) | 
 | } | 
 |  | 
 | func runBuiltTestProg(t *testing.T, exe, name string, env ...string) string { | 
 | 	if *flagQuick { | 
 | 		t.Skip("-quick") | 
 | 	} | 
 |  | 
 | 	testenv.MustHaveGoBuild(t) | 
 |  | 
 | 	cmd := testenv.CleanCmdEnv(exec.Command(exe, name)) | 
 | 	cmd.Env = append(cmd.Env, env...) | 
 | 	if testing.Short() { | 
 | 		cmd.Env = append(cmd.Env, "RUNTIME_TEST_SHORT=1") | 
 | 	} | 
 | 	out, _ := testenv.RunWithTimeout(t, cmd) | 
 | 	return string(out) | 
 | } | 
 |  | 
 | var serializeBuild = make(chan bool, 2) | 
 |  | 
 | func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) { | 
 | 	if *flagQuick { | 
 | 		t.Skip("-quick") | 
 | 	} | 
 | 	testenv.MustHaveGoBuild(t) | 
 |  | 
 | 	testprog.Lock() | 
 | 	if testprog.dir == "" { | 
 | 		dir, err := os.MkdirTemp("", "go-build") | 
 | 		if err != nil { | 
 | 			t.Fatalf("failed to create temp directory: %v", err) | 
 | 		} | 
 | 		testprog.dir = dir | 
 | 		toRemove = append(toRemove, dir) | 
 | 	} | 
 |  | 
 | 	if testprog.target == nil { | 
 | 		testprog.target = make(map[string]*buildexe) | 
 | 	} | 
 | 	name := binary | 
 | 	if len(flags) > 0 { | 
 | 		name += "_" + strings.Join(flags, "_") | 
 | 	} | 
 | 	target, ok := testprog.target[name] | 
 | 	if !ok { | 
 | 		target = &buildexe{} | 
 | 		testprog.target[name] = target | 
 | 	} | 
 |  | 
 | 	dir := testprog.dir | 
 |  | 
 | 	// Unlock testprog while actually building, so that other | 
 | 	// tests can look up executables that were already built. | 
 | 	testprog.Unlock() | 
 |  | 
 | 	target.once.Do(func() { | 
 | 		// Only do two "go build"'s at a time, | 
 | 		// to keep load from getting too high. | 
 | 		serializeBuild <- true | 
 | 		defer func() { <-serializeBuild }() | 
 |  | 
 | 		// Don't get confused if testenv.GoToolPath calls t.Skip. | 
 | 		target.err = errors.New("building test called t.Skip") | 
 |  | 
 | 		exe := filepath.Join(dir, name+".exe") | 
 |  | 
 | 		t.Logf("running go build -o %s %s", exe, strings.Join(flags, " ")) | 
 | 		cmd := exec.Command(testenv.GoToolPath(t), append([]string{"build", "-o", exe}, flags...)...) | 
 | 		cmd.Dir = "testdata/" + binary | 
 | 		out, err := testenv.CleanCmdEnv(cmd).CombinedOutput() | 
 | 		if err != nil { | 
 | 			target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) | 
 | 		} else { | 
 | 			target.exe = exe | 
 | 			target.err = nil | 
 | 		} | 
 | 	}) | 
 |  | 
 | 	return target.exe, target.err | 
 | } | 
 |  | 
 | func TestVDSO(t *testing.T) { | 
 | 	t.Parallel() | 
 | 	output := runTestProg(t, "testprog", "SignalInVDSO") | 
 | 	want := "success\n" | 
 | 	if output != want { | 
 | 		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func testCrashHandler(t *testing.T, cgo bool) { | 
 | 	type crashTest struct { | 
 | 		Cgo bool | 
 | 	} | 
 | 	var output string | 
 | 	if cgo { | 
 | 		output = runTestProg(t, "testprogcgo", "Crash") | 
 | 	} else { | 
 | 		output = runTestProg(t, "testprog", "Crash") | 
 | 	} | 
 | 	want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" | 
 | 	if output != want { | 
 | 		t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestCrashHandler(t *testing.T) { | 
 | 	testCrashHandler(t, false) | 
 | } | 
 |  | 
 | func testDeadlock(t *testing.T, name string) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	output := runTestProg(t, "testprog", name) | 
 | 	want := "fatal error: all goroutines are asleep - deadlock!\n" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestSimpleDeadlock(t *testing.T) { | 
 | 	testDeadlock(t, "SimpleDeadlock") | 
 | } | 
 |  | 
 | func TestInitDeadlock(t *testing.T) { | 
 | 	testDeadlock(t, "InitDeadlock") | 
 | } | 
 |  | 
 | func TestLockedDeadlock(t *testing.T) { | 
 | 	testDeadlock(t, "LockedDeadlock") | 
 | } | 
 |  | 
 | func TestLockedDeadlock2(t *testing.T) { | 
 | 	testDeadlock(t, "LockedDeadlock2") | 
 | } | 
 |  | 
 | func TestGoexitDeadlock(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	output := runTestProg(t, "testprog", "GoexitDeadlock") | 
 | 	want := "no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.Contains(output, want) { | 
 | 		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestStackOverflow(t *testing.T) { | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		t.Skip("gccgo does not do stack overflow checking") | 
 | 	} | 
 | 	output := runTestProg(t, "testprog", "StackOverflow") | 
 | 	want := []string{ | 
 | 		"runtime: goroutine stack exceeds 1474560-byte limit\n", | 
 | 		"fatal error: stack overflow", | 
 | 		// information about the current SP and stack bounds | 
 | 		"runtime: sp=", | 
 | 		"stack=[", | 
 | 	} | 
 | 	if !strings.HasPrefix(output, want[0]) { | 
 | 		t.Errorf("output does not start with %q", want[0]) | 
 | 	} | 
 | 	for _, s := range want[1:] { | 
 | 		if !strings.Contains(output, s) { | 
 | 			t.Errorf("output does not contain %q", s) | 
 | 		} | 
 | 	} | 
 | 	if t.Failed() { | 
 | 		t.Logf("output:\n%s", output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestThreadExhaustion(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "ThreadExhaustion") | 
 | 	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRecursivePanic(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "RecursivePanic") | 
 | 	want := `wrap: bad | 
 | panic: again | 
 |  | 
 | ` | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | func TestRecursivePanic2(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "RecursivePanic2") | 
 | 	want := `first panic | 
 | second panic | 
 | panic: third panic | 
 |  | 
 | ` | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | func TestRecursivePanic3(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "RecursivePanic3") | 
 | 	want := `panic: first panic | 
 |  | 
 | ` | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | func TestRecursivePanic4(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "RecursivePanic4") | 
 | 	want := `panic: first panic [recovered] | 
 | 	panic: second panic | 
 | ` | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | func TestRecursivePanic5(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "RecursivePanic5") | 
 | 	want := `first panic | 
 | second panic | 
 | panic: third panic | 
 | ` | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | } | 
 |  | 
 | func TestGoexitCrash(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	output := runTestProg(t, "testprog", "GoexitExit") | 
 | 	want := "no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.Contains(output, want) { | 
 | 		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestGoexitDefer(t *testing.T) { | 
 | 	c := make(chan struct{}) | 
 | 	go func() { | 
 | 		defer func() { | 
 | 			r := recover() | 
 | 			if r != nil { | 
 | 				t.Errorf("non-nil recover during Goexit") | 
 | 			} | 
 | 			c <- struct{}{} | 
 | 		}() | 
 | 		runtime.Goexit() | 
 | 	}() | 
 | 	// Note: if the defer fails to run, we will get a deadlock here | 
 | 	<-c | 
 | } | 
 |  | 
 | func TestGoNil(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "GoNil") | 
 | 	want := "go of nil func value" | 
 | 	if !strings.Contains(output, want) { | 
 | 		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestMainGoroutineID(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "MainGoroutineID") | 
 | 	want := "panic: test\n\ngoroutine 1 [running]:\n" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestNoHelperGoroutines(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "NoHelperGoroutines") | 
 | 	matches := regexp.MustCompile(`goroutine [0-9]+ \[`).FindAllStringSubmatch(output, -1) | 
 | 	if len(matches) != 1 || matches[0][0] != "goroutine 1 [" { | 
 | 		t.Fatalf("want to see only goroutine 1, see:\n%s", output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestBreakpoint(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "Breakpoint") | 
 | 	// If runtime.Breakpoint() is inlined, then the stack trace prints | 
 | 	// "runtime.Breakpoint(...)" instead of "runtime.Breakpoint()". | 
 | 	// For gccgo, no parens. | 
 | 	want := "runtime.Breakpoint" | 
 | 	if !strings.Contains(output, want) { | 
 | 		t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want) | 
 | 	} | 
 | } | 
 |  | 
 | func TestGoexitInPanic(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	// see issue 8774: this code used to trigger an infinite recursion | 
 | 	output := runTestProg(t, "testprog", "GoexitInPanic") | 
 | 	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | // Issue 14965: Runtime panics should be of type runtime.Error | 
 | func TestRuntimePanicWithRuntimeError(t *testing.T) { | 
 | 	testCases := [...]func(){ | 
 | 		0: func() { | 
 | 			var m map[uint64]bool | 
 | 			m[1234] = true | 
 | 		}, | 
 | 		1: func() { | 
 | 			ch := make(chan struct{}) | 
 | 			close(ch) | 
 | 			close(ch) | 
 | 		}, | 
 | 		2: func() { | 
 | 			var ch = make(chan struct{}) | 
 | 			close(ch) | 
 | 			ch <- struct{}{} | 
 | 		}, | 
 | 		3: func() { | 
 | 			var s = make([]int, 2) | 
 | 			_ = s[2] | 
 | 		}, | 
 | 		4: func() { | 
 | 			n := -1 | 
 | 			_ = make(chan bool, n) | 
 | 		}, | 
 | 		5: func() { | 
 | 			close((chan bool)(nil)) | 
 | 		}, | 
 | 	} | 
 |  | 
 | 	for i, fn := range testCases { | 
 | 		got := panicValue(fn) | 
 | 		if _, ok := got.(runtime.Error); !ok { | 
 | 			t.Errorf("test #%d: recovered value %v(type %T) does not implement runtime.Error", i, got, got) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func panicValue(fn func()) (recovered any) { | 
 | 	defer func() { | 
 | 		recovered = recover() | 
 | 	}() | 
 | 	fn() | 
 | 	return | 
 | } | 
 |  | 
 | func TestPanicAfterGoexit(t *testing.T) { | 
 | 	// an uncaught panic should still work after goexit | 
 | 	output := runTestProg(t, "testprog", "PanicAfterGoexit") | 
 | 	want := "panic: hello" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRecoveredPanicAfterGoexit(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	output := runTestProg(t, "testprog", "RecoveredPanicAfterGoexit") | 
 | 	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRecoverBeforePanicAfterGoexit(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	t.Parallel() | 
 | 	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit") | 
 | 	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRecoverBeforePanicAfterGoexit2(t *testing.T) { | 
 | 	// External linking brings in cgo, causing deadlock detection not working. | 
 | 	testenv.MustInternalLink(t) | 
 |  | 
 | 	t.Parallel() | 
 | 	output := runTestProg(t, "testprog", "RecoverBeforePanicAfterGoexit2") | 
 | 	want := "fatal error: no goroutines (main called runtime.Goexit) - deadlock!" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestNetpollDeadlock(t *testing.T) { | 
 | 	t.Parallel() | 
 | 	output := runTestProg(t, "testprognet", "NetpollDeadlock") | 
 | 	want := "done\n" | 
 | 	if !strings.HasSuffix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestPanicTraceback(t *testing.T) { | 
 | 	t.Parallel() | 
 | 	output := runTestProg(t, "testprog", "PanicTraceback") | 
 | 	want := "panic: hello\n\tpanic: panic pt2\n\tpanic: panic pt1\n" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 |  | 
 | 	// Check functions in the traceback. | 
 | 	fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"} | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		fns = []string{"main.pt1..func1", "panic", "main.pt2..func1", "panic", "main.pt2", "main.pt1"} | 
 | 	} | 
 | 	for _, fn := range fns { | 
 | 		var re *regexp.Regexp | 
 | 		if runtime.Compiler != "gccgo" { | 
 | 			re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `\(.*\n`) | 
 | 		} else { | 
 | 			re = regexp.MustCompile(`(?m)^` + regexp.QuoteMeta(fn) + `.*\n`) | 
 | 		} | 
 | 		idx := re.FindStringIndex(output) | 
 | 		if idx == nil { | 
 | 			t.Fatalf("expected %q function in traceback:\n%s", fn, output) | 
 | 		} | 
 | 		output = output[idx[1]:] | 
 | 	} | 
 | } | 
 |  | 
 | func testPanicDeadlock(t *testing.T, name string, want string) { | 
 | 	// test issue 14432 | 
 | 	output := runTestProg(t, "testprog", name) | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestPanicDeadlockGosched(t *testing.T) { | 
 | 	testPanicDeadlock(t, "GoschedInPanic", "panic: errorThatGosched\n\n") | 
 | } | 
 |  | 
 | func TestPanicDeadlockSyscall(t *testing.T) { | 
 | 	testPanicDeadlock(t, "SyscallInPanic", "1\n2\npanic: 3\n\n") | 
 | } | 
 |  | 
 | func TestPanicLoop(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "PanicLoop") | 
 | 	if want := "panic while printing panic value"; !strings.Contains(output, want) { | 
 | 		t.Errorf("output does not contain %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | func TestMemPprof(t *testing.T) { | 
 | 	testenv.MustHaveGoRun(t) | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		t.Skip("gccgo may not have the pprof tool") | 
 | 	} | 
 |  | 
 | 	exe, err := buildTestProg(t, "testprog") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 |  | 
 | 	got, err := testenv.CleanCmdEnv(exec.Command(exe, "MemProf")).CombinedOutput() | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	fn := strings.TrimSpace(string(got)) | 
 | 	defer os.Remove(fn) | 
 |  | 
 | 	for try := 0; try < 2; try++ { | 
 | 		cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-alloc_space", "-top")) | 
 | 		// Check that pprof works both with and without explicit executable on command line. | 
 | 		if try == 0 { | 
 | 			cmd.Args = append(cmd.Args, exe, fn) | 
 | 		} else { | 
 | 			cmd.Args = append(cmd.Args, fn) | 
 | 		} | 
 | 		found := false | 
 | 		for i, e := range cmd.Env { | 
 | 			if strings.HasPrefix(e, "PPROF_TMPDIR=") { | 
 | 				cmd.Env[i] = "PPROF_TMPDIR=" + os.TempDir() | 
 | 				found = true | 
 | 				break | 
 | 			} | 
 | 		} | 
 | 		if !found { | 
 | 			cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) | 
 | 		} | 
 |  | 
 | 		top, err := cmd.CombinedOutput() | 
 | 		t.Logf("%s:\n%s", cmd.Args, top) | 
 | 		if err != nil { | 
 | 			t.Error(err) | 
 | 		} else if !bytes.Contains(top, []byte("MemProf")) { | 
 | 			t.Error("missing MemProf in pprof output") | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | var concurrentMapTest = flag.Bool("run_concurrent_map_tests", false, "also run flaky concurrent map tests") | 
 |  | 
 | func TestConcurrentMapWrites(t *testing.T) { | 
 | 	if !*concurrentMapTest { | 
 | 		t.Skip("skipping without -run_concurrent_map_tests") | 
 | 	} | 
 | 	testenv.MustHaveGoRun(t) | 
 | 	output := runTestProg(t, "testprog", "concurrentMapWrites") | 
 | 	want := "fatal error: concurrent map writes" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 | func TestConcurrentMapReadWrite(t *testing.T) { | 
 | 	if !*concurrentMapTest { | 
 | 		t.Skip("skipping without -run_concurrent_map_tests") | 
 | 	} | 
 | 	testenv.MustHaveGoRun(t) | 
 | 	output := runTestProg(t, "testprog", "concurrentMapReadWrite") | 
 | 	want := "fatal error: concurrent map read and map write" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 | func TestConcurrentMapIterateWrite(t *testing.T) { | 
 | 	if !*concurrentMapTest { | 
 | 		t.Skip("skipping without -run_concurrent_map_tests") | 
 | 	} | 
 | 	testenv.MustHaveGoRun(t) | 
 | 	output := runTestProg(t, "testprog", "concurrentMapIterateWrite") | 
 | 	want := "fatal error: concurrent map iteration and map write" | 
 | 	if !strings.HasPrefix(output, want) { | 
 | 		t.Fatalf("output does not start with %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | type point struct { | 
 | 	x, y *int | 
 | } | 
 |  | 
 | func (p *point) negate() { | 
 | 	*p.x = *p.x * -1 | 
 | 	*p.y = *p.y * -1 | 
 | } | 
 |  | 
 | // Test for issue #10152. | 
 | func TestPanicInlined(t *testing.T) { | 
 | 	defer func() { | 
 | 		r := recover() | 
 | 		if r == nil { | 
 | 			t.Fatalf("recover failed") | 
 | 		} | 
 | 		buf := make([]byte, 2048) | 
 | 		n := runtime.Stack(buf, false) | 
 | 		buf = buf[:n] | 
 | 		want := []byte("(*point).negate(") | 
 | 		if runtime.Compiler == "gccgo" { | 
 | 			want = []byte("point.negate") | 
 | 		} | 
 | 		if !bytes.Contains(buf, want) { | 
 | 			t.Logf("%s", buf) | 
 | 			t.Fatalf("expecting stack trace to contain call to %s", want) | 
 | 		} | 
 | 	}() | 
 |  | 
 | 	pt := new(point) | 
 | 	pt.negate() | 
 | } | 
 |  | 
 | // Test for issues #3934 and #20018. | 
 | // We want to delay exiting until a panic print is complete. | 
 | func TestPanicRace(t *testing.T) { | 
 | 	testenv.MustHaveGoRun(t) | 
 |  | 
 | 	exe, err := buildTestProg(t, "testprog") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 |  | 
 | 	// The test is intentionally racy, and in my testing does not | 
 | 	// produce the expected output about 0.05% of the time. | 
 | 	// So run the program in a loop and only fail the test if we | 
 | 	// get the wrong output ten times in a row. | 
 | 	const tries = 10 | 
 | retry: | 
 | 	for i := 0; i < tries; i++ { | 
 | 		got, err := testenv.CleanCmdEnv(exec.Command(exe, "PanicRace")).CombinedOutput() | 
 | 		if err == nil { | 
 | 			t.Logf("try %d: program exited successfully, should have failed", i+1) | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		if i > 0 { | 
 | 			t.Logf("try %d:\n", i+1) | 
 | 		} | 
 | 		t.Logf("%s\n", got) | 
 |  | 
 | 		wants := []string{ | 
 | 			"panic: crash", | 
 | 			"PanicRace", | 
 | 			"created by ", | 
 | 		} | 
 | 		if runtime.Compiler == "gccgo" { | 
 | 			// gccgo will dump a function name like main.$nested30. | 
 | 			// Match on the file name instead. | 
 | 			wants[1] = "panicrace" | 
 | 		} | 
 | 		for _, want := range wants { | 
 | 			if !bytes.Contains(got, []byte(want)) { | 
 | 				t.Logf("did not find expected string %q", want) | 
 | 				continue retry | 
 | 			} | 
 | 		} | 
 |  | 
 | 		// Test generated expected output. | 
 | 		return | 
 | 	} | 
 | 	t.Errorf("test ran %d times without producing expected output", tries) | 
 | } | 
 |  | 
 | func TestBadTraceback(t *testing.T) { | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		t.Skip("gccgo does not do a hex dump") | 
 | 	} | 
 | 	output := runTestProg(t, "testprog", "BadTraceback") | 
 | 	for _, want := range []string{ | 
 | 		"runtime: unexpected return pc", | 
 | 		"called from 0xbad", | 
 | 		"00000bad",    // Smashed LR in hex dump | 
 | 		"<main.badLR", // Symbolization in hex dump (badLR1 or badLR2) | 
 | 	} { | 
 | 		if !strings.Contains(output, want) { | 
 | 			t.Errorf("output does not contain %q:\n%s", want, output) | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | func TestTimePprof(t *testing.T) { | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		t.Skip("gccgo may not have the pprof tool") | 
 | 	} | 
 | 	// This test is unreliable on any system in which nanotime | 
 | 	// calls into libc. | 
 | 	switch runtime.GOOS { | 
 | 	case "aix", "darwin", "illumos", "openbsd", "solaris": | 
 | 		t.Skipf("skipping on %s because nanotime calls libc", runtime.GOOS) | 
 | 	} | 
 |  | 
 | 	// Pass GOTRACEBACK for issue #41120 to try to get more | 
 | 	// information on timeout. | 
 | 	fn := runTestProg(t, "testprog", "TimeProf", "GOTRACEBACK=crash") | 
 | 	fn = strings.TrimSpace(fn) | 
 | 	defer os.Remove(fn) | 
 |  | 
 | 	cmd := testenv.CleanCmdEnv(exec.Command(testenv.GoToolPath(t), "tool", "pprof", "-top", "-nodecount=1", fn)) | 
 | 	cmd.Env = append(cmd.Env, "PPROF_TMPDIR="+os.TempDir()) | 
 | 	top, err := cmd.CombinedOutput() | 
 | 	t.Logf("%s", top) | 
 | 	if err != nil { | 
 | 		t.Error(err) | 
 | 	} else if bytes.Contains(top, []byte("ExternalCode")) { | 
 | 		t.Error("profiler refers to ExternalCode") | 
 | 	} | 
 | } | 
 |  | 
 | // Test that runtime.abort does so. | 
 | func TestAbort(t *testing.T) { | 
 | 	if runtime.Compiler == "gccgo" && runtime.GOOS == "solaris" { | 
 | 		t.Skip("not supported by gofrontend on Solaris") | 
 | 	} | 
 |  | 
 | 	// Pass GOTRACEBACK to ensure we get runtime frames. | 
 | 	output := runTestProg(t, "testprog", "Abort", "GOTRACEBACK=system") | 
 | 	if want := "runtime.abort"; !strings.Contains(output, want) { | 
 | 		t.Errorf("output does not contain %q:\n%s", want, output) | 
 | 	} | 
 | 	if strings.Contains(output, "BAD") { | 
 | 		t.Errorf("output contains BAD:\n%s", output) | 
 | 	} | 
 | 	// Check that it's a signal traceback. | 
 | 	want := "PC=" | 
 | 	// For systems that use a breakpoint, check specifically for that. | 
 | 	if runtime.Compiler == "gc" { | 
 | 		switch runtime.GOARCH { | 
 | 		case "386", "amd64": | 
 | 			switch runtime.GOOS { | 
 | 			case "plan9": | 
 | 				want = "sys: breakpoint" | 
 | 			case "windows": | 
 | 				want = "Exception 0x80000003" | 
 | 			default: | 
 | 				want = "SIGTRAP" | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	if !strings.Contains(output, want) { | 
 | 		t.Errorf("output does not contain %q:\n%s", want, output) | 
 | 	} | 
 | } | 
 |  | 
 | // For TestRuntimePanic: test a panic in the runtime package without | 
 | // involving the testing harness. | 
 | func init() { | 
 | 	if os.Getenv("GO_TEST_RUNTIME_PANIC") == "1" { | 
 | 		defer func() { | 
 | 			if r := recover(); r != nil { | 
 | 				// We expect to crash, so exit 0 | 
 | 				// to indicate failure. | 
 | 				os.Exit(0) | 
 | 			} | 
 | 		}() | 
 | 		runtime.PanicForTesting(nil, 1) | 
 | 		// We expect to crash, so exit 0 to indicate failure. | 
 | 		os.Exit(0) | 
 | 	} | 
 | } | 
 |  | 
 | func TestRuntimePanic(t *testing.T) { | 
 | 	testenv.MustHaveExec(t) | 
 | 	cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestRuntimePanic")) | 
 | 	cmd.Env = append(cmd.Env, "GO_TEST_RUNTIME_PANIC=1") | 
 | 	out, err := cmd.CombinedOutput() | 
 | 	t.Logf("%s", out) | 
 | 	if err == nil { | 
 | 		t.Error("child process did not fail") | 
 | 	} else if want := "runtime.unexportedPanicForTesting"; !bytes.Contains(out, []byte(want)) { | 
 | 		t.Errorf("output did not contain expected string %q", want) | 
 | 	} | 
 | } | 
 |  | 
 | // Test that g0 stack overflows are handled gracefully. | 
 | func TestG0StackOverflow(t *testing.T) { | 
 | 	if runtime.Compiler == "gccgo" { | 
 | 		t.Skip("g0 stack overflow not supported by gofrontend") | 
 | 	} | 
 |  | 
 | 	testenv.MustHaveExec(t) | 
 |  | 
 | 	switch runtime.GOOS { | 
 | 	case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "android": | 
 | 		t.Skipf("g0 stack is wrong on pthread platforms (see golang.org/issue/26061)") | 
 | 	} | 
 |  | 
 | 	if os.Getenv("TEST_G0_STACK_OVERFLOW") != "1" { | 
 | 		cmd := testenv.CleanCmdEnv(exec.Command(os.Args[0], "-test.run=TestG0StackOverflow", "-test.v")) | 
 | 		cmd.Env = append(cmd.Env, "TEST_G0_STACK_OVERFLOW=1") | 
 | 		out, err := cmd.CombinedOutput() | 
 | 		// Don't check err since it's expected to crash. | 
 | 		if n := strings.Count(string(out), "morestack on g0\n"); n != 1 { | 
 | 			t.Fatalf("%s\n(exit status %v)", out, err) | 
 | 		} | 
 | 		// Check that it's a signal-style traceback. | 
 | 		if runtime.GOOS != "windows" { | 
 | 			if want := "PC="; !strings.Contains(string(out), want) { | 
 | 				t.Errorf("output does not contain %q:\n%s", want, out) | 
 | 			} | 
 | 		} | 
 | 		return | 
 | 	} | 
 |  | 
 | 	runtime.G0StackOverflow() | 
 | } | 
 |  | 
 | // Test that panic message is not clobbered. | 
 | // See issue 30150. | 
 | func TestDoublePanic(t *testing.T) { | 
 | 	output := runTestProg(t, "testprog", "DoublePanic", "GODEBUG=clobberfree=1") | 
 | 	wants := []string{"panic: XXX", "panic: YYY"} | 
 | 	for _, want := range wants { | 
 | 		if !strings.Contains(output, want) { | 
 | 			t.Errorf("output:\n%s\n\nwant output containing: %s", output, want) | 
 | 		} | 
 | 	} | 
 | } |