函数的调用信息是程序中比较重要运行期信息, 在很多场合都会用到(比如调试或日志).

runtimeruntime.Callerruntime.Callersruntime.FuncForPC

这几个函数的文档链接:

本文主要讲述这几个函数的用法.

runtime.Caller

函数的签名如下:

runtime.Callergoroutinepcokfalse
skip0runtime.Caller
runtime.Callerruntime.Callersskip

下面是一个简单的例子, 打印函数调用的栈帧信息:

skip = 0main.main
skip = 1skip = 2runtime/proc.cruntime.mainruntime.goexit

整理之后可以知道, Go的普通程序的启动顺序如下:

runtime.goexitmain.mainruntime.goexitruntime.mainruntime.mainmain.main
runtime.Callers

函数的签名如下:

runtime.Callersruntime.Callers
runtime.Callerspcskipruntime.Callerspc
runtime.Callerspc
pcskipskip = 0runtime.Callers
runtime.Callersruntime.Callers
runtime.Callersruntime.Caller
runtime.Callersruntime.Caller
pc
42809624290608pcruntime.mainruntime.goexit
runtime.Caller4198456runtime.Callers4198635pc
runtime.Callers4305334runtime.Callers
pc
runtime.FuncForPC

函数的签名如下:

runtime.FuncForPCpcpcnil
runtime.Func.FileLinepcpc
runtime.Func.Entryruntime.Func.Name
runtime.FuncForPC
pc0runtime.Func.FileLineruntime.Callerruntime.Func.FileLinepc
CallerName
CallerNameCallerName

函数实现如下:

runtime.Callerskip + 1CallerName
CallerName

这样就可以方便的输出函数调用者的信息了.

Go语言中函数的类型

在Go语言中, 除了语言定义的普通函数调用外, 还有闭包函数/init函数/全局变量初始化等不同的函数调用类型.

PrintCallerName

观察输出结果, 可以发现以下几个规律:

main.initinitmain.init·1main.init·2main.func·001
main.init
initmain.init·1main.init·2
001002003
CallerName
CallerName
init
CallerNameinitinit

处理的思路:

init"init·d+$"initfunc"func·d+$"
CallerName

有以下的代码:

myInitinitmainmyInit
myInitmain.myInitmain.myInitmain.func·???main.myInit
callerNamemain.func·???main.funcgettext.Gettext
callerName

不同Go程序启动流程

基于函数调用者信息可以很容易的验证各种环境的程序启动流程.

caller
caller/main.go

分析输出数据我们可以发现, 测试代码和例子代码的启动流程和普通的程序流程都不太一样.

测试代码的启动流程:

runtime.goexitruntime.goexitruntime.maintesting.tRunnertesting.tRunnergo test

例子代码的启动流程:

runtime.goexitruntime.goexitruntime.mainruntime.maingo testmain.main_test/_testmain.gotesting.Main
main.mainmainmainmainmain.mainmain.main

2015.06.09补充: 更深入的可以看下这个文章 GO语解惑:从源码分析GO程序的入口

总结

runtimeruntime.Callerruntime.Callersruntime.FuncForPC

这几个函数不仅可以解决一些实际的工程问题(比如 gettext-go 中用于获取翻译的上下文信息), 而且非常适合用于调试和分析各种Go程序的运行时信息.