57

Is it possible get information about caller function in Golang? For example if I have

func foo() {
    //Do something
}
func main() {
    foo() 
}

How can I get that foo has been called from main?
I'm able to this in other language (for example in C# I just need to use CallerMemberName class attribute)

wasmup
  • 14,541
  • 6
  • 42
  • 58
Tinwor
  • 7,765
  • 6
  • 35
  • 56
  • yes, it's possible - see https://golang.org/pkg/runtime/#Callers – Not_a_Golfer Feb 04 '16 at 22:39
  • yes, but although it is possible it usually shows bad design decision. Except debug purpose it does not make sense. All necessary information should be passed as function arguments, or like closure variables. – lofcek Feb 04 '16 at 22:45
  • 1
    For an example, check out how the stretchr/testify library does it: https://github.com/stretchr/testify/blob/v1.1.3/assert/assertions.go#L73 – Tim Allclair Feb 04 '16 at 22:51

2 Answers2

120

You can use runtime.Caller for easily retrieving information about the caller:

func Caller(skip int) (pc uintptr, file string, line int, ok bool)

Example #1: Print caller file name and line number: https://play.golang.org/p/cdO4Z4ApHS

package main

import (
    "fmt"
    "runtime"
)

func foo() {
    _, file, no, ok := runtime.Caller(1)
    if ok {
        fmt.Printf("called from %s#%d\n", file, no)
    }
}

func main() {
    foo()
}

Example #2: Get more information with runtime.FuncForPC: https://play.golang.org/p/y8mpQq2mAv

package main

import (
    "fmt"
    "runtime"
)

func foo() {
    pc, _, _, ok := runtime.Caller(1)
    details := runtime.FuncForPC(pc)
    if ok && details != nil {
        fmt.Printf("called from %s\n", details.Name())
    }
}

func main() {
    foo()
}
svenwltr
  • 17,002
  • 12
  • 56
  • 68
32

expanding on my comment, here's some code that returns the current func's caller

import(
    "fmt"
    "runtime"
)

func getFrame(skipFrames int) runtime.Frame {
    // We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
    targetFrameIndex := skipFrames + 2

    // Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
    programCounters := make([]uintptr, targetFrameIndex+2)
    n := runtime.Callers(0, programCounters)

    frame := runtime.Frame{Function: "unknown"}
    if n > 0 {
        frames := runtime.CallersFrames(programCounters[:n])
        for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
            var frameCandidate runtime.Frame
            frameCandidate, more = frames.Next()
            if frameIndex == targetFrameIndex {
                frame = frameCandidate
            }
        }
    }

    return frame
}

// MyCaller returns the caller of the function that called it :)
func MyCaller() string {
        // Skip GetCallerFunctionName and the function to get the caller of
        return getFrame(2).Function
}

// foo calls MyCaller
func foo() {
    fmt.Println(MyCaller())
}

// bar is what we want to see in the output - it is our "caller"
func bar() {
    foo()
}

func main(){
    bar()
}

For more examples: https://play.golang.org/p/cv-SpkvexuM

Bo Sunesen
  • 911
  • 1
  • 7
  • 9
Not_a_Golfer
  • 47,012
  • 14
  • 126
  • 92
  • I'm probably being daft, but why are you subtracting 1 from the fpc in `runtime.FuncForPC`? – Femaref Dec 12 '16 at 09:50
  • @Femaref TBH I don't remember :) there shouldn't be a reason to, but this is copied off of a working piece of code. – Not_a_Golfer Dec 12 '16 at 09:57
  • 1
    @Femaref: Per the [godoc on runtime.Callers](https://golang.org/pkg/runtime/#Callers), you would need to subtract because it "will normally return the file and line number of the instruction immediately following the call". The doc also suggests not using this function for this purpose, and instead using runtime.Frames. – Zach Jan 03 '17 at 22:21
  • 1
    Rather than use the result of runtime.Callers, runtime.CallersFrames should be used as shown here: https://stackoverflow.com/a/46289376/5250939 and described in the doc: https://godoc.org/runtime#Callers – Bo Sunesen Jan 07 '19 at 10:21
  • @BoSunesen this answer predates CallersFrames, feel free to suggest an edit. – Not_a_Golfer Jan 07 '19 at 11:48