一个go1.9.x 编译器内联引起的栈信息错乱的问题分析
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
? ? 背景是在寫個(gè)日志庫,日志庫有個(gè)很重要的功能就是要打印出調(diào)用棧,知道具體是哪個(gè)文件,哪個(gè)函數(shù)調(diào)用的Info 等。 然后在測試中發(fā)現(xiàn)了一種寫法,我自己本機(jī)測試一直ok, 但是業(yè)務(wù)使用的時(shí)候調(diào)用棧始終不對,打的調(diào)用棧少了一層。莫名其妙的,后來對比發(fā)現(xiàn),我們就是go version 不一樣。
? ? go version :
go version go1.9.2 darwin/amd64? ?go env:
GOARCH="amd64" GOBIN="" GOEXE="" GOHOSTARCH="amd64" GOHOSTOS="darwin" GOOS="darwin" GOPATH="/Users/didi/Desktop/didi" GORACE="" GOROOT="/usr/local/go1.9.2" GOTOOLDIR="/usr/local/go1.9.2/pkg/tool/darwin_amd64" GCCGO="gccgo" CC="clang" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/2w/tt1p_4td3yq9xlbl7c2t4jn00000gn/T/go-build427754844=/tmp/go-build -gno-record-gcc-switches -fno-common" CXX="clang++" CGO_ENABLED="1" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config"我的示例代碼是這樣的:
package mainimport ("fmt""runtime" )var i []bytetype AAA struct { }func (a *AAA) test1() *AAA {buf := make([]byte, 1<<20)runtime.Stack(buf, true)fmt.Printf("\n%s", buf)return a }func (a *AAA) test2() *AAA {i = append(i, "test2"...)return a }func test() {a := AAA{}a.test1().test2() }func main() {test() }然后呢,我期望的結(jié)果:
goroutine 1 [running]: main.(*AAA).test1(0xc420045f60, 0x1003a4c)/Users/didi/Desktop/didi/src/test/testCall/main.go:15 +0x87 main.test()/Users/didi/Desktop/didi/src/test/testCall/main.go:27 +0x2f main.main()/Users/didi/Desktop/didi/src/test/testCall/main.go:31 +0x20但是真實(shí)結(jié)果是這樣的:
goroutine 1 [running]: main.(*AAA).test1(0xc42003df48, 0xc42003df70)/Users/didi/Desktop/didi/src/test/testCall/main.go:15 +0x87 main.(*AAA).test2(...)/Users/didi/Desktop/didi/src/test/testCall/main.go:27 main.test()/Users/didi/Desktop/didi/src/test/testCall/main.go:27 +0x2f main.main()/Users/didi/Desktop/didi/src/test/testCall/main.go:31 +0x20? ? 問題來了,我日志庫封裝要是有這種類似邏輯,那打印的日志全都是有問題的,怎么可能是test2調(diào)用test1? 莫名其妙的。。。
? ? 初步懷疑是內(nèi)聯(lián)引起的問題,這里現(xiàn)象看著很像。編譯,加上不允許內(nèi)聯(lián)后,問題解決,? 解決方式蠻簡單的,函數(shù)前加個(gè) // go:noinline。
? ? 為什么會出現(xiàn)這種讓人困惑的現(xiàn)象,通過查看go 官方issue 和 release note? 發(fā)現(xiàn)下面解釋:
Users of runtime.Callers should avoid directly inspecting the resulting PC slice and instead use runtime.CallersFrames to get a complete view of the call stack, or runtime.Caller to get information about a single caller. This is because an individual element of the PC slice cannot account for inlined frames or other nuances of the call stack. // 使用runtime.Caller 不能顯示內(nèi)聯(lián)的細(xì)微區(qū)別。Specifically, code that directly iterates over the PC slice and uses functions such as runtime.FuncForPC to resolve each PC individually will miss inlined frames. To get a complete view of the stack, such code should instead use CallersFrames. Likewise, code should not assume that the length returned by Callers is any indication of the call depth. It should instead count the number of frames returned by CallersFrames.Code that queries a single caller at a specific depth should use Caller rather than passing a slice of length 1 to Callers.runtime.CallersFrames has been available since Go 1.7, so code can be updated prior to upgrading to Go 1.9.? ?然后官方有人提了這個(gè)issue,?https://github.com/golang/go/issues/22916。總結(jié)就是,官方在1.9 的時(shí)候覺得1.8及以前版本的不對,Caller 應(yīng)該將內(nèi)聯(lián)棧也算進(jìn)去。然后后來大家覺得這種使用不符合習(xí)慣,在1.10 又改回去了。我個(gè)人試了下,1.10.x, 1.11.x 都是正常的。
? ? 這種問題,大多數(shù)人應(yīng)該遇不上,一個(gè)是要求鏈?zhǔn)秸{(diào)用的寫法,第二個(gè)得關(guān)心調(diào)用棧,才會遇到這種奇怪現(xiàn)象。
轉(zhuǎn)載于:https://my.oschina.net/u/2950272/blog/2995702
總結(jié)
以上是生活随笔為你收集整理的一个go1.9.x 编译器内联引起的栈信息错乱的问题分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 查询两张表结构相同的数据库_
- 下一篇: 用命令行建立空白的simulink模型(