01 什么coredump
当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中(core文件),这种行为就叫做 Core Dump 或者叫做"核心转储",利用 coredump 可以帮助我们快速定位程序崩溃位置。
通常情况下,core文件会包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种函数调用堆栈信息等,我们可以理解为是程序工作当前状态存储生成的一个文件,许多的程序出错的时候都会产生一个core文件,通过工具分析这个文件,我们可以定位到程序异常退出的时候对应的堆栈调用等信息,找出问题所在并进行及时解决。
02 开启和关闭coredump功能
已centos6.9为例说明。
02.01 查看coredump功能是否打开
ulimit -c # 如果为 0 表示coredump开关处于关闭状态
02.02 打开coredump功能
# 指定coredump文件大小,即打开coredump功能
#ulimit -c 1024 # 1024个blocks,一般1block=512bytes
ulimit -c unlimited # 取消大小限制,开发,试运行阶段可以使用次参数
02.03 检查coredump文件的选项参数
ulimit -a # 显示当前所有limit信息
core file size (blocks, -c) unlimited
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15154
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) unlimited
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 1024
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
02.04 ulimit 参数介绍
命令参数 描述 例子
-H 设置硬资源限制,一旦设置不能增加。 ulimit –Hs 64;限制硬资源,线程栈大小为 64K。
-S 设置软资源限制,设置后可以增加,但是不能超过硬资源设置。 ulimit –Sn 32;限制软资源,32 个文件描述符。
-a 显示当前所有的 limit 信息 ulimit –a;显示当前所有的 limit 信息
-c 最大的 core 文件的大小, 以 blocks 为单位 ulimit –c unlimited; 对生成的 core 文件的大小不进行限制
-d 进程最大的数据段的大小,以 Kbytes 为单位 ulimit -d unlimited;对进程的数据段大小不进行限制
-f 进程可以创建文件的最大值,以 blocks 为单位 ulimit –f 2048;限制进程可以创建的最大文件大小为 2048 blocks
-l 最大可加锁内存大小,以 Kbytes 为单位 ulimit –l 32;限制最大可加锁内存大小为 32 Kbytes
-m 最大内存大小,以 Kbytes 为单位 ulimit –m unlimited;对最大内存不进行限制
-n 可以打开最大文件描述符的数量 ulimit –n 128;限制最大可以使用 128 个文件描述符
-p 管道缓冲区的大小,以 Kbytes 为单位 ulimit –p 512;限制管道缓冲区的大小为 512 Kbytes
-s 线程栈大小,以 Kbytes 为单位 ulimit –s 512;限制线程栈的大小为 512 Kbytes
-t 最大的 CPU 占用时间,以秒为单位 ulimit –t unlimited;对最大的 CPU 占用时间不进行限制
-u 用户最大可用的进程数 ulimit –u 64;限制用户最多可以使用 64 个进程
-v 进程最大可用的虚拟内存,以 Kbytes 为单位 ulimit –v 200000;限制最大可用的虚拟内存为 200000 Kbytes
02.05 配置coredump永久生效
ulimit -c unlimited
类型的ulimit -c
仅仅是设置当前session中启动转储功能。
要永久生效,需要修改配置文件。
02.05.01 在配置文件中设置coredemp生效
可以在/etc/profile,~/.bash_profile, ~/.bashrc 中的某一个加入
ulimit -c unlimited
通过 source /etc/profile
类型的命令,使设置在当前session中生效。
02.05.02
修改/etc/security/limits.conf
#vim /etc/security/limits.conf
<domain> <type> <item> <value>
* soft core unlimited
03 coredump文件的存储位置和文件名
03.01 转储文件默认位置
coredump文件默认的存储位置与对应的可执行程序在同一目录下,文件名是core.pid,可以通过下面的命令看到core文件的存在位置:
cat /proc/sys/kernel/core_pattern # 缺省值是|/usr/libexec/abrt-hook-ccpp %s %c %p %u %g %t e
注意:这里是指在进程当前工作目录的下创建。通常与程序在相同的路径下。但如果程序中调用了chdir函数,则有可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。有好多程序崩溃了,我们却找不到core文件放在什么位置。和chdir函数就有关系。当然程序崩溃了不一定都产生 core文件。
echo "/data/coredump/core" > /proc/sys/kernel/core_pattern # 把core文件生成到/data/coredump/core目录下
03.02 coredump文件的命名
缺省情况下,内核在coredump时所产生的coredump文件放在与该程序相同的目录中,并且文件名固定为core。
A. coredump文件名带pid
/proc/sys/kernel/core_uses_pid可以控制core文件的文件名中是否添加pid作为扩展。文件内容为1,表示添加pid作为扩展名,生成的core文件格式为core.xxxx;为0则表示生成的core文件同一命名为core。可通过以下命令修改此文件:
echo "1" > /proc/sys/kernel/core_uses_pid
B. coredump文件名格式
proc/sys/kernel/core_pattern可以控制core文件保存位置和文件名格式,可通过以下命令修改此文件:
echo "/corefile/core-%e-%p-%t" > core_pattern # 可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
C. coredump 文件名参数说明
%% - 单个%字符
%p - 添加pid
%u - 添加当前uid
%g - 添加当前gid
%s - 添加导致产生core的信号
%t - 添加core文件生成时的unix时间
%h - 添加主机名
%e - 添加程序文件名
04 造成程序core的原因
造成程序coredump的原因有很多,这里总结一些比较常用的经验吧:
04.01 内存访问越界
A. 由于使用错误的下标,导致数组访问越界。
B. 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符。
C. 使用strcpy, strcat, sprintf, strcmp, strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。
04.02 多线程程序使用了线程不安全的函数
应该使用下面这些可重入的函数,它们很容易被用错
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)
04.03 多线程读写的数据未加锁保护
对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump。
04.04 非法指针
A. 使用空指针
B. 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。
04.05 堆栈溢出:
不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
05 用GDB调试coredump文件
注意:调试coredump文件最好是debug。因为有符号文件,方便问题定位。
05.01 一个产生coredump的c文件
testdump.c
void test_core_dump() {
const char* str = "test_core_dump";
char* p = (char*)str;
p[0] = '0'; // 段错误程序终止,生成core文件
}
int main(int argc, char* argv[]) {
test_core_dump();
return 0;
}
编译
gcc -o testdump testdump.c
运行
./testdump
段错误 (core dumped)
ls core*
core.18293
05.02 判断是否为core文件
在类unix系统下,coredump文件本身主要的格式也是ELF格式,可以通过简单的file命令进行快速判断。
file core.18293
core.18293: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './testdump', real uid: 500, effective uid: 500, real gid: 500, effective gid: 500, execfn: './testdump', platform: 'x86_64'
05.01 gdb调试方法01
A. 启动gdb,进入core文件,命令格式:gdb ["exec file params"] [core file]
,用法示例:
gdb ./testdump core.18293
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 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 /home/soft/test/testdump...(no debugging symbols found)...done.
[New Thread 18293]
Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/lib64/libc-2.12.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug/lib64/ld-2.12.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./testdump'.
Program terminated with signal 11, Segmentation fault.
#0 0x000000000040048c in test_core_dump ()
B. 进入gdb
后,查找段错误位置:where
或者 bt
,用法示例:
(gdb) where
#0 0x000000000040048c in test_core_dump ()
#1 0x00000000004004aa in main ()
(gdb) bt
#0 0x000000000040048c in test_core_dump ()
#1 0x00000000004004aa in main ()
05.02 gdb调试方法02
A. 启动gdb,进入core文件,命令格式:gdb -c [core file] //或 gdb --core=[core file]
B. 在进入gdb后,指定core文件对应的符号表,命令格式:(gdb) file [exec file]
C. 查找段错误位置:where或者bt。用法示例:bt
05.03 带符号的coredump文件调试效果
编译时,带-g
参数,即debug
版本产生coredump
文件
$ gcc -g -o testdumpg testdump.c
$ ./testdumpg
段错误 (core dumped)
$ gdb "./testdumpg" core.18
core.18293 core.18473
$ gdb "./testdumpg" core.18473
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 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 /home/soft/test/testdumpg...done.
[New Thread 18473]
Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/lib64/libc-2.12.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug/lib64/ld-2.12.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./testdumpg'.
Program terminated with signal 11, Segmentation fault.
#0 0x000000000040048c in test_core_dump () at testdump.c:5
5 p[0] = '0'; // 段错误程序终止,生成core文件
(gdb) where
#0 0x000000000040048c in test_core_dump () at testdump.c:5
#1 0x00000000004004aa in main (argc=1, argv=0x7ffff6b3ff08) at testdump.c:10
(gdb) q
05.04 不带符号的coredump
文件调试效果
$ gcc -o testdump testdump.c
$ ./testdump
段错误 (core dumped)
$ gdb "./testdump" core.18506
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 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 /home/soft/test/testdump...(no debugging symbols found)...done.
[New Thread 18506]
Reading symbols from /lib64/libc.so.6...Reading symbols from /usr/lib/debug/lib64/libc-2.12.so.debug...done.
done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...Reading symbols from /usr/lib/debug/lib64/ld-2.12.so.debug...done.
done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Core was generated by `./testdump'.
Program terminated with signal 11, Segmentation fault.
#0 0x000000000040048c in test_core_dump ()
(gdb) where
#0 0x000000000040048c in test_core_dump ()
#1 0x00000000004004aa in main ()
(gdb) bt
#0 0x000000000040048c in test_core_dump ()
#1 0x00000000004004aa in main ()
(gdb) q