我有这个示例代码https://play.golang.org/p/c_2GECIcrW
我期望 getFileName 会打印出类似 main.go:11 的内容,但我得到的是 asm_amd64p32.s:1014
在这种情况下,我该怎样做才能获得预期结果? 我可以存档并仍然使用匿名函数吗?
答案 0 :(得分:0)
您的期望不正确。
让我在此处粘贴您的代码以便于解释:
package main
import (
"fmt"
"path/filepath"
"runtime"
"time"
)
func main() {
getFileName(1)
time.Sleep(time.Hour)
}
func getFileName(shift int) {
go func() {
_, file, line, ok := runtime.Caller(shift)
if !ok {
file = "???"
line = 0
} else {
file = filepath.Base(file)
}
fmt.Printf("%s:%d", file, line)
}()
}
您的匿名函数正在由getFileName
生成的goroutine中运行。
每个goroutine都使用自己的调用堆栈执行,即使getFileName
生成的goroutine也以新堆栈开头。
通过调用跳过值大于零的runtime.Caller(skip int)
,您可以遍历当前goroutine的堆栈帧。
在你的例子中:
runtime.Caller(0)
打印 main.go:17 ,因为这是实际调用runtime.Caller()
的行,runtime.Caller(1)
打印 asm_amd64p32.s:1014 ,因为它是当前goroutine堆栈的顶部,runtime.Caller(x)
任何x> 1返回时布尔ok
设置为false
,因为您正尝试访问堆栈顶部上方的内存。如果您想知道 asm_amd64p32.s:1014 代表什么,实际上它对应于Go 1.8的goexit汇编功能(Go Playground运行最新的稳定版本Go)。尽管它的名称,goexit
函数始终位于goroutine堆栈的顶部:它调用goroutine入口点函数,然后在函数返回后清理堆栈。 goexit
实现是特定于体系结构的,因为它是处理goroutines堆栈细节的大多数代码。
回到你的问题,你不能指望getFileName
生成的goroutine的堆栈跟踪中提到 main.go:11 ,因为主函数不在堆栈中所有。如果你真的需要打印主函数的堆栈跟踪,请不要生成goroutine。
话虽如此,还有更多值得一提的事情。 Go运行时实际上存储了有关生成goroutine的位置的其他信息。 debug.PrintStack()函数(依次基于runtime.Stack())能够在格式良好的堆栈跟踪中打印出来:
package main
import (
"time"
"runtime/debug"
)
func main() {
getFileName(1)
time.Sleep(time.Hour)
}
func getFileName(shift int) {
go func() {
debug.PrintStack()
}()
}
输出:
goroutine 5 [running]:
runtime/debug.Stack(0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/runtime/debug/stack.go:24 +0x80
runtime/debug.PrintStack()
/usr/local/go/src/runtime/debug/stack.go:16 +0x20
main.getFileName.func1()
/tmp/sandbox085104368/main.go:15 +0x20
created by main.getFileName
/tmp/sandbox085104368/main.go:16 +0x40
由于所有这些细节都依赖于实现并且可能会在不同的Go版本之间发生变化,因此 - 按设计 - 没有简单的方法可以通过标准库访问它们。它们是出于调试目的而提供的,作为开发人员,您不应该依赖这些信息。