Go Wiki:CoreDumpDebugging

最初发表于 https://rakyll.org/coredumps/


调试对于检查执行流程和了解程序的当前状态非常有用。

核心文件是一个包含正在运行进程的内存转储及其进程状态的文件。它主要用于程序的事后调试,以及了解程序在运行时的状态。这两者使核心转储调试成为事后诊断和分析生产服务的一个良好的诊断辅助工具。

我将在本文中使用一个简单的 hello world Web 服务器,但在实际生活中,我们的程序可能会变得非常复杂。核心转储分析的可用性为您提供了一个机会,可以从特定的快照中恢复程序,并研究在特定条件/环境下才可能重现的案例。

注意:目前,此流程仅适用于 Linux 端到端,我不太确定其他 Unix,但 macOS 尚未支持。目前不支持 Windows。

在开始之前,你需要确保核心转储的 ulimit 处于合理级别。默认值为 0,这意味着最大核心文件大小只能为零。我通常通过键入将其设置为我的开发机器上的无限

$ ulimit -c unlimited

然后,确保你的机器上已安装 delve

这是一个包含简单处理程序并启动 HTTP 服务器的 main.go

$ 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 的一部分。