| // 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) | |
| } | |
| } |