1. 写在前面
最近部分模块使用了c/c++
进行开发,由于目前在测试环境出现偶发性的core dump
的情况,在问题的跟进过程中用到了部分方法,本文着重介绍core
文件生成配置于gdb
对其调试的一些方法。
2. core dump 介绍
(1) 概念
当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为叫core dump
。
我们一般认为core dump
就是内存快照,实际上除了内存信息之外,还有些关键的程序运行状态也会同时dump
下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。
core dump
对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而core dump
文件可以再现程序出错时的情景。
(2) core dump 文件生成
对于程序的异常终止,我们发现实际上,如果使用kill -9 xxx
的命令,并不会产生core
文件,下面列出几种信号会让系统产生core dump
的系统信号。
Signal | Action | Comment |
---|---|---|
SIGQUIT | Core | Quit from keyboard |
SIGILL | Core | Illegal Instruction |
SIGABRT | Core | Abort signal from abort |
SIGSEGV | Core | Invalid memory reference |
SIGTRAP | Core | Trace/breakpoint trap |
core
文件不都是立即生成的,随着吐core
进程的内存空间越大,此过程可能持续很长一段时间,当进程占用60G+以上内存时,完整core
文件需要15分钟才能完全写到磁盘上。
本文主要介绍一些基本的情况,所以对于core dump
更详细的产生情况,可以看文末的一些参考资料,里面有详细的介绍。
(3) core dump 配置
配置文件生成
一般在系统中,可以通过命令ulimit –c
查看core dump
产生的文件大小,当为0
时,则不会产生core dump,需要进行修改和配置。
ulimit -c unlimited
可以产生coredump且不受大小限制,设置仅对当前生效。
永久生效的配置方法,需要在/etc/profile
中加入ulimit -c unlimited
命令,允许生成core dump
文件。
更改 core dump 生成路径
core dump
默认会生成在程序的工作目录,有时候为了方便,会进行默认路径的调整,大家需要的可以参考。
创建/coredump
文件夹,调用以下命令即可:
echo /coredump/core.%e.%p> /proc/sys/kernel/core_pattern
其中%e表示程序名, %p表示进程id。
3. gdb调试
(1) 基本概念
GDB
是一个由GNU
开源组织发布的UNIX/LINUX
操作系统下的,基于命令行的功能强大的程序调试工具。对于Linux
下工作的c/c++
程序员,gdb
是必不可少的工具。
对于GDB
详细使用方法,文末也引用了一篇更加详细的文章,供大家参考。
(2) 调试实战
该介绍默认大家对于linux
下文件编译已经掌握,我们在编译的时候,会根据是否需要调试,由于我写的模块异常挂掉,为了快速定位问题,添加了-g
指令进行编译,当添加了-g
以后,在程序core dump
后,可以查看到更加详细的堆栈信息。
虽然我写的模块,是挂载在其他程序上的,但是只要生成了core
文件,就不影响对问题的排查,首先进入挂掉的可执行程序目录,找到对应的core
文件。
执行命令gdb xxx.so core.xxx
,加载了当时程序挂掉的内存及其他信息:
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `/usr/local/freeswitch/bin/freeswitch -nc -nf -nonat'.
Program terminated with signal 8, Arithmetic exception.
#0 0x00007f0f09e0619b in onSentenceEnd (cbEvent=0x7f0f08dcd810, cbParam=0x7f0f08fd3020) at mod_asr.cpp:331
331 int volume_avg = tmpParam->voiceInfo.volume_total/tmpParam->voiceInfo.count;
Missing separate debuginfos, use: debuginfo-install cyrus-sasl-lib-2.1.26-23.el7.x86_64 flac-libs-1.3.0-5.el7_1.x86_64 glibc-2.17-292.el7.x86_64 gsm-1.0.13-11.el7.x86_64 jbigkit-libs-2.0-11.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5
-libs-1.15.1-37.el7_7.2.x86_64 lame-libs-3.100-1.el7.x86_64 libcom_err-1.42.9-16.el7.x86_64 libedit-3.0-12.20121213cvs.el7.x86_64 libgcc-4.8.5-39.el7.x86_64 libjpeg-turbo-1.2.90-8.el7.x86_64 libogg-1.3.0-7.el7.x86_64 libselinux-2.5-14.1.el7.x86_64 libshout-2.2.2-11.el7.x86_64 libsndfile-1.0.25-10.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64 libtheora-1.1.1-8.el7.x86_64 libtiff-4.0.3-32.el7.x86_64 libtool-ltdl-2.4.2-22.el7_3.x86_64 libvorbis-1.3.3-8.el7.1.x86_64 lua-5.1.4-15.el7.x86_64 mpg123-libs-1.25.6-1.el7.x86_64 ncurses-libs-5.9-14.20130511.el7_4.x86_64 nspr-4.21.0-1.el7.x86_64 nss-3.44.0-7.el7_7.x86_64 nss-softokn-freebl-3.44.0-8.el7_7.x86_64 nss-util-3.44.0-4.el7_7.x86_64 openldap-2.4.44-21.el7_6.x86_64 openssl-libs-1.0.2k-19.el7.x86_64 pcre-8.32-17.el7.x86_64 postgresql-libs-9.2.24-2.el7_7.x86_64 speex-1.2-0.19.rc1.el7.x86_64 sqlite-3.7.17-8.el7_7.1.x86_64
进入调试模式后,使用bt
查看堆栈信息:
unixODBC-2.3.1-14.el7.x86_64 xz-libs-5.2.2-1.el7.x86_64 zlib-1.2.7-18.el7.x86_64(gdb) bt
#0 0x00007f0f09e0619b in onSentenceEnd (cbEvent=0x7f0f08dcd810, cbParam=0x7f0f08fd3020) at mod_asr.cpp:331
#1 0x00007f0f09b1cff9 in AlibabaNls::SpeechTranscriberListener::handlerFrame(AlibabaNls::NlsEvent) () from /usr/local/freeswitch/lib/libnlsCppSdk.so
#2 0x00007f0f09b03b9c in AlibabaNls::NlsSessionBase::handlerFrame(AlibabaNls::util::WebsocketFrame) () from /usr/local/freeswitch/lib/libnlsCppSdk.so
#3 0x00007f0f09b0dba7 in AlibabaNls::transport::engine::webSocketAgent::workloop() () from /usr/local/freeswitch/lib/libnlsCppSdk.so
#4 0x00007f0f09b0cc70 in AlibabaNls::transport::engine::thread_func(void*) () from /usr/local/freeswitch/lib/libnlsCppSdk.so
#5 0x00007f0f9b121e65 in start_thread () from /lib64/libpthread.so.0
#6 0x00007f0f9a77588d in clone () from /lib64/libc.so.6
由于我在模块编译过程中加入了-g
所以能够直接定位到代码行级别的问题,后来查看自己的源码发现,在极端情况下,确实会出现分母为0
的情况,导致Segmentation fault
,前几天还在提醒同事分母不能为0
,自己却遗漏了,实在是疏忽大意。