今天讲讲怎么让golang程序生成coredump文件,并且进行调试的。

别看我写了不少golang的博客,其实我平时写c++的时间更多,所以也算和coredump是老相识了。core dump文件实际上是进程在某个时间点时的内存映像,当时进程使用的内存是啥样就会被原样保存下来存在文件系统的某个位置上,这个时间点一般是触发了SIGSEGV或者SIGABRT这两个信号的时候,当进程的内存映像保存完毕后进程就会异常终止,也就是大家喜闻乐见的“程序崩了”和“段错误:核心已转储”。

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

不过我很少在golang里调试coredump文件,通常来说可靠的日志和panic时打印的错误信息加堆栈就足够定位错误了。然而有时光靠这些信息还不够,不得不去求助老朋友coredump了。

下面我们主要针对这段代码调试,这只是个事例,所以你一眼看出问题在哪了也不要介意:

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])
}
}

编译并运行这段代码,运行上一小会儿就会看到程序panic了。假设报错信息没能帮助我们定位问题,接下来我们看看如何用coredump调试golang程序。

如何让golang程序生成coredump

首先,如果你不做任何额外的设置,那么golang程序崩溃的时候只会打印崩溃信息和简单的调用栈信息,并不会生成coredump文件。

想改变这个行为有两种方式:设置环境变量和在代码里调用相关的标准库接口。

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

$ 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文件。所以我们要做的就是像下面这样:

go build main.go
GOTRACEBACK=crash ./main

或者你嫌麻烦,那就在服务器系统里做全局设置,一般是修改/etc/profile:

# 其他内容
# 全局设置,需要让所有已登录的用户注销会话重新登录或者干脆重启系统才会生效
export GOTRACEBACK=crash

上面的全局设置是针对Linux的,Windows就按正常设置环境变量那样操作,然后重新登录用户即可。

这样运行后就会生成coredump文件了。一般会生成在当前的工作目录里。

还有一点要注意:如果你正在使用较新的linux发行版,那么coredump文件会被coredumpctl接管,并不会生成在当前目录

可以看到coredump文件被集中管理了,使用info子命令可以看到存放这些文件的路径和崩溃的进程的信息:

其中的present表示coredump的文件还保存着,可以用来调试,missing的哪些就代码coredump文件已经没了。

想要用dlv来调试的话得用这样的命令:

coredumpctl debug <list那给出的崩溃的进程的id> --debugger=<调试器程序的名字或路径> -A <传给调试器的参数>

填一下空就是这样:

coredumpctl debug 156814 --debugger=dlv -A core ./main

这样就能正常进行调试了。另外编译main程序的时候记得把优化关了,以免代码被优化得和写的不一样导致没法调试。

coredumpctl除了把coredump文件压缩了一下节约了一点硬盘空间之外没有什么优势,整个就体现了systemd家族的臭毛病:多管闲事。

使用标准库接口

没有标准库函数可以主动触发coredump生成,但有可以在代码里设置panic时候的行为的,使用的值和GPTRACEBACK一模一样:

debug.SetTraceback

这个函数优先级比环境变量高,但有个限制,它只能设置比环境变量的值打印更多信息的值,也就是说如果环境变量是all,那么这个函数就只能设置systemcrash,不能设置nonesingle

代码例子:

package main

import (
"fmt"
"math/rand"
+ "runtime/debug"
) func main() {
+ debug.SetTraceback("crash")
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for {
index := rand.Intn(11)
fmt.Println(arr[index])
}
}

效果和设置环境变量一样,这里就不展示了。

我该用哪个

没什么特别的需求的话,我推荐你只用GOTRACEBACK环境变量。

环境变量可以在不修改代码或者配置文件的情况下控制程序的行为,不需要花时间改代码改配置然后再编译运行。用标准库的接口想达到类似效果就得写不少代码了。

还有个好处是方便在容器里管理,也符合云原生十二要素。

调试coredump

coredump里保存了程序崩溃前的所有状态,包括执行到哪行代码了,各个变量的值是什么,还包含了runtime当前的状态等等。

仔细检查这些信息就可以发现程序崩溃的原因。

还是用这条命令打开调试器:

coredumpctl debug 156814 --debugger=dlv -A core ./main

然后按下面的步骤查看信息:

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

这里的问题很明显:数组长度是10,索引最大只有9,而index变量的值是10。所以索引访问越界,导致了panic。

QA

Q: 上面只说了panic的时候生成coredump,如果我想要个程序正常运行时的快照该怎么做?

A: Linux上有不少进程内存快照生成工具,不过delve内置的交互式命令dump就可以满足需求。

具体方法是dlv attach <pid>之后直接运行dump <输出coredump的文件名>命令,然后退出。或者还有全自动化的:

$ echo 'dump coredump'|dlv attach <pid> ./main --allow-non-terminal-interactive
$ ls -lh 总计 47M
-rw-r--r-- 1 a a 45M 7月 8日 00:34 coredump
-rw-r--r-- 1 a a 25 7月 8日 00:20 go.mod
-rwxr-xr-x 1 a a 1.8M 7月 8日 00:31 main
-rw-r--r-- 1 a a 141 7月 8日 00:30 main.go

可以看到当前目录下生成了一个名为“coredump”的coredump文件。

这个命令本身比较耗时,进程用的内存越多就越慢,请谨慎在生产环境使用

Q: 这个例子里没看出来有调试coredump的必要。

A: 是这个例子的问题,它不够好。我可以简单举一个以前遇到的真实情况:

以前有个处理用户输入的程序,用户可以输入任何utf8字符,程序会简单处理这些字符然后存到一块内存里,这东西上线后隔三差五就会panic,每次都是越界访问,但越界的值和发生的时间都没有规律可言。

最后实在没办法,抓了一次coredump,仔细检查了用户的输入,发现是我们的代码在处理某些特殊字符时想当然了,没能正确处理数据的长度。如果光看代码本身的话这个问题很难排查。

至于为什么不把用户输入打进日志,这涉及了隐私和权益问题,不能这么做,但调试完coredump后删除勉强能规避这些问题。

Q: 我有必要总是开启coredump吗?

A: 没有。正如我前面所说,一般日志和panic打印的信息就够用了。coredump本身会占据很多磁盘空间,而且在容器里dump下来的东西容器重启后就没了,除非单独设置数据卷但这非常复杂。

Q: 一些web框架会用recover处理panic,请问这时候还能获得coredump吗?

A: 不能。被recover的panic不会触发coredump。这时候你得想想其他办法了,比如用第一个QA那的办法生成个实时快照。

总结

coredump对于golang来说并不常用,但技多不压身,了解一下对以后处理各种问题总是有帮助的。

参考

https://github.com/go-delve/delve/blob/master/Documentation/usage/dlv_attach.md

https://pkg.go.dev/runtime

https://linderud.dev/blog/coredumpctl-delve-and-debug-packages-for-go/

让golang程序生成coredump文件并进行调试的更多相关文章

  1. gdb 调试coredump文件过程

    gdb 调试coredump文件过程: 第一步:首先需要一个进程的coredump文件,怎么搞出coredump文件呢? 1. ps -fax|grep                 进程名称 找到 ...

  2. gdb调试coredump文件

    linux上程序崩溃起来挺烦人,不过linux 比较好的是有gdb. 1.生成coredump文件 echo "ulimit -c unlimited" >> /etc ...

  3. gdb 调试coredump文件过程:

    第一步:首先需要一个进程的coredump文件,怎么搞出coredump文件呢? 1. ps -fax|grep                 进程名称 找到进程的pid 2.gdb -p pid ...

  4. 使用GDB生成coredump文件【转载】

    本文转载自: http://blog.csdn.net/sky_qing/article/details/8548989 如果在测试过程中遇到某个进程的CPU利用率过高或者卡死而需要去调试该进程时,可 ...

  5. linux shell 值coredump suid_dumpable和 gdb解析coredump文件

    可以设置产生coredump文件,设置dump文件命名非格式,生成dump文件的路径: linux # set suid_dumpable on if [ -e /proc/sys/kernel/su ...

  6. win7,vs2010,asp.net项目中修改外部js文件,在调试时加载的还是旧文件

    win7,vs2010,asp.net项目中修改外部js文件,在调试时加载的还是旧文件 我杀过 w3wp.exe和asp.net_state的进程,重启 iis admin的服务,都还是不行. 只是把 ...

  7. CentOS 7.2 无法生成 coredump文件

    CentOS版本 cat /etc/centos-release  CentOS Linux release 7.2.1511 (Core) 设置ulimit -c ulimited 依旧无法生成co ...

  8. linux下coredump的产生及调试方法

    什么是coredump 通常情况下coredmp包括了程序执行时的内存,寄存器状态,堆栈指针,内存管理信息等.能够理解为把程序工作的当前状态存储成一个文件.很多程序和操作系统出错时会自己主动生成一个c ...

  9. dump文件生成与调试(VS2008)

    总结一下dump文件生成和调试的方法: 1:用SetUnhandledExceptionFilter捕获未处理的异常,包含头文件<windows.h>.函数原型为: LPTOP_LEVEL ...

  10. core dump文件分析和调试

    core介绍 当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做Core Dump(中文有的翻译成"核心转储").我们可以认 ...

随机推荐

  1. 由ASP.NET Core读取Response.Body引发的思考

    前言 前几天有群友在群里问如何在我之前的文章<ASP.NET Core WebApi返回结果统一包装实践>的时候有点疑问,主要的疑问点就是关于Respouse的读取的问题.在之前的文章&l ...

  2. Nvidia GPU虚拟化

    1 背景 随着Nvidia GPU在渲染.编解码和计算领域发挥着越来越重要的作用,各大软件厂商对于Nvidia GPU的研究也越来越深入,尽管Nvidia倾向于生态闭源,但受制于极大的硬件成本压力,提 ...

  3. docker 配置 Mysql主从集群

    docker 配置Mysql集群 Docker version 20.10.17, build 100c701 MySQL Image version: 8.0.32 Docker container ...

  4. Kubernetes入门实践(Job/CronJob)

    基于Pod的设计理念,Kubernetes有两种对象Job和CronJob Job和CronJob组合了Pod,实现了对离线业务的处理.如Nginx和busybox,分别代表了Kubernetes里的 ...

  5. [Pytorch框架] 4.5 多GPU并行训练

    文章目录 4.5 多GPU并行训练 4.5.1 torch.nn.DataParalle 4.5.2 torch.distributed 4.5.3 torch.utils.checkpoint im ...

  6. NC17383 A Simple Problem with Integers

    题目链接 题目 题目描述 You have N integers A1, A2, ... , AN. You are asked to write a program to receive and e ...

  7. 推荐一个基于.Net Framework开发的Windows右键菜单管理工具

    平常在我们电脑,我们都会安装非常多的软件,很多软件默认都会向系统注册右键菜单功能,这样方便我们快捷打开.比如图片文件,通过右键的方式,快捷选择PS软件打开. 如果我们电脑安装非常多的软件,就会导致我们 ...

  8. 揭秘Karmada百倍集群规模多云基础设施体系

    摘要:本文结合Karmada社区对大规模场景的思考,揭示Karmada稳定支持100个大规模集群.管理超过50万个节点和200万个Pod背后的原理 本文分享自华为云社区<Karmada百倍集群规 ...

  9. vue中粘贴板clipboard的使用方法

    一.npm安装clipboard npm install clipboard --save 二.页面结构 <span id="copyTarget">{{targetC ...

  10. 2023-03-08:x265的视频编码器,不用ffmpeg,用libx265.dll也行。请用go语言调用libx265.dll,将yuv文件编码成h265文件。

    2023-03-08:x265的视频编码器,不用ffmpeg,用libx265.dll也行.请用go语言调用libx265.dll,将yuv文件编码成h265文件. 答案2023-03-08: 使用 ...