Go Wiki:CoreDumpDebugging

最初發布於 https://rakyll.org/coredumps/


偵錯對於檢查執行流程和瞭解程式目前的狀態非常有用。

核心檔案是一個包含正在執行的程序記憶體傾印及其程序狀態的檔案。它主要用於程式事後除錯,以及了解程式在執行中的狀態。這兩種情況讓核心傾印除錯成為一個良好的診斷輔助工具,用於事後分析生產服務。

我在本文中將使用一個簡單的 hello world 網路伺服器,但在實際情況中,我們的程式可能會變得非常複雜。核心傾印分析的可用性讓你有機會從特定快照中還原程式,並深入探討僅在特定條件/環境中才能重現的情況。

注意:此流程目前僅適用於 Linux 端到端,我不太確定其他 Unix,但 macOS 尚未支援。Windows 目前也不支援。

在我們開始之前,你需要確保核心傾印的 ulimit 處於合理的層級。它預設為 0,表示最大核心檔案大小只能為零。我通常在開發機器上將它設定為無限,方法是輸入

$ 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+反斜線。

$ 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

此內容是 Go Wiki 的一部分。