I have this below code that prints different program counter values depending on where its run from.
Code:
package main
import (
"fmt"
"runtime"
)
func foo() {
bar()
}
func bar() {
pcs := make([]uintptr, 10)
_ = runtime.Callers(0, pcs)
for _, pc := range pcs {
fmt.Printf("Value of pc %+v\n", runtime.FuncForPC(pc).Name())
}
}
func main() {
foo()
}
go run
or the compiled binary, it prints (main.bar
is missing)Value of pc runtime.Callers
Value of pc runtime.Callers
Value of pc main.main
Value of pc main.foo
Value of pc runtime.main
Value of pc runtime.goexit
Value of pc runtime.Callers
Value of pc main.bar
Value of pc main.foo
Value of pc main.main
Value of pc runtime.main
Value of pc runtime.goexit
foo
, bar
, both are missing)Value of pc runtime.Callers
Value of pc runtime.Callers
Value of pc main.main
Value of pc main.main
Value of pc runtime.main
Value of pc runtime.goexit
I'm using a framework (logrus) which relies on the PCs order to perform some operation (logging the filename).
Since the PC values keeps changing depending on where its run from, it works in Debug Mode but fails when running using go run
or the compiled binary.
Any idea what could be causing the PCs to load differently? Any config or optimization that's kicking in?
Documentation of runtime.Callers()
states:
To translate these PCs into symbolic information such as function names and line numbers, use CallersFrames. CallersFrames accounts for inlined functions and adjusts the return program counters into call program counters. Iterating over the returned slice of PCs directly is discouraged, as is using FuncForPC on any of the returned PCs, since these cannot account for inlining or return program counter adjustment.
Doc suggests to use runtime.CallersFrames()
to obtain function information from the raw counters which knows about and accounts for function inlining, for example:
pcs := make([]uintptr, 10)
n := runtime.Callers(0, pcs)
pcs = pcs[:n]
frames := runtime.CallersFrames(pcs)
for {
frame, more := frames.Next()
if !more {
break
}
fmt.Println("Function:", frame.Function)
}
This should output regardless of how you call / run it (try it on the Go Playground):
Function: runtime.Callers
Function: main.bar
Function: main.foo
Function: main.main
Function: runtime.main