翻譯原文鏈接 轉(zhuǎn)帖/轉(zhuǎn)載請(qǐng)注明出處
英文原文鏈接【Go, the unwritten parts】 發(fā)表于2017/05/22 作者JBD是Go語(yǔ)言開(kāi)發(fā)小組成員
檢查程序的執(zhí)行路徑和當(dāng)前狀態(tài)是非常有用的調(diào)試手段诈火。核心文件(core file)包含了一個(gè)運(yùn)行進(jìn)程的內(nèi)存轉(zhuǎn)儲(chǔ)和狀態(tài)恃逻。它主要是用來(lái)作為事后調(diào)試程序用的涡相。它也可以被用來(lái)查看一個(gè)運(yùn)行中的程序的狀態(tài)涡匀。這兩個(gè)使用場(chǎng)景使調(diào)試文件轉(zhuǎn)儲(chǔ)成為一個(gè)非常好的診斷手段。我們可以用這個(gè)方法來(lái)做事后診斷和分析線上的服務(wù)(production services)纽哥。
在這篇文章中背率,我們將用一個(gè)簡(jiǎn)單的hello world網(wǎng)站服務(wù)作為例子。在現(xiàn)實(shí)中羡铲,我們的程序很容易就會(huì)變得很復(fù)雜蜂桶。分析核心轉(zhuǎn)儲(chǔ)給我們提供了一個(gè)機(jī)會(huì)去重構(gòu)程序的狀態(tài)并且查看只有在某些條件/環(huán)境下才能重現(xiàn)的案例。
作者注: 這個(gè)調(diào)試流程只在Linux上可行也切。我不是很確定它是否在其它Unixs系統(tǒng)上工作扑媚。macOS對(duì)此還不支持腰湾。Windows現(xiàn)在也不支持。
在我們開(kāi)始前疆股,需要確保核心轉(zhuǎn)儲(chǔ)的ulimit設(shè)置在合適的范圍费坊。它的缺省值是0,意味著最大的核心文件大小是0旬痹。我通常在我的開(kāi)發(fā)機(jī)器上將它設(shè)置成unlimited附井。使用以下命令:
$ ulimit -c unlimited
接下來(lái),你需要在你的機(jī)器上安裝delve两残。
下面我們使用的main.go
文件永毅。它注冊(cè)了一個(gè)簡(jiǎn)單的請(qǐng)求處理函數(shù)(handler)然后啟動(dòng)了HTTP服務(wù)。
$ 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))
}
讓我們編譯并生產(chǎn)二進(jìn)制文件磕昼。
$ go build .
現(xiàn)在讓我們假設(shè)卷雕,這個(gè)服務(wù)器出了些問(wèn)題,但是我們并不是很確定問(wèn)題的根源票从。你可能已經(jīng)在程序里加了很多輔助信息漫雕,但還是無(wú)法從這些調(diào)試信息中找出線索。通常在這種情況下峰鄙,當(dāng)前進(jìn)程的快照會(huì)非常有用浸间。我們可以用這個(gè)快照深入查看程序的當(dāng)前狀態(tài)。
有幾個(gè)方式來(lái)獲取核心文件吟榴。你可能已經(jīng)熟悉了奔潰轉(zhuǎn)儲(chǔ)(crash dumps)魁蒜。它們是在一個(gè)程序奔潰的時(shí)候?qū)懭氪疟P(pán)的核心轉(zhuǎn)儲(chǔ)。Go語(yǔ)言在缺省設(shè)置下不會(huì)生產(chǎn)奔潰轉(zhuǎn)儲(chǔ)吩翻。但是當(dāng)你把GOTRACEBACK
環(huán)境變量設(shè)置成“crash”兜看,你就可以用Ctrl+backslash
才觸發(fā)奔潰轉(zhuǎn)儲(chǔ)。如下圖所示:
$ GOTRACEBACK=crash ./hello
(Ctrl+\)
上面的操作會(huì)使程序終止狭瞎,將堆棧跟蹤(stack trace)打印出來(lái)细移,并把核心轉(zhuǎn)儲(chǔ)文件寫(xiě)入磁盤(pán)。
另外個(gè)方法可以從一個(gè)運(yùn)行的程序獲得核心轉(zhuǎn)儲(chǔ)而不需要終止相應(yīng)的進(jìn)程熊锭。gcore
可以生產(chǎn)核心文件而無(wú)需使運(yùn)行中的程序退出弧轧。
$ ./hello &
$ gcore 546 # 546 is the PID of hello.
根據(jù)上面的操作,我們獲得了轉(zhuǎn)儲(chǔ)而沒(méi)有終止對(duì)應(yīng)的進(jìn)程碗殷。下一步就是把核心文件加載進(jìn)delve并開(kāi)始分析精绎。
$ dlv core ./hello core.546
差不多就這些。delve的常用操作都可以使用锌妻。你可以backtrace代乃,list,查看變量等等从祝。有些功能不可用因?yàn)槲覀兪褂玫暮诵霓D(zhuǎn)儲(chǔ)是一個(gè)快照而不是正在運(yùn)行的進(jìn)程襟己。但是程序執(zhí)行路徑和狀態(tài)全部可以訪問(wèn)引谜。
(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