寫在前面
本文基於GoLang delve 1.4.1。
coredump是一個包含程序意外終止時的內存快照的文件。它可以用於事后調試,以了解崩潰發生的原因以及其中涉及的變量。通過GOTRACEBACK,Go提供了一個環境變量來控制程序崩潰時產生的輸出。這個變量還可以強制生成coredump,這樣以來就使得調試成為可能。
GOTRACEBACK
GOTRACEBACK可以用於控制程序程序崩潰時輸出內容的多少,它可以有以下一些取值:
none不顯示goroutine的堆棧調用的tracesingle(默認值)顯示當前goroutine的堆棧調用的traceall顯示用戶創建的所有的goroutine的堆棧調用的tracesystem顯示包含運行時goroutine及其它所有goroutine的堆棧調用的tracecrash類似於system,不同的是,它同時也會產生coredump
最后一個選項使我們能夠在程序崩潰時進行調試。如果你沒有得到足夠的日志,或者崩潰無法重現,這可能是一個不錯的選擇。讓我們用下面的程序來舉個例子:
package main
import "math/rand"
func main() {
sum := 0
for {
n := rand.Intn(1e6)
sum += n
if sum%42 == 0 {
panic(":(")
}
}
}
運行上面的程序,我們很快就會發現程序出錯了:
F:\hello>go run main.go
panic: :(
goroutine 1 [running]:
main.main()
F:/hello/main.go:11 +0x8f
exit status 2
但這里的問題是,我們無法從堆棧調用的trace中判斷出具體是哪個值引起的崩潰。當然了,您可以通過添加日志的方法去一步步的定位問題具體出現在哪里,但我們並不總是能知道在我們應該把日志添加在哪里。另外呢,當一個問題無法重現時,寫測試用例並確保它被修復也是相當困難的。
總結一下上面的思路:在添加日志和運行應用程序之間迭代,直到它崩潰,並查看可能的原因運行后。
是否有其它的辦法呢?答案是:有的。
我們可以用GOTRACEBACK=crash再運行一次,這樣將有比較詳細的輸出了,因為我們現在已經打印出了所有的goroutines,包括運行時的。除此之外,也生成了相應的coredump文件(注意,coredump文件是由SIGABRT觸發產生的,win上目前應該還沒有支持產生coredump)。
F:\hello>set GOTRACEBACK=crash
F:\hello>go run main.go
panic: :(
goroutine 1 [running]:
panic(0x469600, 0x48d7b0)
D:/Go/src/runtime/panic.go:1064 +0x470 fp=0xc00002bf58 sp=0xc00002bea0 pc=0x42fd90
main.main()
F:/hello/main.go:11 +0x8f fp=0xc00002bf88 sp=0xc00002bf58 pc=0x461d8f
runtime.main()
D:/Go/src/runtime/proc.go:204 +0x209 fp=0xc00002bfe0 sp=0xc00002bf88 pc=0x432929
runtime.goexit()
D:/Go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc00002bfe8 sp=0xc00002bfe0 pc=0x45abc1
goroutine 2 [force gc (idle)]:
runtime.gopark(0x47cf20, 0x4d1b20, 0x1411, 0x1)
D:/Go/src/runtime/proc.go:306 +0xfa fp=0xc000027fb0 sp=0xc000027f90 pc=0x432cfa
runtime.goparkunlock(...)
D:/Go/src/runtime/proc.go:312
runtime.forcegchelper()
D:/Go/src/runtime/proc.go:255 +0xcd fp=0xc000027fe0 sp=0xc000027fb0 pc=0x432b8d
runtime.goexit()
D:/Go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000027fe8 sp=0xc000027fe0 pc=0x45abc1
created by runtime.init.6
D:/Go/src/runtime/proc.go:243 +0x3c
goroutine 3 [GC sweep wait]:
runtime.gopark(0x47cf20, 0x4d1c00, 0x140c, 0x1)
D:/Go/src/runtime/proc.go:306 +0xfa fp=0xc000029fa8 sp=0xc000029f88 pc=0x432cfa
runtime.goparkunlock(...)
D:/Go/src/runtime/proc.go:312
runtime.bgsweep(0xc000018070)
D:/Go/src/runtime/mgcsweep.go:163 +0xb2 fp=0xc000029fd8 sp=0xc000029fa8 pc=0x41e132
runtime.goexit()
D:/Go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000029fe0 sp=0xc000029fd8 pc=0x45abc1
created by runtime.gcenable
D:/Go/src/runtime/mgc.go:217 +0x67
goroutine 4 [GC scavenge wait]:
runtime.gopark(0x47cf20, 0x4d1c60, 0x140d, 0x1)
D:/Go/src/runtime/proc.go:306 +0xfa fp=0xc000037f78 sp=0xc000037f58 pc=0x432cfa
runtime.goparkunlock(...)
D:/Go/src/runtime/proc.go:312
runtime.bgscavenge(0xc000018070)
D:/Go/src/runtime/mgcscavenge.go:265 +0xe5 fp=0xc000037fd8 sp=0xc000037f78 pc=0x41c105
runtime.goexit()
D:/Go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000037fe0 sp=0xc000037fd8 pc=0x45abc1
created by runtime.gcenable
D:/Go/src/runtime/mgc.go:218 +0x89
exit status 2
Delve
Delve是一個用Go編寫的Go程序的調試器。它可以通過在用戶代碼以及運行時的任何地方添加斷點來逐步進行調試,甚至可以使用命令dlv core調試coredump,該命令將二進制和coredump作為參數。
命令運行后,我們就可以開始與coredump進行交互。
