gdb-7.2-64.el6_5.2.x86_64
/usr/bin/gcore
/usr/bin/gdb
/usr/bin/gdb-add-index
/usr/bin/gdbtui
/usr/bin/gstack
/usr/bin/pstack
[root@coreserv tmp]# rpm -qa|grep abr
abrt-libs-2.0.8-6.el6.centos.x86_64
abrt-addon-ccpp-2.0.8-6.el6.centos.x86_64
abrt-2.0.8-6.el6.centos.x86_64
abrt-addon-kerneloops-2.0.8-6.el6.centos.x86_64
abrt-tui-2.0.8-6.el6.centos.x86_64
abrt-cli-2.0.8-6.el6.centos.x86_64
abrt-addon-python-2.0.8-6.el6.centos.x86_64
ABRT (Automated Bug Reporting Tool) Daemon:
ABRT is an application, included in Fedora Linux Distribution, that is used to report bugs in the software packages whenever crash occurs. Due to this, ABRT also helps in creation of core dump files. Multiple packages may be needed to run various features of ABRT daemon, and their listing is as follows.
$service abrtd status
$sysctl -a|grep core_pattern
kernel.core_pattern = |/usr/libexec/abrt-hook-ccpp /var/cache/abrt %p %s %u %c
By default, “abrtd” created core dump files only for those executable (or packages) that are managed by “rpm” (red hat package manager) utility. To enable “abrtd” for non-rpm application (something you compiled locally and are not managed through rpm), you need to edit the file cat “/etc/abrt/abrt.conf” , and change the value of the field “ProcessUnpackaged” to “yes” as follows:
永久设置 abrt :
ProcessUnpackaged = no #(before editing the file)
ProcessUnpackaged = yes #(after editing the file)
gcore命令
问题:
当调试一个程序的时候,理想状态是不重启应用程序就获取core文件。
解决:
gcore命令可以使用下面步骤来获取core文件:
1. 确认gdb软件包已经被正确安装。
2. 使用调试参数编译程序(例如: gcc中使用"-g"选项),编译后不要去除文件的调试符号信息。
3. 执行应用程序。
4. 执行gcore命令生成指定应用程序的core文件并且保存在当前目录下。
$ gcore pid (进程号)
core简介
Core文件其实就是内存的映像,当程序崩溃时,存储内存的相应信息,主用用于对程序进行调试。当程序崩溃时便会产生core文件,其实准确的应该说是core dump 文件,默认生成位置与可执行程序位于同一目录下,文件名为core.***,其中***是某一数字。
造成程序coredump的原因很多,这里根据以往的经验总结一下:
1 内存访问越界
2 多线程程序使用了线程不安全的函数。
3 多线程读写的数据未加锁保护。
对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成core dump
4 非法指针
a) 使用空指针
b) 随意使用指针转换。
5 堆栈溢出
不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
在程序不寻常退出时,内核会在当前工作目录下生成一个core文件(是一个内存映像,同时加上调试信息)。使用gdb来查看core文件,可以指示出导致程序出错的代码所在文件和行数。
当系统中的一些程序在遇到一些错误以及crash时,系统会自动产生core文件记录crash时刻系统信息,包括内存和寄存器信息,用以程序员日 后debug时可以使用。这些错误包括段错误、非法指令、总线错误或用户自己生成的退出信息等等,一般地,core文件在当前文件夹中存放。core dump又叫核心转储, 当程序运行过程中发生异常, 程序异常退出时, 由操作系统把程序当前的内存状况存储在一个core文件中, 叫core dump. (linux中如果内存越界会收到SIGSEGV信号,然后就会core dump)
有时候一些问题只能在特定的环境下才能重现,重现的时机和条件都难以把握,可能很多次的测试才能偶尔的重现一次问题,这给我们的调试和修改都带来很多不便之处,还有一种难以跟踪调试的情形,在大型的软件项目中,要从数万行甚至更多的代码中准确的找到问题所在,靠设断点和单步跟踪的方法是很麻烦很需要时间的,这些问题可以通过Core Dump的方式,或者说事后调试(postmortem debug)技术,来协助分析.主要方法是在程序崩溃的时候,将程序的内存映象加上调试信息保存到一个文件中,这后通过分析这个所谓的Core文件来找到程序崩溃的原因.Core Dump的名称来源于以前工业界的叫法---当内存还是线圈的时候,它被叫做Core,我们可以利用GDB来分析core文件来查找出错的原因
设置查看当前core文件的系统级配置
core文件的生成开关和大小限制
关闭或阻止core文件生成:
$ulimit -c 0
打开core文件生成:
$ulimit -c unlimited
检查core文件的选项是否打开:
$ulimit -a
如果想通过limits.conf里面的设置来控制用户是否可以产生core文件,需要把/etc/profile里面的ulimits设置注释掉
以上配置只对当前会话起作用,下次重新登陆后,还是得重新配置。要想配置永久生效,
两种方法
得在/etc/profile或者/etc/security/limits.conf文件中进行配置。
首先以root权限登陆,然后打开/etc/security/limits.conf文件,进行配置:
#vim /etc/security/limits.conf
<domain> <type> <item> <value>
* soft core unlimited
或者在/etc/profile中作如下配置:
#vim /etc/profile
ulimit -S -c unlimited >/dev/null 2>&1
或者想配置只针对某一用户有效,则修改此用户的~/.bashrc或者~/.bash_profile文件:
ulimit -c unlimited
ulimit -c 0 是禁止产生core文件,而ulimit -c 1024则限制产生的core文件的大小不能超过1024kb
1)使用ulimit -c命令可查看core文件的生成开关。若结果为0,则表示关闭了此功能,不会生成core文件。
2)使用ulimit -c filesize命令,可以限制core文件的大小(filesize的单位为kbyte)。若ulimit -c unlimited,则表示core文件的大小不受限制。
proc/sys/kernel/core_pattern 可以设置格式化的 core 文件保存位置或文件名 ,默认的是|/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e。需要修改的话,可以使用如下命令:
echo "/media/test/core-%e-%p-%t">/proc/sys/kernel/core_pattern
在RHEL/CentOS 64位(32位没用过)6.0以上版本中,有core文件被截断的问题,即使你已经设置了ulimit -S -c unlimited。
原因好像是因为core pattern设置是abrt,abrt的问题导致core文件很小或者不产生core文件。解决的方法是不使用abrt作为core pattern。
如果core pattern设置成了abrt,改成core方式:
先查看当前系统设置
[root@localhost log]# sysctl -a | grep core_pattern
kernel.core_pattern = core
linux-y94w:/ # sysctl -w kernel.core_pattern=core.%p.%e
kernel.core_pattern = core.%p.%e
或者:
linux-y94w:/ # sysctl -w kernel.core_pattern=core.%p
kernel.core_pattern = core.%p
core文件名与生成路径
/proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式。
可通过以下命令修改此文件:
echo "/corefile/core-%e-%p-%t" > core_pattern,可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%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 添加命令名
[root@localhost log]# cat /proc/sys/kernel/core_pattern
core
[root@localhost log]# cat /proc/sys/kernel/core_pipe_limit
0
/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件统一命名为core。
可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
[root@localhost log]# cat /proc/sys/kernel/core_uses_pid
1
并且注意,只有超级用户才可以修改这两个表。/proc/sys/kernel/core_uses_pid可以控制产生的core文件的文件名中是否添加pid作为扩展,如果添加则文件内容为1,否则为0
core_pattern接受的是core文件名称的pattern,它包含任何字符串,并且用%作为转移符号生成一些标示符,为core文件名称加入特殊含义。已定义的标示符有如下这些:
%%:相当于%
%p:相当于<pid>
%u:相当于<uid>
%g:相当于<gid>
%s:相当于导致dump的信号的数字
%t:相当于dump的时间
%e:相当于执行文件的名称
%h:相当于hostname
除以上这些标志位外,还规定:
1、末尾的单个%可以直接去除;
2、%加上除上述以外的任何字符,%和该字符都会被去除;
3、所有其他字符都作为一般字符加入名称中;
4、core文件的名称最大值为64个字节(包括' ');
5、core_pattern中默认的pattern为core;
6、为了保持兼容性,通过设置core_uses_pid,可以在core文件的末尾加上%p;
7、pattern中可以包含路径信息。
如果想让修改永久生效,则需要修改配置文件,如 .bash_profile、/etc/profile或/etc/security/limits.conf。
vi /etc/profile
ulimit -S -c 204800
ulimit -u 4096
ulimit -n 20480
测试产生与查看分析core文件
一个小方法来测试产生core文件
直接输入指令:kill -s SIGSEGV $$ $$为希望产生core文件的进程名
kill -9 pid也会产生以下类似文件
asterisk重启会在/tmp/下产生以下文件
-rw------- 1 root root 337735680 Aug 27 19:54 core.109-com1-2014-08-27T19:54:07+0800
[root@coreserv tmp]# gdb asterisk core.coreserv-2014-08-28T17:44:33+0800
gdb --core=core.12345(core dump文件名)或gdb exe名 core名 例如:gdb asterisk core文件
bt 查看程序运行到哪儿,backtrace
file exe名 找exe位置
l 列出代码
where 显示在哪儿down掉 GDB中键入where,就会看到程序崩溃时堆栈信息(当前函数之前的所有已调用函数的列表(包括当前函数),gdb只显示最近几个)
默认情况下,GCC在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息,可以使用GCC的-g或者-ggdb选项。GCC在产生调试符号时,同样采用了分级的思路,开发人员可以通过在-g选项后附加数字1、2或3来指定在代码中加入调试信息的多少。默认的级别是2(-g2),此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。级别3(-g3)包含级别2中的所有调试信息,以及源代码中定义的宏。级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史,堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法,两者都是经常用到的调试手段。
仔细分析一下GDB给出的输出结果不难看出,程序是由于段错误而导致异常中止的,说明内存操作出了问题,具体发生问题的地方是在调用_IO_vfscanf_internal ( )的时候。为了得到更加有价值的信息,可以使用GDB提供的回溯跟踪命令backtrace,执行结果如下:
(gdb) backtrace
#0 0×4008576b in _IO_vfscanf_internal () from /lib/libc.so.6
#1 0xbffff0c0 in ?? ()
#2 0×4008e0ba in scanf () from /lib/libc.so.6
#3 0×08048393 in main () at crash.c:11
#4 0×40042917 in __libc_start_main () from /lib/libc.so.6
跳过输出结果中的前面三行,从输出结果的第四行中不难看出,GDB已经将错误定位到crash.c中的第11行了。现在仔细检查一下
(gdb) frame 3
#3 0×08048393 in main () at crash.c:11
11 scanf("%d", input);
第四行是main()函数的代码,而前几行都是系统代码
还有一个小问题,网上很少提到:被调试的程序必须和源码放在同一台机器上,才能用list命令列出源码,否则提示找不到。
[root@109-com1 tmp]# gdb asterisk core.109-com1-2014-12-09T09:52:26+0800 Loaded symbols for /lib64/libnss_dns.so.2 Core was generated by `/usr/sbin/asterisk -f -vvvg -c'. Program terminated with signal 11, Segmentation fault. #0 0x00007f528e49879a in _int_free () from /lib64/libc.so.6 Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6_5.3.x86_64 ncurses-libs-5.7-3.20090208.el6.x86_64 程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)。要打印堆栈,发出命令 'bt'('backtrace' [回溯] 的缩写): 实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(0 号帧)。可以使用命令 'frame' 打印当前帧。要查看 main 函数(在 1 号帧中)中的变量,可以发出 'frame 1' 切换到 1 号帧,然后发出 'info locals' 命令: 从下面可以看出函数的调用栈信息:clone()->start_thread()->......->_int_free(),由栈顶5到栈底0 (gdb) bt #0 0x00007f528e49879a in _int_free () from /lib64/libc.so.6 #1 0x00007f5276acd3da in msrm_rm_ok_unit (msg_sn=65896) at chan_flt_comserver.c:11870 #2 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:19277 #3 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:895 #4 0x00007f528ee8f9d1 in start_thread () from /lib64/libpthread.so.0 #5 0x00007f528e508b5d in clone () from /lib64/libc.so.6 (gdb) where #0 0x00007f528e49879a in _int_free () from /lib64/libc.so.6 #1 0x00007f5276acd3da in msrm_rm_ok_unit (msg_sn=65896) at chan_flt_comserver.c:11870 #2 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:19277 #3 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:895 #4 0x00007f528ee8f9d1 in start_thread () from /lib64/libpthread.so.0 #5 0x00007f528e508b5d in clone () from /lib64/libc.so.6 (gdb) f #0 0x00007f528e49879a in _int_free () from /lib64/libc.so.6 (gdb) info locals No symbol table info available. (gdb) f 2 #2 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:19277 19277 msrm_rm_ok_unit(rsp_sn); (gdb) info locals thire_addr = {sin_family = 2, sin_port = 16796, sin_addr = {s_addr = 33728704}, sin_zero = "