blob: a55145fa15750bc2b751af4cdd5c93880b398c71 [file] [log] [blame]
// 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)
}
}