Go1.17 新特性,優(yōu)化拋出的錯(cuò)誤堆棧
本文轉(zhuǎn)載自微信公眾號(hào)「腦子進(jìn)煎魚了」,作者陳煎魚。轉(zhuǎn)載本文請(qǐng)聯(lián)系腦子進(jìn)煎魚了公眾號(hào)。
大家好,我是煎魚。
平時(shí)在日常工程中,我們常常會(huì)用到異??只?panic)的記錄和追蹤。最常見(jiàn)的就是,線上 panic 了之后,我們總想從中找到一些蛛絲馬跡。
我們很多人是看 panic 是看他的調(diào)用堆棧。然后就開(kāi)始猜,看代碼。猜測(cè)是不是哪里寫的有問(wèn)題,就想知道 panic 是由什么參數(shù)引起的?
因?yàn)橹懒苏T發(fā)的參數(shù),排查問(wèn)題就非常方便了。為此在 Go1.17,官方對(duì)這塊的調(diào)用堆棧信息展示進(jìn)行了優(yōu)化,使其可讀性更友好。
案例
結(jié)合我們平時(shí)所使用的 panic 案例。如下:
- func main() {
 - example(make([]string, 1, 2), "煎魚", 3)
 - }
 - //go:noinline
 - func example(slice []string, str string, i int) error {
 - panic("腦子進(jìn)煎魚了")
 - }
 
運(yùn)行結(jié)果:
- $ go run main.go
 - panic: 腦子進(jìn)煎魚了
 - goroutine 1 [running]:
 - main.example(0xc000032758, 0x1, 0x2, 0x1073d11, 0x6, 0x3, 0xc000102058, 0x1013201)
 - /Users/eddycjy/go-application/awesomeProject/main.go:9 +0x39
 - main.main()
 - /Users/eddycjy/go-application/awesomeProject/main.go:4 +0x68
 - exit status 2
 
我們函數(shù)的入?yún)⑹牵篬]string、string、int,核心關(guān)注到 main.example 方法的調(diào)用堆棧信息:
- main.example(
 - 0xc000032758,
 - 0x1,
 - 0x2,
 - 0x1073d11,
 - 0x6,
 - 0x3,
 - 0xc000102058,
 - 0x1013201
 - )
 
明明只是函數(shù)三個(gè)參數(shù),卻輸出了一堆,對(duì)應(yīng)起來(lái)非常的不清晰。
其實(shí)際對(duì)應(yīng)是:
- slice:0xc000032758、0x1、0x2。
 - string:0x1073d11、0x6。
 - int:0x3。
 
這里存在的問(wèn)題是,看調(diào)用堆棧的人,還得必須了解基本數(shù)據(jù)結(jié)構(gòu)(例如:slice、string、int 等),他才知道每個(gè)函數(shù)入?yún)⑺麑?duì)應(yīng)擁有幾個(gè)字段,才能知道其內(nèi)存布局的結(jié)構(gòu),有一點(diǎn)麻煩。
并且從程序運(yùn)行的角度來(lái)講,這么水平平鋪的方式,并不直觀和準(zhǔn)確。因?yàn)椴煌愋退嵌鄠€(gè)字段組合成結(jié)構(gòu)才能代表一個(gè)類型。這不得還要人為估測(cè)?
優(yōu)化
終于,這一塊的調(diào)用堆棧查看在 Go1.17 做了正式的改善。如下:
- $ go1.17 run main.go
 - panic: 腦子進(jìn)煎魚了
 - goroutine 1 [running]:
 - main.example({0x0, 0xc0000001a0, 0xc000034770}, {0x1004319, 0x60}, 0x0)
 - /Users/eddycjy/go-application/awesomeProject/main.go:9 +0x27
 - main.main()
 - /Users/eddycjy/go-application/awesomeProject/main.go:4 +0x47
 - exit status 2
 
新版本的調(diào)用堆棧的信息改變:
- main.example(
 - {0x0, 0xc0000001a0, 0xc000034770},
 - {0x1004319, 0x60},
 - 0x0
 - )
 
在 Go 語(yǔ)言以前的版本中,調(diào)用堆棧中的函數(shù)參數(shù)被打印成基于內(nèi)存布局的十六進(jìn)制值的形式,比較難以讀取。
在 Go1.17 后,每個(gè)函數(shù)的參數(shù)都會(huì)被單獨(dú)打印,并且以 “,” 隔開(kāi),復(fù)合數(shù)據(jù)類型(例如:結(jié)構(gòu)體、數(shù)組、切片等)的參數(shù)會(huì)用大括號(hào)包裹起來(lái),整體更易讀。
其實(shí)際對(duì)應(yīng)如下:
- slice:0x0, 0xc0000001a0, 0xc000034770。
 - string:0x1004319, 0x60。
 - int:0x0。
 
這里也有一塊細(xì)節(jié)要注意,你會(huì)發(fā)現(xiàn) Go1.17 的函數(shù)參數(shù)的數(shù)量和以往的版本相比,少了。是因?yàn)楹瘮?shù)的返回值存在于寄存器中,而不會(huì)存儲(chǔ)到內(nèi)存中。
因此函數(shù)返回值可能會(huì)是不準(zhǔn)確的,所以也在新版本中也就不再打印了。
總結(jié)
在 Go1.17 的新版本中,調(diào)用堆棧的函數(shù)參數(shù)的可讀性得到了進(jìn)一步的優(yōu)化和調(diào)整,在后續(xù)的使用上可能能夠帶來(lái)一定的排錯(cuò)效率的提高。
你平時(shí)在借助調(diào)用堆棧排查問(wèn)題呢,希望還獲得什么輔助呢?
參考
GoTip: New Stack Trace Output Wrong
cmd/compile: bad traceback arguments
Go 1.17新特性詳解:使用基于寄存器的調(diào)用慣例
doc/go1.17: reword "results" in stack trace printing















 
 
 














 
 
 
 