程序調試對於檢查和理解程序運行過程和狀態是非常有用的。
一個核心轉儲文件( core dump file )中包含程序進程運行時的內存信息和進程狀態。它主要用於程序的問題調試,以及在運行過程中理解程序的狀態。這些對於我們診斷程序問題原因和分析生產環境中的服務問題有非常大的幫助。
在本文中,我會用一個非常簡單的 hello world 網頁應用服務舉例,實際情況,我們的程序會更加複雜。對核心轉儲文件的分析意義在於可以幫助我們查看程序當時的運行情況,並可能讓我們有機會重現當時的程序問題。
注意: 接下來的操作都是在Linux系統終端中執行,我不確定其它類Unix系統是否可以工作正常,macOS 和 Windows 應該都不支持。
在開始之前,你需要確定已經打開了操作系統對核心轉儲文件的支持。 ulimit
的默認值爲 0 意思是說核心轉儲文件最大容量只能是零。我通常在開發機上設置爲 unlimited
命令如下:
$ ulimit -c unlimited
然後,確定你的機器上已經安裝了 delve 。
這是一個 main.go
文件,包含一個HTTP啓動服務和一個處理函數。
$ cat main.go
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "hello world\n")
})
log.Fatal(http.ListenAndServe("localhost:7777", nil))
}
我們把它編譯成二進制文件。
$ go build .
我們假設下,將來這個服務可能會出現問題,但是你不知道會出現什麼樣的問題。你可能已經用了很多方法測試程序但仍然找不到程序異常退出的原因。
一般在這種情況下,最好能夠有當時程序進程的快照,然後用你的調試工具對快照進行調試。
有很多種方式可以獲得程序的核心轉儲文件。你可能已經熟悉程序崩潰轉儲方式,當程序崩潰時會將崩潰時的程序內核信息寫入磁盤文件。Go 默認是不開啓程序崩潰轉儲的,但是你可以設置 GOTRACEBACK
爲 crash
來開啓 Ctrl + backslash 生成崩潰轉儲文件。
$ GOTRACEBACK=crash ./hello
(Ctrl+\)
這樣就可以使程序崩潰並將堆棧跟蹤打印寫入核心轉儲文件。
另一種方法是從正在運行的進程中生成核心轉儲文件,而不必殺死進程。使用 gcore
選項就可以在不崩潰的情況下生成核心轉儲文件。我們重新啓動程序:
$ ./hello &
$ gcore 546 # 546 is the PID of hello.
我們已經可以在程序不崩潰的情況下拿到核心轉儲文件。下一步通過 delve 加載內核轉儲文件進行分析。
$ dlv core ./hello core.546
這和 delve 的一般用法是相同的。你可以回放,查看代碼,查看變量等。有些功能會被禁用,畢竟核心轉儲文件只是快照,而不是真實的進程情況,但程序的執行過程和進程狀態是完全可以訪問的。
(dlv) bt
0 0x0000000000457774 in runtime.raise
at /usr/lib/go/src/runtime/sys_linux_amd64.s:110
1 0x000000000043f7fb in runtime.dieFromSignal
at /usr/lib/go/src/runtime/signal_unix.go:323
2 0x000000000043f9a1 in runtime.crash
at /usr/lib/go/src/runtime/signal_unix.go:409
3 0x000000000043e982 in runtime.sighandler
at /usr/lib/go/src/runtime/signal_sighandler.go:129
4 0x000000000043f2d1 in runtime.sigtrampgo
at /usr/lib/go/src/runtime/signal_unix.go:257
5 0x00000000004579d3 in runtime.sigtramp
at /usr/lib/go/src/runtime/sys_linux_amd64.s:262
6 0x00007ff68afec330 in (nil)
at :0
7 0x000000000040f2d6 in runtime.notetsleep
at /usr/lib/go/src/runtime/lock_futex.go:209
8 0x0000000000435be5 in runtime.sysmon
at /usr/lib/go/src/runtime/proc.go:3866
9 0x000000000042ee2e in runtime.mstart1
at /usr/lib/go/src/runtime/proc.go:1182
10 0x000000000042ed04 in runtime.mstart
at /usr/lib/go/src/runtime/proc.go:1152
(dlv) ls
> runtime.raise() /usr/lib/go/src/runtime/sys_linux_amd64.s:110 (PC: 0x457774)
105: SYSCALL
106: MOVL AX, DI // arg 1 tid
107: MOVL sig+0(FP), SI // arg 2
108: MOVL $200, AX // syscall - tkill
109: SYSCALL
=> 110: RET
111:
112: TEXT runtime·raiseproc(SB),NOSPLIT,$0
113: MOVL $39, AX // syscall - getpid
114: SYSCALL
115: MOVL AX, DI // arg 1 pid
via: https://rakyll.org/coredumps/
作者:rakyll 譯者:jzhongming 校對:Unknwon
更多Go語言知識,歡迎關注微信公衆號:Go語言中文網