Skip to content

runtime/pprof: goroutine profile at debug=2 hides the start of the finalizer goroutine #73011

@gavraz

Description

@gavraz

Go version

1.23

Output of go env in your module/workspace:

GO111MODULE='on'
GOARCH='arm64'
GOBIN='/usr/local/bin/'
GOCACHE='/Users/user/Library/Caches/go-build'
GOENV='/Users/user/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/user/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/user/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.7'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/user/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/user/go/projects/github/cyolosecurity/cyolo-core/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/sk/l0sxrtq14h9ck0kv43dns5_h0000gn/T/go-build346811353=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

Following this discussion I run a program with a blocking finalizer and compared the output of the goroutine profile (debug=1, debug=2).

package main

import (
	"fmt"
	"net/http"
	_ "net/http/pprof"
	"runtime"
	"sync"
	"time"
)

func main() {
	go func() {
		addr := fmt.Sprintf("0.0.0.0:%d", 6060)
		fmt.Printf("Starting PPROF: http://%s/debug/pprof/\n", addr)
		fmt.Printf("PPROF stopped: %s\n", http.ListenAndServe(addr, nil).Error())
	}()

	fmt.Println("Blocking finalizer forever...")
	mu := sync.Mutex{}
	mu.Lock()
	x := new([16]byte)
	runtime.SetFinalizer(x, func(_ *[16]byte) {
		mu.Lock()
	})
	runtime.KeepAlive(x)
	x = nil
	runtime.GC()

	for {
		time.Sleep(time.Second)
	}
}

What did you see happen?

debug=1

1 @ 0x102ed7c58 0x102eb5728 0x102eb5705 0x102ed90b8 0x102ef4744 0x1030af354 0x1030af301 0x102e7ce94 0x102edffc4
#	0x102ed90b7	sync.runtime_SemacquireMutex+0x27	/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/runtime/sema.go:95
#	0x102ef4743	sync.(*Mutex).lockSlow+0x173		/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/sync/mutex.go:173
#	0x1030af353	sync.(*Mutex).Lock+0x73			/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/sync/mutex.go:92
#	0x1030af300	main.main.func2+0x20			/Users/user/Library/Application Support/JetBrains/GoLand2024.3/scratches/scratch_71.go:24
#	0x102e7ce93	runtime.runfinq+0x3e3			/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/runtime/mfinal.go:255

debug=2

goroutine 33 [sync.Mutex.Lock]:
sync.runtime_SemacquireMutex(0x0?, 0x0?, 0x0?)
	/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/runtime/sema.go:95 +0x28
sync.(*Mutex).lockSlow(0x14000114108)
	/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/sync/mutex.go:173 +0x174
sync.(*Mutex).Lock(...)
	/Users/user/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.23.7.darwin-arm64/src/sync/mutex.go:92
main.main.func2(0x0?)
	/Users/user/Library/Application Support/JetBrains/GoLand2024.3/scratches/scratch_71.go:24 +0x74

What did you expect to see?

We anticipated that the debug=2 goroutine trace would include the runtime.runfinq function, showing the path to the finalizer's routine, just as it does in debug=1. The absence of this trace in debug=2 during our blocked finalizer debugging made identifying the root cause significantly more challenging as we found no occurrences of mfinal.go in the trace.

Metadata

Metadata

Assignees

Labels

BugReportIssues describing a possible bug in the Go implementation.NeedsFixThe path to resolution is known, but the work has not been done.compiler/runtimeIssues related to the Go compiler and/or runtime.

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions