From 26d6dc6bf8a7e5487844a63aa26a4de3afdd688e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 14 Jun 2016 14:38:22 -0700 Subject: [PATCH] runtime: if the test program hangs, try to get a stack trace This is an attempt to get more information for #14809, which seems to occur rarely. Updates #14809. Change-Id: Idbeb136ceb57993644e03266622eb699d2685d02 Reviewed-on: https://go-review.googlesource.com/24110 Reviewed-by: Mikio Hara Reviewed-by: Austin Clements --- src/runtime/crash_nonunix_test.go | 13 +++++++++ src/runtime/crash_test.go | 44 +++++++++++++++++++++++++++++-- src/runtime/crash_unix_test.go | 4 +++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/runtime/crash_nonunix_test.go diff --git a/src/runtime/crash_nonunix_test.go b/src/runtime/crash_nonunix_test.go new file mode 100644 index 0000000000..2ce995c069 --- /dev/null +++ b/src/runtime/crash_nonunix_test.go @@ -0,0 +1,13 @@ +// Copyright 2016 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. + +// +build windows plan9 nacl + +package runtime_test + +import "os" + +// sigquit is the signal to send to kill a hanging testdata program. +// On Unix we send SIGQUIT, but on non-Unix we only have os.Kill. +var sigquit = os.Kill diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go index ec740990dc..0b4a1f538a 100644 --- a/src/runtime/crash_test.go +++ b/src/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,8 +68,45 @@ 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, flags ...string) (string, error) { diff --git a/src/runtime/crash_unix_test.go b/src/runtime/crash_unix_test.go index 0a79661f1e..6e4d04bd20 100644 --- a/src/runtime/crash_unix_test.go +++ b/src/runtime/crash_unix_test.go @@ -19,6 +19,10 @@ import ( "testing" ) +// sigquit is the signal to send to kill a hanging testdata program. +// Send SIGQUIT to get a stack trace. +var sigquit = syscall.SIGQUIT + func TestCrashDumpsAllThreads(t *testing.T) { switch runtime.GOOS { case "darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris": -- 2.50.0