序言
- 记录coredump问题的一些定位技巧
1. coredump简介
- coredump称为核心转储,就是在进程异常时的一个快照,保存了异常时的内存、寄存器、堆栈等数据
- 当进程接收到某些 信号 而导致异常退出时,就会生成 coredump 文件
- core文件是ELF文件格式,可通过readelf读取查看其信息
2. 常见的coredump错误
2.1 哪些信号会导致coredump
2.1 coredump的常见原因
2.2.1 非法指针
- 使用空指针:指针为空,但仍然访问其成员;或指针已经释放又再次直接访问
- 对未初始化的指针进行了操作
- 内存double free:多次释放同一段内存
- 随意使用指针转换:一个指向一段内存的指针,可能这段内存的开始地址就是按照某种结构或者类型对齐的,如果随意进行指针类型转换,可能导致bus error或coredump
- 存在野指针:内存释放后,指针为赋值为nulptr。这个原因只是我个人猜想
2.2.2 内存越界访问
- 数组下标越界:比如未检查传参元素个数,下标超了max_size
- 搜索字符串时以字符串结束符作为判断条件,但字符串可能没有正常使用结束符
- 使用了非安全的字符串操作函数:如使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等安全函数防止读写越界
2.2.3 多线程原因
- 多线程使用了线程不安全函数:应使用可重入函数asctime_r(3c) gethostbyname_r(3n) getservbyname_r(3n) ctermid_r(3s) gethostent_r(3n) getservbyport_r(3n) ctime_r(3c) getlogin_r(3c) getservent_r(3n) fgetgrent_r(3c) getnetbyaddr_r(3n) getspent_r(3c) fgetpwent_r(3c) getnetbyname_r(3n) getspnam_r(3c) fgetspent_r(3c) getnetent_r(3n) gmtime_r(3c) gamma_r(3m) getnetgrent_r(3n) lgamma_r(3m) getauclassent_r(3) getprotobyname_r(3n) localtime_r(3c) getauclassnam_r(3) etprotobynumber_r(3n) nis_sperror_r(3n) getauevent_r(3) getprotoent_r(3n) rand_r(3c) getauevnam_r(3) getpwent_r(3c) readdir_r(3c) getauevnum_r(3) getpwnam_r(3c) strtok_r(3c) getgrent_r(3c) getpwuid_r(3c) tmpnam_r(3s) getgrgid_r(3c) getrpcbyname_r(3n) ttyname_r(3c) getgrnam_r(3c) getrpcbynumber_r(3n) gethostbyaddr_r(3n) getrpcent_r(3n)等
- 多线程读写的数据未加保护:会被多个线程同时访问的全局数据应该加保护,否则很容易导致coredump;
2.2.4 堆栈溢出
- 不要使用太大的局部变量,容易造成堆栈溢出,导致一些奇怪的错误
2.2.5 字节对齐原因
看到有的文章列的原因,目前没有碰到过
- 字节对齐方式引起的程序核心转储
- 引用模块与自身模块所定义的结构体的字节对齐方式不同
- 在代码中, 把引用到的别的模块的头文件包含到自身文件中的字节对齐方式语法声明的中间了, 结果导致字节对齐方式出现了变化
3. 如何生成coredump文件
-
为了生成coredump文件,编译选项中需要添加-g选项
-
添加-g后可使用gdb调试,调用栈也能显示行号
3.1 先看系统是否允许生成coredump文件
ulimit -c
- 0:表示禁止产生coredump文件
- unlimited: 表示不限制coredump文件的大小
- 1024: 表示coredump文件大小不能超过1024K
3.2 设置允许coredump文件生成
3.2.1 设置当前会话生效
ulimit -c 1024 // 或其他数值/unlimited
3.2.2 设置对当前用户生效
- 在~/.bashrc或者~/.bash_profile中进行配置
ulimit -c unlimited
3.2.3 设置系统生效
-
在/etc/security/limits.conf文件中进行配置
-
vim /etc/security/limits.conf
-
配置如下字段:
<domain> <type> <item> <value> * soft core unlimited
3.3 设置coredump文件的存储位置
- 先看默认配置:cat /proc/sys/kernel/core_pattern
3.3.1 修改方式1
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
- 将会在/corefile目录下生成e-线程名-p-进程号-t时间戳的core文件
3.3.2 修改方式2
sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t
3.3.3 修改方式3:对系统生效
-
在/etc/sysctl.conf中添加配置
kernel.core_uses_pid = 1 kernel.core_pattern = /your/own/path/core_%e_%p_%t
-
然后执行:
sysctl -p
-
查看cat /proc/sys/kernel/core_pattern如果设置未生效则可在目标目录如corefile下执行sysctl -p或者重启电脑后执行sysctl -p
【参数含义】
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名
- 注:vscode调试时发生coredump后右键选择copy call stack也可以保存
4. 如何排查coredump问题
4.1 问题能复现
4.1.1 debug模式下gdb运行
-
debug模式下使用,即编译选项加-g
gdb 可执行文件 r # 运行 bt # 查看调用栈
-
bt或where查看调用栈
-
如果是多线程可通过如下命令查看每个线程的调用栈情况
info thread # 查看所有线程 thread apply all bt # 查看所有线程的调用栈情况
-
gdb常用命令汇总
4.1.2 coredump发生时记录core文件
-
按照3中的流程打开core文件设置,使程序能在coredump文件发生时记录core文件
-
但要防止core文件过大,需要定期清理
-
拿到core文件后执行如下语句复现问题
gdb executable_file core_file
4.1.3 log辅助定位
- 使用glog打印,节点运行过程中记录log,节点掉线保存log过后查看日志信息
- 一种方法是使用glog日志框架添加和保存日志;
- 一种是比较简单的std::cout/printf添加日志并:executable_file > log.txt 2>&1保存为文件查看
【本地复现】
-
本地gdb executable_file长时播包测试直到问题复现
-
最好也设置core文件存储,否则因为其他原因导致系统死机无法查看现场
4.2 问题不能复现
-
考虑到2中描述的coredump可能的原因,如果不能复现,可以做一些排查工作
-
此外,无法复现的coredump问题应该这样跟踪
4.2.1 代码review
- 确认问题引入的时间段,针对性的进行代码review找出可能导致coredump的原因
- 但是当问题引入时间不明确,或者代码特别多的时候,这种方法可能不太实用
4.2.2 使用valgrind查内存问题
-
valgrind简单使用指导
-
valgrind -v ./executable_file然后直接查看ERROR SUMMARY
4.2.3 针对常见coredump问题原因进行排查
- 是否有空指针未判空访问
- 是否有内存泄漏或者内存double free等
- 是否有数组越界访问
- 是否多线程全局变量未加保护访问
4.3 容器创建时设置core文件存储
-
容器内启动的程序,可在容器创建时设置core文件存储,设置方式见3.3章节
-
通过设置Dockerfile中CMD或ENTRYPOINT命令来设置,dockerfile创建镜像命令
- CMD:容器启动时执行的命令,这些命令在docker run时执行
- ENTRYPOINT:容器启动时执行的命令,与CMD类似
- CMD会被docker run时添加的参数覆盖,ENTRYPOINT不会被覆盖
-
可设置自动设置脚本setenv.sh如下
#! /bin/bash sysctl -w kernel.core_pattern=/corefile/core-%e-%p-%t sysctl -p
-
修改文件权限
chmod 777 setenv.sh
-
然后Dockerfile中添加
CMD ["./setenv.sh"]
【参考文章】
如何配置生效core文件
配置生效core文件和压缩coredump文件
coredump问题排查方法
coredump常见原因1
coredump常见原因2
coredump常见原因3
coredump文件是如何产生的 (推荐)
除gdb外的其他定位coredump方法
created by shuaixio, 2022.11.20