Go version

go version go1.22.2 darwin/arm64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='arm64'
GOBIN=''
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOOS='darwin'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOVCS=''
GOVERSION='go1.22.2'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
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/s8/7ynb7ztx01l2vgsdm0fty3t80000gn/T/go-build27692839=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

package main

import (
    "fmt"
    "runtime"
    "strings"
)

func GetFuncName() string {
    pc := make([]uintptr, 10)
    runtime.Callers(2, pc)
    f := runtime.FuncForPC(pc[0])
    name := f.Name()
    res := strings.Split(name, ".")
    return res[len(res)-1]
}

func wrapper(name string) func() {
    return func() {
        // It should print "demo"
        fmt.Println("function name: ", name)
    }
}

func demo() {
    // name is "demo"
    name := GetFuncName()
    defer wrapper(name)()
}

func main() {
    demo()
}

What did you see happen?

run go build demo.go && ./demo, output: function name: wrapper run go build -gcflags "-N" demo.go && ./demo, output: function name: demo.

What did you expect to see?

The output is always demo.

Comment From: randall77

runtime.FuncForPC does not (and can't) handle inlining correctly. Use runtime.CallersFrames for optimization-independent traceback. That said, it is very strange that wrapper appears as the output. It is not on the stack at all when runtime.Callers is invoked.