Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: FuncForPC FileLine returns autogenerated instead of actual path #51774

Open
vearutop opened this issue Mar 17, 2022 · 13 comments
Open
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@vearutop
Copy link
Contributor

What version of Go are you using (go version)?

$ go version
go version go1.18 darwin/amd64

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/vearutop/Library/Caches/go-build"
GOENV="/Users/vearutop/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/vearutop/go/pkg/mod"
GONOPROXY="github.com/adjust,github.com/vearutop,github.com/Unbotify"
GONOSUMDB="github.com/adjust,github.com/vearutop,github.com/Unbotify"
GOOS="darwin"
GOPATH="/Users/vearutop/go"
GOPRIVATE="github.com/adjust,github.com/vearutop,github.com/Unbotify"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/Users/vearutop/sdk/go1.18"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/Users/vearutop/sdk/go1.18/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.18"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/vearutop/gohack/github.com/cucumber/godog/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/n7/jv2b0zv57jzc8y6zhmfzbn7c0000gn/T/go-build1890007164=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

I was getting file and line of struct method.

https://go.dev/play/p/VFs8FKAkTLf

package main

import (
	"reflect"
	"runtime"
)

type s struct{}

func (tc *s) f() {}

func main() {
	tc := &s{}

	v := reflect.ValueOf(tc.f)
	ptr := v.Pointer()
	f := runtime.FuncForPC(ptr)
	file, line := f.FileLine(f.Entry())
	name := f.Name()

	println(file, line, name)
}

What did you expect to see?

.../main.go 10 main.(*s).f-fm

What did you see instead?

<autogenerated> 1 main.(*s).f-fm

The issue is reproducible with go1.18 and gotip, however go1.18rc1 and below (go1.17) works as expected.

@heschi
Copy link
Contributor

heschi commented Mar 18, 2022

cc @golang/runtime

@heschi heschi added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 18, 2022
@heschi heschi added this to the Go1.19 milestone Mar 18, 2022
@jakebailey
Copy link

jakebailey commented Mar 18, 2022

Bisects to CL 388794 (I was curious).

@ianlancetaylor
Copy link
Contributor

CC @cherrymui

@cherrymui
Copy link
Member

I'm not really sure what the right answer to be here. Technically this function is indeed autogenerated, as it is a closure capturing the receiver. This is also seen in the synthetic name (-fm). So I would expect a synthetic location. Both the runtime and reflect packages' documentation doesn't mention what is expected in this case.

We may be able to restore the old behavior, but I'm not sure that is best. If we want to give the location of the definition, perhaps we also want to give the original method name instead of the synthetic -fm name?

If you use the method that does not capture the receiver, like

v := reflect.ValueOf((*s).f)

It prints the actual line number. Would that be okay? Thanks.

@vearutop
Copy link
Contributor Author

In my case using (*s).f does not seem feasible, as I receive functions from library users and they may provide any kind. Later, I print file and line of that function to help users to diagnose the problems, unfortunately <autogenerated> is not helpful in such scenario.

If there is a workaround or a more reliable solution to resolve arbitrary function into a place of origin in code, I would be happy to use it.

@e-nikolov
Copy link

e-nikolov commented Apr 6, 2022

This is also affecting https://github.com/go-chai/chai, which is a http router extension that generates a swagger spec based on the new generics. It used to work with go1.18beta2.

The way I use it is

chai.Get(r, "/{id}", controller.SomeHandler)

runtime.FuncForPC then finds the file and line where SomeHandler is defined so it could use the AST and parse some swagger annotations from the comment above that function.

Now it returns <autogenerated> and the rest fails.

I wonder how Delve figures out the line numbers of functions when debugging and if it can be used for this sort of thing.

@ianlancetaylor
Copy link
Contributor

@cherrymui @randall77 What is the current status here? This issue is currently in the 1.19 milestone. Should it move to 1.20? To Backlog? Thanks.

@cherrymui
Copy link
Member

I'd think it behaves as intended, in the same spirit of #52809 . The method value is a compiler-generated closure, so it actually has a synthetic name and file path.

@cherrymui
Copy link
Member

I wonder how Delve figures out the line numbers of functions when debugging and if it can be used for this sort of thing.

The method value is a wrapper which immediately calls the actual method function, which has a real name and file path. At run time, when debugging you'll debug the actual function, not the wrapper. Similarly, runtime.Callers/runtime.CallersFrames will get the actual function name and file path if called within the actual function.

For controller.SomeHandler, assuming controller is of type T you can use T.SomeHandler to get the actual function (which is not a closure).

@cherrymui cherrymui modified the milestones: Go1.19, Backlog Jun 27, 2022
@vearutop
Copy link
Contributor Author

vearutop commented Jun 27, 2022

I'm not sure I fully understand technical implications here, but from my end-user perspective I see a breaking, backwards incompatible change that affects existing code. Lack of workaround makes it worse.

I'd be happy to use any other way to find out which file:line is the origin of closure. Using T.SomeHandler instead of controller.SomeHandler is not an option, since it only works for global state.

Instrumenting closures to retrieve runtime.Callers is not an option too, as it requires unnecessary complication of otherwise simple 3rd-party callback functions.

#52809 seems related, however it does not say anything about file:line.

Hopefully a compromise can be found, thanks.

@randall77
Copy link
Contributor

Unlike #52809 which is about the function name, we could report a reasonable file/line position, I think. The whole method would have the file/line of the declaration of the method it is wrapping.

In fact even the name we might be able to slice off a trailing -fm. The linker needs to have distinct names for the base function and its wrapper, but that doesn't mean FuncToPC needs to maintain that uniqueness with its Name call.

@arvidfm
Copy link

arvidfm commented Jun 29, 2022

I'm also banging my head against a wall trying to figure out a workaround for this, as our code for autogenerating our REST API documentation broke with the upgrade to 1.18. Is there any way to access the original function that the -fm function wraps?

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 7, 2022
@gopherbot
Copy link

Change https://go.dev/cl/416455 mentions this issue: cmd/compile,runtime: use better line numbers and names for wrappers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
Status: Triage Backlog
Status: No status
Development

No branches or pull requests

9 participants