]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: correctly print panics before fatal-ing on defer
authorqiulaidongfeng <2645477756@qq.com>
Tue, 17 Sep 2024 12:10:20 +0000 (20:10 +0800)
committerGopher Robot <gobot@golang.org>
Fri, 7 Nov 2025 20:55:37 +0000 (12:55 -0800)
Fixes #67792

Change-Id: I93d16580cb31e54cee7c8490212404e4d0dec446
Reviewed-on: https://go-review.googlesource.com/c/go/+/613757
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Keith Randall <khr@golang.org>

src/runtime/panic.go
src/runtime/panic_test.go
src/runtime/testdata/testprog/panicprint.go

index 325bf74aa822b93adf71cf7d95ab83e38c12e74d..3c967a29997da39a7bbb04bb1f1644e31820e69c 100644 (file)
@@ -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
index 4a30c30db6e333ab710d2690ec1886cea47d87b2..2b06bce45d260e18778a78cb3dd180847297f837 100644 (file)
@@ -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 {
index 4ce958ba3dbacc9f1b972f80465d9eb38ecf6cd0..eaf3ba2c3371d2d6cf76cdeb52e9cbbd7f496206 100644 (file)
@@ -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)
 }