在完成公司项目,测试进程的时候,经常会发现日志到了某一段特定的代码的时候就没了,进程直接退出,也没有捕获到任何的异常信息,如果日志打印的较多还可能比较容易发现问题,如果日志较少,就很难进行进一步的查错了。
但是发现在该目录下生成了一个core文件,可以帮助我们查找程序崩溃的原因。
在linux系统下,如果进程不能正常运行,就可能会产生core文件。core文件就是当前内存状态的一个映像,同时加上一些调试信息。
bug和操作系统或硬件的保护机制都会导致程序异常终止,操作系统会kill掉这些进程并产生core文件。
使用 ulimit -a 命令可以查看当前系统资源的一些限制信息,如下图所示:
其中的core file size 如果设置为0的话,当程序崩溃的时候就不会产生core文件。
#设置core文件大小为无限
ulimit -c unlimited
#阻止系统生成core文件
ulimit -c 0
注意:这条命令只在当前生效,如果希望永久生效,就需要在.bash_profile中加上这条命令。
/proc/sys/kernel/core_uses_pid可以控制产生的core文件的文件名中是否添加pid作为扩展,如果添加则文件内容为1,否则为0。需要有超级用户的权限才能进行修改。
/proc/sys/kernel/core_pattern 可以设置格式化的 core文件保存位置或文件名,默认的是|/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e。需要修改的话,可以使用如下命令:
echo "/corefile/core-%e-%p-%t"> /proc/sys/kernel/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 - insertcoredumping executable name into filename 添加命令名
在linux上可以使用gdb来调试core文件,格式为:
gdb [程序名] [core文件名]
如果你不知道这个core文件到底是哪个程序生成的,可以使用
gdb -c[core 文件名] 来查看生成此core文件的程序名。
显示结果中可以看出程序名,可能像下面这样
Core wasgenerated by `./test'.
之后进入gdb调试状态,输入 where就可以看到程序崩溃时堆栈信息(当前函数之前的所有已调用函数的列表(包括当前函数),我们可以借此找出是程序中的哪个部分导致了程序崩溃。注意:在编译程序的时候要加入选项-g。
编译如下的程序:
#include
using namespace std;
class A
{
public:
int a;
};
void fun()
{
A*t = new A();
t->a = 1;
cout << t->a << endl;
delete t;
delete t;
}
int main()
{
fun();
return 0;
}
执行:
./test
结果为:
1
*** glibc detected *** ./test: double freeor corruption (fasttop): 0x09fd7008 ***
======= Backtrace: =========
/lib/libc.so.6[0x3ebe31]
/usr/lib/libstdc++.so.6(_ZdlPv+0x22)[0x43fc552]
./test[0x8048705]
./test[0x8048712]
/lib/libc.so.6(__libc_start_main+0xe6)[0x391d26]
./test[0x8048611]
======= Memory map: ========
00327000-00328000 r-xp 00000000 00:000 [vdso]
00334000-00351000 r-xp 00000000 08:02926955 /lib/libgcc_s-4.4.7-20120601.so.1
00351000-00352000 rw-p 0001d000 08:02926955 /lib/libgcc_s-4.4.7-20120601.so.1
00355000-00373000 r-xp 00000000 08:02926876 /lib/ld-2.12.so
00373000-00374000 r--p 0001d000 08:02926876 /lib/ld-2.12.so
00374000-00375000 rw-p 0001e000 08:02926876 /lib/ld-2.12.so
0037b000-0050c000 r-xp 00000000 08:02926877 /lib/libc-2.12.so
0050c000-0050e000 r--p 00191000 08:02926877 /lib/libc-2.12.so
0050e000-0050f000 rw-p 00193000 08:02926877 /lib/libc-2.12.so
0050f000-00512000 rw-p 00000000 00:00 0
00543000-0056b000 r-xp 00000000 08:02926889 /lib/libm-2.12.so
0056b000-0056c000 r--p 00027000 08:02926889 /lib/libm-2.12.so
0056c000-0056d000 rw-p 00028000 08:02926889 /lib/libm-2.12.so
0434d000-0442e000 r-xp 00000000 08:02155001 /usr/lib/libstdc++.so.6.0.13
0442e000-04432000 r--p 000e0000 08:02155001 /usr/lib/libstdc++.so.6.0.13
04432000-04434000 rw-p 000e4000 08:02155001 /usr/lib/libstdc++.so.6.0.13
04434000-0443a000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 08:02419326 /home/wcl/fate/src/app/test/test
08049000-0804a000 rw-p 00000000 08:02419326 /home/wcl/fate/src/app/test/test
09fd7000-09ff8000 rw-p 00000000 00:000 [heap]
b7719000-b771c000 rw-p 00000000 00:00 0
b7727000-b772a000 rw-p 00000000 00:00 0
bfd2a000-bfd3f000 rw-p 00000000 00:000 [stack]
Aborted (core dumped)
因为我们对一个已经delete过了的指针再次delete,所以程序down掉了,可以看到在当前目录下已经生成了一个core.4377的文件,4377就是之前程序启动的PID。
调试core文件:
gdb test core.4377
进入gdb调试后,键入where命令:
(gdb) where
#0 0x00327424 in __kernel_vsyscall ()
#1 0x003a5b11 in raise () from /lib/libc.so.6
#2 0x003a73ea in abort () from /lib/libc.so.6
#3 0x003e59d5 in __libc_message () from /lib/libc.so.6
#4 0x003ebe31in malloc_printerr () from /lib/libc.so.6
#5 0x043fc552 in operator delete(void*) () from/usr/lib/libstdc++.so.6
#6 0x08048705 in fun() ()
#7 0x08048712 in main ()
可以很明显的看出是在main函数中调用fun函数,之后delete指针的时候出错了,后面的函数调用栈就是程序输出错误信息的部分了,和我们的用户代码无关。到这一步,我们就能推断是是fun()这个函数中delete某个指针的时候出现了错误,就可以有的放矢地查找具体的问题了。