| // 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", func() { FinalizerOrCleanupDeadlock(false) }) |
| register("CleanupDeadlock", func() { FinalizerOrCleanupDeadlock(true) }) |
| } |
| |
| func FinalizerOrCleanupDeadlock(useCleanup bool) { |
| flag.Parse() |
| |
| started := make(chan struct{}) |
| fn := func() { |
| started <- struct{}{} |
| select {} |
| } |
| b := new([16]byte) |
| if useCleanup { |
| runtime.AddCleanup(b, func(struct{}) { fn() }, struct{}{}) |
| } else { |
| runtime.SetFinalizer(b, func(*[16]byte) { fn() }) |
| } |
| 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) |
| } |
| } |