mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: when crash with panic, call user Error/String methods before freezing the world
Fixes #14432. Change-Id: I0a92ef86de95de39217df9a664d8034ef685a906 Reviewed-on: https://go-review.googlesource.com/19792 Reviewed-by: Ian Lance Taylor <iant@golang.org> Run-TryBot: Minux Ma <minux@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
bc8458ab02
commit
e960302410
5 changed files with 99 additions and 0 deletions
|
|
@ -178,3 +178,12 @@ func TestCgoCheckBytes(t *testing.T) {
|
|||
t.Errorf("cgo check too slow: got %v, expected at most %v", d1, d2*10)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCgoPanicDeadlock(t *testing.T) {
|
||||
// test issue 14432
|
||||
got := runTestProg(t, "testprogcgo", "CgoPanicDeadlock")
|
||||
want := "panic: cgo error\n\n"
|
||||
if !strings.HasPrefix(got, want) {
|
||||
t.Fatalf("output does not start with %q:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,3 +336,19 @@ 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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -333,6 +333,21 @@ func Goexit() {
|
|||
goexit1()
|
||||
}
|
||||
|
||||
// Call all Error and String methods before freezing the world.
|
||||
// Used when crashing with panicking.
|
||||
// This must match types handled by printany.
|
||||
func preprintpanics(p *_panic) {
|
||||
for p != nil {
|
||||
switch v := p.arg.(type) {
|
||||
case error:
|
||||
p.arg = v.Error()
|
||||
case stringer:
|
||||
p.arg = v.String()
|
||||
}
|
||||
p = p.link
|
||||
}
|
||||
}
|
||||
|
||||
// Print all currently active panics. Used when crashing.
|
||||
func printpanics(p *_panic) {
|
||||
if p.link != nil {
|
||||
|
|
@ -459,6 +474,10 @@ func gopanic(e interface{}) {
|
|||
}
|
||||
|
||||
// ran out of deferred calls - old-school panic now
|
||||
// Because it is unsafe to call arbitrary user code after freezing
|
||||
// the world, we call preprintpanics to invoke all necessary Error
|
||||
// and String methods to prepare the panic strings before startpanic.
|
||||
preprintpanics(gp._panic)
|
||||
startpanic()
|
||||
printpanics(gp._panic)
|
||||
dopanic(0) // should not return
|
||||
|
|
|
|||
25
src/runtime/testdata/testprog/deadlock.go
vendored
25
src/runtime/testdata/testprog/deadlock.go
vendored
|
|
@ -30,6 +30,8 @@ func init() {
|
|||
register("PanicAfterGoexit", PanicAfterGoexit)
|
||||
register("RecoveredPanicAfterGoexit", RecoveredPanicAfterGoexit)
|
||||
register("PanicTraceback", PanicTraceback)
|
||||
register("GoschedInPanic", GoschedInPanic)
|
||||
register("SyscallInPanic", SyscallInPanic)
|
||||
}
|
||||
|
||||
func SimpleDeadlock() {
|
||||
|
|
@ -152,6 +154,29 @@ func GoexitInPanic() {
|
|||
runtime.Goexit()
|
||||
}
|
||||
|
||||
type errorThatGosched struct{}
|
||||
|
||||
func (errorThatGosched) Error() string {
|
||||
runtime.Gosched()
|
||||
return "errorThatGosched"
|
||||
}
|
||||
|
||||
func GoschedInPanic() {
|
||||
panic(errorThatGosched{})
|
||||
}
|
||||
|
||||
type errorThatPrint struct{}
|
||||
|
||||
func (errorThatPrint) Error() string {
|
||||
fmt.Println("1")
|
||||
fmt.Println("2")
|
||||
return "3"
|
||||
}
|
||||
|
||||
func SyscallInPanic() {
|
||||
panic(errorThatPrint{})
|
||||
}
|
||||
|
||||
func PanicAfterGoexit() {
|
||||
defer func() {
|
||||
panic("hello")
|
||||
|
|
|
|||
30
src/runtime/testdata/testprogcgo/deadlock.go
vendored
Normal file
30
src/runtime/testdata/testprogcgo/deadlock.go
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
/*
|
||||
char *geterror() {
|
||||
return "cgo error";
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("CgoPanicDeadlock", CgoPanicDeadlock)
|
||||
}
|
||||
|
||||
type cgoError struct{}
|
||||
|
||||
func (cgoError) Error() string {
|
||||
fmt.Print("") // necessary to trigger the deadlock
|
||||
return C.GoString(C.geterror())
|
||||
}
|
||||
|
||||
func CgoPanicDeadlock() {
|
||||
panic(cgoError{})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue