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.Caller return frame of bad function #39342

Closed
rekby opened this issue Jun 1, 2020 · 3 comments
Closed

runtime.Caller return frame of bad function #39342

rekby opened this issue Jun 1, 2020 · 3 comments

Comments

@rekby
Copy link

rekby commented Jun 1, 2020

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

$ go version
go version go1.14.3 darwin/amd64

Does this issue reproduce with the latest release?

yes

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

darwin, amd64

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/rekby/Library/Caches/go-build"
GOENV="/Users/rekby/Library/Application Support/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GONOPROXY="*.yandex-team.ru,git.1gb.ru"
GONOSUMDB="*.yandex-team.ru,git.1gb.ru"
GOOS="darwin"
GOPATH="/Users/rekby/go"
GOPRIVATE="*.yandex-team.ru,git.1gb.ru"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/Cellar/go/1.14.3/libexec"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/Cellar/go/1.14.3/libexec/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/rekby/projects/rekby/tmp-go/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/8_/68c7r6rj6xg4fpjls__qtsjn18s1hz/T/go-build670439081=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

run go test

main_test.go
// main_test.go
package main

import (
"github.com/stretchr/testify/require"
"runtime"
"testing"
)

func TestCaller(t *testing.T) {
at := require.New(t)
var name string
func() {
name = getNameCaller(t)
}()
//t.Log(name)
at.True(hasSuffix(t, name, ".TestCaller"))
}

func TestCallers(t *testing.T) {
at := require.New(t)
var name string
func() {
name = getNameCallers(t)
}()
//t.Log(name)
at.True(hasSuffix(t, name, ".TestCallers"))
}

func hasSuffix(t *testing.T, s, suffix string) bool {
// copy from strings.hasSuffix, add t.Logf
a := len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
t.Logf("%v: '%v' '%v'", a, s, suffix)
return a
}

func getNameCaller(t *testing.T) string {
caller, _, _, _ := runtime.Caller(2)
frames := runtime.CallersFrames([]uintptr{caller})
frame, _ := frames.Next()
return frame.Function
}

func getNameCallers(t *testing.T) string {
callers := make([]uintptr, 100)
callers = callers[:runtime.Callers(3, callers)]
frames := runtime.CallersFrames(callers)
frame, _ := frames.Next()
return frame.Function
}

go.mod
module tmp

go 1.14

require github.com/stretchr/testify v1.6.0

go.sum
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

(error repeated if I clear go.mod, go.sum and re-init module)

What did you expect to see?

PASS

What did you see instead?

--- FAIL: TestCaller (0.00s)
    main_test.go:33: false: 'github.com/stretchr/testify/require.New' '.TestCaller'
    main_test.go:17:
        	Error Trace:	main_test.go:17
        	Error:      	Should be true
        	Test:       	TestCaller
FAIL
exit status 1
FAIL	tmp	0.102s

Test pass if I uncomment log in TestCaller (line 16//t.Log(name))
Test pass if I remove at := require.New(t) and use require.True(t, ...) instead.

@davecheney
Copy link
Contributor

This is related to inlining, try printing out the string who’s suffix does not match, that will give you a clue why the frame was off by one

@rekby
Copy link
Author

rekby commented Jun 1, 2020

@davecheney Yes, I understand about it is inline side effect.

Test out print strings:
I have pointer to 'github.com/stretchr/testify/require.New' func from Caller.

I surprised because runtime.Caller and runtime.Callers have different result for same situation.

Internal Caller(2) and Callers(3) run intenal callers(3), but have different result.

If the difference is expected - I think need write it in documentations and write in documentation about what of function have more precision.

@randall77
Copy link
Contributor

runtime.CallersFrames must take the result of runtime.Callers. The slice you are constructing from the result of runtime.Caller is not an allowed input.

You should use runtime.FuncForPC with the pc result from runtime.Caller.

More generally, runtime.Caller is an old API which does not take into account inlining correctly. You should use runtime.Callers + runtime.CallersFrames whenever possible.

I'm going to close, as this isn't a bug. If you have concrete suggestions for improving the documentation, please reopen and include those suggestions (or send a CL).

@golang golang locked and limited conversation to collaborators Jun 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants