core dump 概念

core dump文件实际上是进程在某个时间点时的内存映像,当时进程使用的内存是啥样就会被原样保存下来存在文件系统的某个位置上,这个时间点一般是触发了SIGSEGV或者SIGABRT这两个信号的时候,当进程的内存映像保存完毕后进程就会异常终止,也就是大家喜闻乐见的“程序崩了”和“段错误:核心已转储”。

​ 因此 coredump 就像是程序出错崩溃后的“第一现场”,是用来排查错误的主要资源。

golang 程序生成 coredump 方法

设置环境变量和在代码里调用相关的标准库接口

​ 在这之前先用ulimit命令检测下系统当前能不能生成coredump:

1
2
$ ulimit -c
unlimited

​ 如果是unlimited就表示可以,如果是0那就不会生成,需要修改ulimit的设置。

修改GOTRACEBACK环境变量

GOTRACEBACK是用来控制panic发生时golang程序行为的,值是字符串,具体内容如下:

行为
none不打印任何堆栈跟踪信息,不过崩溃的原因和哪行代码触发的panic还是会打印
single只打印当前正在运行的触发panic的goroutine的堆栈以及runtime的堆栈;如果panic是runtime里发出的,则打印所有goroutine的堆栈跟踪信息
all打印所有用户创建的goroutine的堆栈信息(不包含runtime的)
system在前面all的基础上把runtime相关的所有协程的堆栈信息也一起打印出来
crash打印的内容和前面system一样,但还会额外生成对应操作系统上的 coredump 文件

​ 将这个环境变量设置成crash就可以获得信息最全面的coredump文件。

设置编译参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main
 
import (
	"fmt"
	"math/rand"
)
 
func main() {
	arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	for {
		index := rand.Intn(11)
		fmt.Println(arr[index])
	}
}
1
go build -gcflags="all=-N -l" main.go

运行程序生成 coredump

临时修改

1
GOTRACEBACK=crash ./main

永久修改(设置环境变量)

1
2
# 修改 /etc/profile 新增一行
export GOTRACEBACK=crash

修改core dump文件路径

ps: 如果是容器启动的程序,可设置挂载卷将coredump生成到挂载目录中,这样就可以防止程序panic,容器退出丢失 coredump 文件

默认路径

1
cat /proc/sys/kernel/core_pattern

临时修改

​ 修改 /proc/sys/kernel/core_pattern 文件,但/proc目录本身是动态加载的,每次系统重启都会重新加载,因此这种方法只能作为临时修改。例如:

1
#echo ‘/var/log/%e.core.%p’ > /proc/sys/kernel/core_pattern

永久修改

​ 使用 sysctl -w name=value 命令。例如:

1
#sysctl -w kernel.core_pattern=/var/log/%e.core.%p

​ 另外,为了更详尽的记录core dump当时的系统状态,可通过以下参数来丰富core文件的命名:

1
2
3
4
5
6
7
8
%% 单个%字符
%p 所dump进程的进程ID
%u 所dump进程的实际用户ID
%g 所dump进程的实际组ID
%s 导致本次core dump的信号
%t core dump的时间 (由1970年1月1日计起的秒数)
%h 主机名
%e 程序文件名

调试 coredump 文件

​ 上述步骤会在获取到的 coredump 文件,使用 dlv(golang debug工具)调试。

dlv 安装

go 版本在1.6之前

1
2
3
$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/dlv

1.6之后的版本

1
2
3
4
5
6
7
8
9
# Install the latest release:
$ go install github.com/go-delve/delve/cmd/dlv@latest

# Install at tree head:
$ go install github.com/go-delve/delve/cmd/dlv@master

# Install at a specific version or pseudo-version:
$ go install github.com/go-delve/delve/cmd/[email protected]
$ go install github.com/go-delve/delve/cmd/[email protected]

开始调试

按下面的步骤查看信息: image-20240220112416089

  1. bt,查看当前的调用堆栈,找到触发panic的那行代码在哪个frame(栈帧)里
  2. 看到是编号为10的frame,使用frame 10进入这个栈帧
  3. 使用locals查看当前栈帧内变量的值
  4. p <变量名/表达式>查看变量的具体内容,或者执行一些简单的表达式
  5. 还可以修改变量的值,设置断点后再次运行查看结果,不过例子里的问题到第四步就已经明了了。(数组索引为10,越界访问)

参考文章