GOLANG空指標崩潰時堆疊消失和解決方案

winlin發表於2017-06-07

錯誤處理這個文章中,tkk提出了空指標時堆疊消失的問題,看下面的例子

package main

func main() {
    run() // line 4
}
func run() {
    causedPanic()
}
func causedPanic() {
    //defer func() {}() // line 10
    //panic("Panic from user") // line 11
    var p *byte
    *p = 0 // line 13
}

這個程式崩潰時,列印的竟然是:

panic: runtime error: invalid memory address or nil pointer dereference

goroutine 1 [running]:
main.main()
    /tmp/sandbox277759147/main.go:4 +0x4

神奇的是,把第 10 行的 defer 開啟,變成這樣

func causedPanic() {
    defer func() {}() // line 10
    //panic("Panic from user") // line 11
    var p *byte
    *p = 0 // line 13
}

堆疊神奇的回來了:

panic: runtime error: invalid memory address or nil pointer dereference

goroutine 1 [running]:
main.causedPanic()
    /tmp/sandbox416089181/main.go:13 +0x48
main.run()
    /tmp/sandbox416089181/main.go:7 +0x20
main.main()
    /tmp/sandbox416089181/main.go:4 +0x20

而主動呼叫 panic 堆疊也是沒有問題的,可以把第 10 行註釋掉,同時開啟第 11 行。這個問題確實很詭異,在 go-nuts 中發了一篇文章問,strange stack trace when panic,馬上就有神回覆了:

On Wednesday, June 7, 2017 at 4:25:35 PM UTC+8, Dave Cheney wrote:

Try building your program with -gcflags="-l" to disable inlining. 
If that restores the stacktrace, then it's inlining. 
The good news is this should be fixed with Go 1.9

果然,執行時加上這個引數 (編譯時加上也是可以的),禁用內聯編譯後,堆疊就回來了:

go run -gcflags="-l" t.go

難怪了,主動呼叫 panic 時,內聯編譯不會把函式懟一坨去,如果沒有 defer 和 panic 這種函式,就可能把函式懟一坨,看起來像是一個函式,堆疊消失了,這樣在空指標時就找不到堆疊資訊。

解決方案:

  1. 編譯時加引數-gcflags="-l"
  2. 可能在 GO1.9 會解決這個問題。

結貼。

更多原創文章乾貨分享,請關注公眾號
  • GOLANG空指標崩潰時堆疊消失和解決方案
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章