]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: don't duplicate reraised panic values in printpanics
authorDamien Neil <dneil@google.com>
Fri, 31 Jan 2025 23:03:15 +0000 (15:03 -0800)
committerDamien Neil <dneil@google.com>
Thu, 6 Feb 2025 20:02:14 +0000 (12:02 -0800)
Change the output printed when crashing with a reraised panic value
to not duplicate that value.

Changes output of panicking with "PANIC", recovering, and reraising
from:

  panic: PANIC [recovered]
    panic: PANIC

to:

  panic: PANIC [recovered, reraised]

Fixes #71517

Change-Id: Id59938c4ea0df555b851ffc650fe6f94c0845499
Reviewed-on: https://go-review.googlesource.com/c/go/+/645916
Reviewed-by: Michael Knyszek <mknyszek@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

doc/next/4-runtime.md
src/runtime/crash_test.go
src/runtime/panic.go
src/runtime/runtime2.go
src/runtime/testdata/testprog/crash.go

index 1f8e445e0b10dec62dbb8c5f06d35ece0db1cfdd..28483eb519d3423489383716ea4573437d625fb0 100644 (file)
@@ -1 +1,18 @@
 ## Runtime {#runtime}
+
+<!-- go.dev/issue/71517 -->
+
+The message printed when a program exits due to an unhandled panic
+that was recovered and re-raised no longer repeats the text of
+the panic value.
+
+Previously, a program which panicked with `panic("PANIC")`,
+recovered the panic, and then re-panicked with the original
+value would print:
+
+    panic: PANIC [recovered]
+      panic: PANIC
+
+This program will now print:
+
+    panic: PANIC [recovered, reraised]
index 236c32ea34f7dea7443e65229ee4ca6e54b79157..c390218355c57f9f0a1263ab0d49ae06cbd2d29f 100644 (file)
@@ -356,6 +356,37 @@ panic: third panic
 
 }
 
+func TestReraisedPanic(t *testing.T) {
+       output := runTestProg(t, "testprog", "ReraisedPanic")
+       want := `panic: message [recovered, reraised]
+`
+       if !strings.HasPrefix(output, want) {
+               t.Fatalf("output does not start with %q:\n%s", want, output)
+       }
+}
+
+func TestReraisedMiddlePanic(t *testing.T) {
+       output := runTestProg(t, "testprog", "ReraisedMiddlePanic")
+       want := `panic: inner [recovered]
+       panic: middle [recovered, reraised]
+       panic: outer
+`
+       if !strings.HasPrefix(output, want) {
+               t.Fatalf("output does not start with %q:\n%s", want, output)
+       }
+}
+
+func TestReraisedPanicSandwich(t *testing.T) {
+       output := runTestProg(t, "testprog", "ReraisedPanicSandwich")
+       want := `panic: outer [recovered]
+       panic: inner [recovered]
+       panic: outer
+`
+       if !strings.HasPrefix(output, want) {
+               t.Fatalf("output does not start with %q:\n%s", want, output)
+       }
+}
+
 func TestGoexitCrash(t *testing.T) {
        // External linking brings in cgo, causing deadlock detection not working.
        testenv.MustInternalLink(t, false)
index 3ffb3966d026c8d43220c0fb45ff92e355f485b4..2dd3c3c2dbfe0dafad91704d61107f81eefd4bf2 100644 (file)
@@ -649,6 +649,13 @@ func preprintpanics(p *_panic) {
                }
        }()
        for p != nil {
+               if p.link != nil && *efaceOf(&p.link.arg) == *efaceOf(&p.arg) {
+                       // This panic contains the same value as the next one in the chain.
+                       // Mark it as reraised. We will skip printing it twice in a row.
+                       p.link.reraised = true
+                       p = p.link
+                       continue
+               }
                switch v := p.arg.(type) {
                case error:
                        p.arg = v.Error()
@@ -664,6 +671,9 @@ func preprintpanics(p *_panic) {
 func printpanics(p *_panic) {
        if p.link != nil {
                printpanics(p.link)
+               if p.link.reraised {
+                       return
+               }
                if !p.link.goexit {
                        print("\t")
                }
@@ -673,7 +683,9 @@ func printpanics(p *_panic) {
        }
        print("panic: ")
        printpanicval(p.arg)
-       if p.recovered {
+       if p.reraised {
+               print(" [recovered, reraised]")
+       } else if p.recovered {
                print(" [recovered]")
        }
        print("\n")
index e837c28af8715dbef7fd757ccc9e1712e37066d9..7280643f4835665c0c7ced98484ed32a8170779d 100644 (file)
@@ -1016,6 +1016,7 @@ type _panic struct {
        slotsPtr     unsafe.Pointer
 
        recovered   bool // whether this panic has been recovered
+       reraised    bool // whether this panic was reraised
        goexit      bool
        deferreturn bool
 }
index bdc395f652edcac538e04c502f6ff7788d87019a..56dd701ffb373cab497d2e90e838dc6dcb9c8ad4 100644 (file)
@@ -19,6 +19,9 @@ func init() {
        register("StringPanic", StringPanic)
        register("NilPanic", NilPanic)
        register("CircularPanic", CircularPanic)
+       register("ReraisedPanic", ReraisedPanic)
+       register("ReraisedMiddlePanic", ReraisedMiddlePanic)
+       register("ReraisedPanicSandwich", ReraisedPanicSandwich)
 }
 
 func test(name string) {
@@ -137,3 +140,52 @@ func (e exampleCircleEndError) Error() string {
 func CircularPanic() {
        panic(exampleCircleStartError{})
 }
+
+func ReraisedPanic() {
+       defer func() {
+               panic(recover())
+       }()
+       panic("message")
+}
+
+func ReraisedMiddlePanic() {
+       defer func() {
+               recover()
+               panic("outer")
+       }()
+       func() {
+               defer func() {
+                       panic(recover())
+               }()
+               func() {
+                       defer func() {
+                               recover()
+                               panic("middle")
+                       }()
+                       panic("inner")
+               }()
+       }()
+}
+
+// Panic sandwich:
+//
+//     panic("outer") =>
+//     recovered, panic("inner") =>
+//     panic(recovered outer panic value)
+//
+// Exercises the edge case where we reraise a panic value,
+// but with another panic in the middle.
+func ReraisedPanicSandwich() {
+       var outer any
+       defer func() {
+               recover()
+               panic(outer)
+       }()
+       func() {
+               defer func() {
+                       outer = recover()
+                       panic("inner")
+               }()
+               panic("outer")
+       }()
+}