堆栈跟踪对于带有优化(尤其是中栈内联)的gccgo来说并不准确。
package main
import (
"fmt"
"runtime"
)
func main() {
f()
}
func f() {
g()
}
//go:noinline
func g() {
for i := 0; i < 4; i++ {
var pcs [1]uintptr
runtime.Callers(i+1, pcs[:])
f, _ := runtime.CallersFrames(pcs[:]).Next()
fmt.Printf("%x %s %s:%d\n", f.PC, f.Function, f.File, f.Line)
}
}
在gc下运行,我得到:
48683c main.g /usr/local/google/home/khr/gowork/tmp5.go:19
48678f main.f /usr/local/google/home/khr/gowork/tmp5.go:12
48675f main.main /usr/local/google/home/khr/gowork/tmp5.go:9
428846 runtime.main /usr/local/google/home/khr/go1.11/src/runtime/proc.go:201
在gccgo -O3下运行,我得到:
56261c5ebc19 main.g /usr/local/google/home/khr/gowork/tmp5.go:19
56261c5ebeb3 main.f /usr/local/google/home/khr/gowork/tmp5.go:12
56261c5ebeb3 main.f /usr/local/google/home/khr/gowork/tmp5.go:12
7f9974ede51c runtime_main ../../../src/libgo/runtime/proc.c:606
请注意第三行 - 那应该是 main.main
,而不是第二行的 main.f
的复制。
gccgo可以在不使用-O3的情况下工作。
在使用-O3时,发生了中栈内联,main.main
直接调用了 main.g
。 main.f
甚至不在二进制文件中。
提供 runtime.Callers
一个更大的缓冲区也没有帮助。
请注意,这段代码确实可以正常工作,只需调用一次 runtime.Callers
并使用 runtime.CallersFrames
迭代器:
package main
import (
"fmt"
"runtime"
)
func main() {
f()
}
func f() {
g()
}
//go:noinline
func g() {
var pcs [10]uintptr
n := runtime.Callers(1, pcs[:])
iter := runtime.CallersFrames(pcs[:n])
for {
f, more := iter.Next()
fmt.Printf("%x %s %s:%d\n", f.PC, f.Function, f.File, f.Line)
if !more {
break
}
}
}
55bb3972acd4 main.g /usr/local/google/home/khr/gowork/tmp6.go:18
55bb3972afc3 main.f /usr/local/google/home/khr/gowork/tmp6.go:12
55bb3972afc3 main.main /usr/local/google/home/khr/gowork/tmp6.go:9
7fa2706d451c runtime_main ../../../src/libgo/runtime/proc.c:606
@ianlancetaylor
1条答案
按热度按时间enyaitl31#
这并不令人惊讶。我能想到的让这个工作的方法是使用魔法PC值,比如你在cmd/compile中使用的内联标记NOPs。由于gccgo没有改变GCC后端,它没有这样的机会。我看不到任何理由去编写像你的第一个程序那样的代码,所以我不倾向于花时间尝试在gccgo中修复这个问题。