diff options
Diffstat (limited to 'libgo/go/runtime/crash_test.go')
-rw-r--r-- | libgo/go/runtime/crash_test.go | 168 |
1 files changed, 157 insertions, 11 deletions
diff --git a/libgo/go/runtime/crash_test.go b/libgo/go/runtime/crash_test.go index 5f0e77b0dc3..a2f7ff7dec8 100644 --- a/libgo/go/runtime/crash_test.go +++ b/libgo/go/runtime/crash_test.go @@ -5,6 +5,7 @@ package runtime_test import ( + "bytes" "fmt" "internal/testenv" "io/ioutil" @@ -13,9 +14,11 @@ import ( "path/filepath" "regexp" "runtime" + "strconv" "strings" "sync" "testing" + "time" ) var toRemove []string @@ -65,11 +68,48 @@ func runTestProg(t *testing.T, binary, name string) string { if err != nil { t.Fatal(err) } - got, _ := testEnv(exec.Command(exe, name)).CombinedOutput() - return string(got) + + cmd := testEnv(exec.Command(exe, name)) + var b bytes.Buffer + cmd.Stdout = &b + cmd.Stderr = &b + if err := cmd.Start(); err != nil { + t.Fatalf("starting %s %s: %v", binary, name, err) + } + + // If the process doesn't complete within 1 minute, + // assume it is hanging and kill it to get a stack trace. + p := cmd.Process + done := make(chan bool) + go func() { + scale := 1 + // This GOARCH/GOOS test is copied from cmd/dist/test.go. + // TODO(iant): Have cmd/dist update the environment variable. + if runtime.GOARCH == "arm" || runtime.GOOS == "windows" { + scale = 2 + } + if s := os.Getenv("GO_TEST_TIMEOUT_SCALE"); s != "" { + if sc, err := strconv.Atoi(s); err == nil { + scale = sc + } + } + + select { + case <-done: + case <-time.After(time.Duration(scale) * time.Minute): + p.Signal(sigquit) + } + }() + + if err := cmd.Wait(); err != nil { + t.Logf("%s %s exit status: %v", binary, name, err) + } + close(done) + + return b.String() } -func buildTestProg(t *testing.T, binary string) (string, error) { +func buildTestProg(t *testing.T, binary string, flags ...string) (string, error) { checkStaleRuntime(t) testprog.Lock() @@ -86,23 +126,27 @@ func buildTestProg(t *testing.T, binary string) (string, error) { if testprog.target == nil { testprog.target = make(map[string]buildexe) } - target, ok := testprog.target[binary] + name := binary + if len(flags) > 0 { + name += "_" + strings.Join(flags, "_") + } + target, ok := testprog.target[name] if ok { return target.exe, target.err } - exe := filepath.Join(testprog.dir, binary+".exe") - cmd := exec.Command("go", "build", "-o", exe) + exe := filepath.Join(testprog.dir, name+".exe") + cmd := exec.Command("go", append([]string{"build", "-o", exe}, flags...)...) cmd.Dir = "testdata/" + binary out, err := testEnv(cmd).CombinedOutput() if err != nil { exe = "" - target.err = fmt.Errorf("building %s: %v\n%s", binary, err, out) - testprog.target[binary] = target + target.err = fmt.Errorf("building %s %v: %v\n%s", binary, flags, err, out) + testprog.target[name] = target return "", target.err } target.exe = exe - testprog.target[binary] = target + testprog.target[name] = target return exe, nil } @@ -273,6 +317,52 @@ func TestGoexitInPanic(t *testing.T) { } } +// 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 interface{}) { + defer func() { + recovered = recover() + }() + fn() + return +} + func TestPanicAfterGoexit(t *testing.T) { // an uncaught panic should still work after goexit output := runTestProg(t, "testprog", "PanicAfterGoexit") @@ -294,9 +384,9 @@ func TestRecoverBeforePanicAfterGoexit(t *testing.T) { // 1. defer a function that recovers // 2. defer a function that panics // 3. call goexit - // Goexit should run the #2 defer. Its panic + // Goexit should run the #2 defer. Its panic // should be caught by the #1 defer, and execution - // should resume in the caller. Like the Goexit + // should resume in the caller. Like the Goexit // never happened! defer func() { r := recover() @@ -336,3 +426,59 @@ func TestPanicTraceback(t *testing.T) { 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 TestMemPprof(t *testing.T) { + testenv.MustHaveGoRun(t) + + exe, err := buildTestProg(t, "testprog") + if err != nil { + t.Fatal(err) + } + + got, err := testEnv(exec.Command(exe, "MemProf")).CombinedOutput() + if err != nil { + t.Fatal(err) + } + fn := strings.TrimSpace(string(got)) + defer os.Remove(fn) + + cmd := testEnv(exec.Command("go", "tool", "pprof", "-alloc_space", "-top", exe, 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", top) + if err != nil { + t.Fatal(err) + } + + if !bytes.Contains(top, []byte("MemProf")) { + t.Error("missing MemProf in pprof output") + } +} |