coredump的使用
本章我们主要讲述一下Linux下C/C++程序coredump功能的使用。
Linux中很多信号(signal)的默认动作会使进程结束并产生一个coredump文件,即一个该进程结束时的内存镜像文件。一些调试器(如GDB)可以通过该镜像来获得程序结束时候的一些状态。下面列出几种信号,它们在发生时会产生coredump文件:
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 |
在控制终端,我们可以使用 ctrl+\ 来终止一个进程,这会向进程发出SIGQUIT信号,默认是会产生coredump的。还有其他情景也会产生coredump,如:程序调用abort()函数、访存错误、非法指令等等。
1. 开启coredump功能
1.1 修改coredump文件的最大值
1) 在当前会话终端中输入命令ulimit -c
,若输出结果为0,说明默认是关闭core dump的。即当程序异常终止时,也不会产生coredump文件。
2) 我们可以使用ulimit -c unlimited
来开启coredump功能,并且不限制coredump文件的大小;如果需要限制文件的大小,将unlimited改成你想生成core文件最大的大小(注意单位为blocks)。
3) 用上面的命令只会对当前会话终端有效,如果想永久生效,可以修改/etc/security/limits.conf文件,增加如下一行
注意: 此种方式会在系统下一次重启时生效
1.2 修改core文件保存路径
1) 默认情况下,生成的core文件保存在可执行文件所在的目录下,文件名就为core
2) 通过修改/proc/sys/kernel/core_uses_pid文件,可以让生成的core文件名是否自动加上pid号。例如:
# echo 1>/proc/sys/kernel/core_uses_pid
这样生成的core文件名将会变成core.pid,其中pid表示该进程的进程号。
3) 还可以通过修改/proc/sys/kernel/core_pattern来控制生成core文件的保存位置以及文件名格式。格式说明如下:
-
%% 指示一个百分号字符
-
%p 被dumped进程的进程号
-
%u 被dumped进程的实际用户ID
-
%g 被dumped进程的实际组ID
-
%s 引发产生core文件的信号
-
%t 产生dump时候的时间。从1970-1-1 00:00:00到当前的秒数
-
%h 主机名字
-
%e 可执行文件的文件名(不包括路径前缀)
-
%E 可执行文件的文件名(包括路径前缀)
-
%c 指定产生core文件的大小限制
例如可以用如下命令:
这样设置之后生成的core文件保存在/tmp/
目录下,文件名格式为”corefile-progname-pid-timestap”。
2. 使用GDB调试core文件
产生了core文件,我们该如何使用该core文件进行调试呢? Linux可以使用GDB来调试core文件,步骤如下:
1) 首先使用gcc编译源文件,加上-g
以增加调试信息
2) 按照上面打开coredump,以使程序异常终止时能生成core文件
3) 运行程序,当core dump之后,使用命令 gdb program core 来查看core文件,其中program为可执行程序名,core为生成的core文件名.
下面用一个简单的例子test.c来说明:
编译运行(添加-g选项,以增加调试信息):
[root@localhost test-src]# gcc -g -o test test.c [root@localhost test-src]# ./test Segmentation fault (core dumped)
查看/tmp目录我们看到产生了coredump文件:
[root@localhost test-src]# cat /proc/sys/kernel/core_pattern /tmp/corefile-%e-%p-%t [root@localhost test-src]# ls -al /tmp/corefile-test-26150-1514885060 -rw-------. 1 root root 249856 Jan 2 01:24 /tmp/corefile-test-26150-1514885060
采用gdb调试该coredump文件:
[root@localhost test-src]# gdb ./test /tmp/corefile-test-26150-1514885060 GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7 Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/test-src/test...done. [New LWP 26150] Core was generated by `./test'. Program terminated with signal 11, Segmentation fault. #0 0x00000000004004f9 in func (p=0x0) at test.c:5 5 int y = *p; Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7.x86_64 (gdb) r Starting program: /root/test-src/./test Program received signal SIGSEGV, Segmentation fault. 0x00000000004004f9 in func (p=0x0) at test.c:5 5 int y = *p; (gdb) bt #0 0x00000000004004f9 in func (p=0x0) at test.c:5 #1 0x0000000000400526 in main (argc=1, argv=0x7fffffffe4c8) at test.c:14 (gdb)
2.1 无调试信息的情况下找core的代码行
如下代码(test.c):
1) 编译
执行如下命令进行编译(注: 这里特意不加上调试信息):
# gcc -o test test.c # ls test test.c
2) 产生coredump文件
开启coredump并运行:
# ulimit -c unlimited # ./test # ls core.25238 test test.c
3) gdb调试coredump文件
如下我们使用gdb来调试该coredump文件:
在上面没有调试信息的情况下,打开coredump堆栈,并不会直接显示core的代码行。
此时我们用frame 4跳到堆栈的第4帧,然后用disassemble
命令打开该帧函数的汇编代码。从上面可以看到,coredump是发生在箭头所指定位置的。接下来,我们就可以重点分析那一段代码就可以了。
[参看]: