這篇文章主要參考了鳥窩的這篇文章趴腋,寫的很好,自己寫一篇防止遺忘
我們在函數(shù)有時想獲取函數(shù)的調(diào)用者信息或是整個調(diào)用棧论咏,這對于日志記錄是很有必要的优炬,但是這些函數(shù)的開銷會很大,使用時需要謹慎厅贪,相關(guān)函數(shù)如下
- func Caller(skip int) (pc uintptr, file string, line int, ok bool)
- func Callers(skip int, pc []uintptr) int
- func CallersFrames(callers []uintptr) *Frames
- func FuncForPC(pc uintptr) *Func
1. func Caller(skip int) (pc uintptr, file string, line int, ok bool)
這個函數(shù)接收一個skip參數(shù)蠢护,表示調(diào)用的深度,0表示調(diào)用Caller函數(shù)的那個函數(shù)养涮,根據(jù)skip返回四個值葵硕,pc是對應(yīng)函數(shù)的地址,file表示在那個文件单寂,line表示哪行調(diào)用的,ok表示存不存在這個skip對應(yīng)的結(jié)果
演示代碼如下
func main() {
pc, file, line, ok := runtime.Caller(0)
println(runtime.FuncForPC(pc).Name(), file, line, ok)
// main.main D:/Workstation/Goproject/LearningGo//get_function_names_in_go/demo.go 6 true
// 沒有100層深度的調(diào)用吐辙,所以返回0值
c, file, line, ok := runtime.Caller(100)
println(runtime.FuncForPC(pc).Name(), file, line, ok)
// 0 false
}
2. func Callers(skip int, pc []uintptr) int
剛剛的Caller只會返回制定skip的調(diào)用信息宣决,這里的Caller可以根據(jù)從你指定的skip開始,將它上面的所以調(diào)用信息獲取出來昏苏,存儲到pc中尊沸,最后返回的int表示獲取到了幾個調(diào)用信息,這里和鳥窩的文章有點不一樣, 鳥窩在文中寫道Callers由于歷史問題贤惯,skip為0時會返回Callers這個函數(shù)本身洼专,所以Callers的1才等于調(diào)用Callers的那個函數(shù),但是我在試的時候發(fā)現(xiàn)Callers這個函數(shù)被重復(fù)了兩遍孵构,不知道為什么屁商,代碼如下
func testCallers() {
pcs := make([]uintptr, 10)
n := runtime.Callers(0, pcs)
for i := 0; i < n; i++ {
f := runtime.FuncForPC(pcs[i])
file, line := f.FileLine(pcs[i])
fmt.Printf("%d %s:%d %s\n", i, file, line, f.Name())
}
}
func main() {
Bar()
}
// 下面的這些函數(shù)是為了添加調(diào)用棧的深度
func Bar() {
Foo()
}
func Foo() {
testCallers()
}
結(jié)果如下,不知道為什么runtime.Callers出現(xiàn)了一模一樣的兩次
3. func CallersFrames(callers []uintptr) * Frames
這個函數(shù)接收callers的切片颈墅,表示你想獲取的函數(shù)的地址蜡镶,然后會直接返回這些函數(shù)的信息雾袱,省的去一個個調(diào)用FuncForPC
func testCallersFrames() {
pc := make([]uintptr, 10) // at least 1 entry needed
n := runtime.Callers(0, pc)
frames := runtime.CallersFrames(pc[:n])
i := 0
for {
frame, more := frames.Next()
fmt.Printf("%d %s:%d %s\n", i, frame.File, frame.Line, frame.Function)
i++
if !more {
break
}
}
}
func main() {
Bar()
}
// 下面的這些函數(shù)是為了添加調(diào)用棧的深度
func Bar() {
Foo()
}
func Foo() {
// testCallers()
testCallersFrames()
}
結(jié)果如下
4. func FuncForPC(pc uintptr) *Func
FuncForPC 是一個有趣的函數(shù), 它可以把程序計數(shù)器地址對應(yīng)的函數(shù)的信息獲取出來官还。如果因為內(nèi)聯(lián)程序計數(shù)器對應(yīng)多個函數(shù)芹橡,它返回最外面的函數(shù)。
它的返回值是一個*Func類型的值望伦,通過*Func可以獲得函數(shù)地址林说、文件行、函數(shù)名等信息屯伞。
除了上面獲取程序計數(shù)器的方式腿箩,也可以通過反射的方式獲取函數(shù)的地址:
runtime.FuncForPC(reflect.ValueOf(foo).Pointer()).Name()