From: qiulaidongfeng <2645477756@qq.com> Date: Tue, 17 Sep 2024 12:10:20 +0000 (+0800) Subject: runtime: correctly print panics before fatal-ing on defer X-Git-Tag: go1.26rc1~350 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=5cd1b73772;p=gostls13.git runtime: correctly print panics before fatal-ing on defer Fixes #67792 Change-Id: I93d16580cb31e54cee7c8490212404e4d0dec446 Reviewed-on: https://go-review.googlesource.com/c/go/+/613757 Reviewed-by: Keith Randall Reviewed-by: Keith Randall Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI Auto-Submit: Keith Randall --- diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 325bf74aa8..3c967a2999 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -1235,10 +1235,12 @@ func throw(s string) { // //go:nosplit func fatal(s string) { + p := getg()._panic // Everything fatal does should be recursively nosplit so it // can be called even when it's unsafe to grow the stack. printlock() // Prevent multiple interleaved fatal reports. See issue 69447. systemstack(func() { + printPreFatalDeferPanic(p) print("fatal error: ") printindented(s) // logically printpanicval(s), but avoids convTstring write barrier print("\n") @@ -1248,6 +1250,27 @@ func fatal(s string) { printunlock() } +// printPreFatalDeferPanic prints the panic +// when fatal occurs in panics while running defer. +func printPreFatalDeferPanic(p *_panic) { + // Don`t call preprintpanics, because + // don't want to call String/Error on the panicked values. + // When we fatal we really want to just print and exit, + // no more executing user Go code. + for x := p; x != nil; x = x.link { + if x.link != nil && *efaceOf(&x.link.arg) == *efaceOf(&x.arg) { + // This panic contains the same value as the next one in the chain. + // Mark it as repanicked. We will skip printing it twice in a row. + x.link.repanicked = true + } + } + if p != nil { + printpanics(p) + // make fatal have the same indentation as non-first panics. + print("\t") + } +} + // runningPanicDefers is non-zero while running deferred functions for panic. // This is used to try hard to get a panic stack trace out when exiting. var runningPanicDefers atomic.Uint32 diff --git a/src/runtime/panic_test.go b/src/runtime/panic_test.go index 4a30c30db6..2b06bce45d 100644 --- a/src/runtime/panic_test.go +++ b/src/runtime/panic_test.go @@ -34,6 +34,8 @@ func TestPanicWithDirectlyPrintableCustomTypes(t *testing.T) { {"panicCustomUint32", `panic: main.MyUint32(93)`}, {"panicCustomUint64", `panic: main.MyUint64(93)`}, {"panicCustomUintptr", `panic: main.MyUintptr(93)`}, + {"panicDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\")\n\tfatal error: sync: unlock of unlocked mutex"}, + {"panicDoublieDeferFatal", "panic: runtime.errorString(\"invalid memory address or nil pointer dereference\") [recovered, repanicked]\n\tfatal error: sync: unlock of unlocked mutex"}, } for _, tt := range tests { diff --git a/src/runtime/testdata/testprog/panicprint.go b/src/runtime/testdata/testprog/panicprint.go index 4ce958ba3d..eaf3ba2c33 100644 --- a/src/runtime/testdata/testprog/panicprint.go +++ b/src/runtime/testdata/testprog/panicprint.go @@ -4,6 +4,8 @@ package main +import "sync" + type MyBool bool type MyComplex128 complex128 type MyComplex64 complex64 @@ -90,6 +92,23 @@ func panicCustomFloat32() { panic(MyFloat32(-93.70)) } +func panicDeferFatal() { + var mu sync.Mutex + defer mu.Unlock() + var i *int + *i = 0 +} + +func panicDoublieDeferFatal() { + var mu sync.Mutex + defer mu.Unlock() + defer func() { + panic(recover()) + }() + var i *int + *i = 0 +} + func init() { register("panicCustomComplex64", panicCustomComplex64) register("panicCustomComplex128", panicCustomComplex128) @@ -108,4 +127,6 @@ func init() { register("panicCustomUint32", panicCustomUint32) register("panicCustomUint64", panicCustomUint64) register("panicCustomUintptr", panicCustomUintptr) + register("panicDeferFatal", panicDeferFatal) + register("panicDoublieDeferFatal", panicDoublieDeferFatal) }