aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/testdata
diff options
context:
space:
mode:
authorMichael Pratt <mpratt@google.com>2025-03-24 03:08:33 -0400
committerGopher Robot <gobot@golang.org>2025-03-24 17:15:19 -0700
commit9c88db5f1eba68999184bb043a0b339349b81db4 (patch)
tree7a8a73c1624269ecf61cef4285badd602e20578a /src/runtime/testdata
parentaaf9b46800fe12c11c17bffebc82436204a1e85b (diff)
downloadgo-9c88db5f1eba68999184bb043a0b339349b81db4.tar.xz
runtime: always show runfinq in traceback
Today, runtime.runfinq is hidden whenever runtime frames are hidden. However this frame serves as a hint that this goroutine is running finalizers, which is otherwise unclear, but can be useful when debugging issues with finalizers. Fixes #73011. Change-Id: I6a6a636cb63951fbe1fefc3554fe9cea5d0a0fb6 Reviewed-on: https://go-review.googlesource.com/c/go/+/660295 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Michael Pratt <mpratt@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/runtime/testdata')
-rw-r--r--src/runtime/testdata/testprog/finalizer_deadlock.go74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/runtime/testdata/testprog/finalizer_deadlock.go b/src/runtime/testdata/testprog/finalizer_deadlock.go
new file mode 100644
index 0000000000..a55145fa15
--- /dev/null
+++ b/src/runtime/testdata/testprog/finalizer_deadlock.go
@@ -0,0 +1,74 @@
+// Copyright 2025 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
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "runtime"
+ "runtime/pprof"
+)
+
+var finalizerDeadlockMode = flag.String("finalizer-deadlock-mode", "panic", "Trigger mode of FinalizerDeadlock")
+
+func init() {
+ register("FinalizerDeadlock", FinalizerDeadlock)
+}
+
+func FinalizerDeadlock() {
+ flag.Parse()
+
+ started := make(chan struct{})
+ b := new([16]byte)
+ runtime.SetFinalizer(b, func(*[16]byte) {
+ started <- struct{}{}
+ select {}
+ })
+ b = nil
+
+ runtime.GC()
+
+ <-started
+ // We know the finalizer has started running. The goroutine might still
+ // be running or it may now be blocked. Either is fine, the goroutine
+ // should appear in stacks either way.
+
+ mode := os.Getenv("GO_TEST_FINALIZER_DEADLOCK")
+ switch mode {
+ case "panic":
+ panic("panic")
+ case "stack":
+ buf := make([]byte, 4096)
+ for {
+ n := runtime.Stack(buf, true)
+ if n >= len(buf) {
+ buf = make([]byte, 2*len(buf))
+ continue
+ }
+ buf = buf[:n]
+ break
+ }
+ fmt.Printf("%s\n", string(buf))
+ case "pprof_proto":
+ if err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 0); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing profile: %v\n", err)
+ os.Exit(1)
+ }
+ case "pprof_debug1":
+ if err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 1); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing profile: %v\n", err)
+ os.Exit(1)
+ }
+ case "pprof_debug2":
+ if err := pprof.Lookup("goroutine").WriteTo(os.Stdout, 2); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing profile: %v\n", err)
+ os.Exit(1)
+ }
+ default:
+ fmt.Fprintf(os.Stderr, "Unknown mode %q. GO_TEST_FINALIZER_DEADLOCK must be one of panic, stack, pprof_proto, pprof_debug1, pprof_debug2\n", mode)
+ os.Exit(1)
+ }
+}