前段时间遇到一个问题,程序莫名其妙 crash 了,stack 也没看出什么端倪来。今天改了一个参数,让 golang 程序在崩溃的时候 core dump。
其实核心就是加一个环境变量就可以了,GOTRACEBACK=1. 但是还有一些其他跟系统相关的问题,这篇文章简单记录一下。
Golang 1.6 之后,这个环境的变量可选值有了一些变化,新的值如下:
GOTRACEBACK=none
will suppress all tracebacks, you only get the panic message.GOTRACEBACK=single
is the new default behaviour that prints only the goroutine believed to have caused the panic.GOTRACEBACK=all
causes stack traces for all goroutines to be shown, but stack frames related to the runtime are suppressed.GOTRACEBACK=system
is the same as the previous value, but frames related to the runtime are also shown, this will reveal goroutines started by the runtime itself.GOTRACEBACK=crash
is unchanged from Go 1.5.
一些要注意的点:
首先,介绍下除了这个 GOTRACEBACK 参数,还有其他一些很有用的能控制 golang 运行时的环境变量,这篇文章总结的很好。
然后这个参数在 macOS 上是无效的,就不要在 MAC 上白费力气了。
Linux 上还受到 ulimit 的限制。可以用 ulimit -c
查看对 Core dump 的大小限制。如果是 0
是 dump 不出来了,也不建议设置成 ulimited
。我改成了 50G。如果程序使用 systemd 启动的,可以设置 service unit 文件中的 LimitCORE=
参数,效果等同。
产生的 core dump 存放在哪里了呢?
可以通过这里查看:
1 |
cat /proc/sys/kernel/core_pattern |
这里定义了 core dump 文件的命名方式。
但是在 ubuntu 里面,会看到这样的输出:
1 |
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E |
意思是通过 pipe 定向到了 apport. apport 是 ubuntu 发行版选择使用的 core dump 管理服务。
默认情况下,用户程序是不会有 core dump 的。然后我们有两个解决办法:
- 关闭 apport,使用系统的 core dump 直接写在磁盘上
- 配置 apport,让它也写用户的 core dump 文件
第一种方法比较简单,直接修改上文中的 /proc/sys/kernel/core_pattern
文件即可:
1 2 |
echo "kernel.core_pattern=/tmp/%e.%t.%p.%s.core" > /proc/sys/kernel/core_pattern sysctl --system |
注意这里有一个小小的问题要注意一下:这个配置是全局的,只有 root 账户才能编辑。如果在普通用户下执行 sudo echo "kernel.core_pattern=/tmp/%e.%t.%p.%s.core" > /proc/sys/kernel/core_pattern
是不行的,因为在这行命令中,echo
是用 sudo 执行的,但是重定向确实 shell (bash) 来完成的,重定向,即真正的写入工作,实际上没有在 root 下,所以你会得到错误:Permission denied, 或者 Destination /proc/sys/kernel not writable. 解决办法是用这个命令:sudo bash -c 'echo "kernel.core_pattern=/tmp/%e.%t.%p.%s.core" > /proc/sys/kernel/core_pattern'
.
然后可以 disable apport:
1 2 |
systemctl stop apport systemctl disable apport |
第二种方法,首先要确保 apport 在运行。可以通过 systemctl status apport
查看。也可以看下 apport 日志:
1 |
tail -f /var/log/apport.log |
触发一次 core dump,会看到:
1 |
executable does not belong to a package, ignoring |
意思是 core dump 不是来自于 ubuntu 打包的软件,忽略。
配置方法是,修改 ~/.config/apport/settings (如果没有,手动创建)文件,写入:
1 2 |
[main] unpackaged=true |
再触发一次 core dump,这次日志里就会有写入的信息了:
1 2 3 |
ERROR: apport (pid 3709) Thu Jun 22 07:00:58 2023: called for pid 3704, signal 6, core limit 0, dump mode 1 ERROR: apport (pid 3709) Thu Jun 22 07:00:58 2023: executable: /tmp/go-build2218735647/b001/exe/crash (command line "/tmp/go-build2218735647/b001/exe/crash") ERROR: apport (pid 3709) Thu Jun 22 07:00:59 2023: wrote report /var/crash/_tmp_go-build2218735647_b001_exe_crash.0.crash |
还要注意的是,这个文件不是 core dump 文件,而是 apport 打包的 debug 文件,可以使用 apport-unpack 解包:
1 |
apport-unpack _tmp_go-build2218735647_b001_exe_crash.0.crash crash_dump.core |
解包出来的 CoreDump 就可以用 gdb 分析了。其他的文件记录了一些系统相关的信息。(感觉是 Ubuntu 用来让用户报告 bug 的)
最后,如果进程的 workdir 下没有生成 core dump 的话,可以看下是不是在 /var/lib/systemd/coredump/,网上说用 systemd 的系统会存放在这里,不过我没遇到。