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个字节(包括'\0');
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.
Core was generated by `/usr/sbin/asterisk -f -vvvg -c'.
Program terminated with signal , Segmentation fault.
# 0x00007f528e49879a in _int_free () from /lib64/libc.so.
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6_5..x86_64 ncurses-libs-5.7-3.20090208.el6.x86_64
程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 号帧中(“底部”帧)。要打印堆栈,发出命令 'bt'('backtrace' [回溯] 的缩写):
实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数( 号帧)。可以使用命令 'frame' 打印当前帧。要查看 main 函数(在 号帧中)中的变量,可以发出 'frame 1' 切换到 号帧,然后发出 'info locals' 命令:
从下面可以看出函数的调用栈信息:clone()->start_thread()->......->_int_free(),由栈顶5到栈底0 (gdb) bt
# 0x00007f528e49879a in _int_free () from /lib64/libc.so.
# 0x00007f5276acd3da in msrm_rm_ok_unit (msg_sn=) at chan_flt_comserver.c:
# 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:
# 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:
# 0x00007f528ee8f9d1 in start_thread () from /lib64/libpthread.so.
# 0x00007f528e508b5d in clone () from /lib64/libc.so.
(gdb) where
# 0x00007f528e49879a in _int_free () from /lib64/libc.so.
# 0x00007f5276acd3da in msrm_rm_ok_unit (msg_sn=) at chan_flt_comserver.c:
# 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:
# 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:
# 0x00007f528ee8f9d1 in start_thread () from /lib64/libpthread.so.
# 0x00007f528e508b5d in clone () from /lib64/libc.so.
(gdb) f
# 0x00007f528e49879a in _int_free () from /lib64/libc.so.
(gdb) info locals
No symbol table info available.
(gdb) f
# 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:
msrm_rm_ok_unit(rsp_sn);
(gdb) info locals
thire_addr = {sin_family = , sin_port = , sin_addr = {s_addr = }, sin_zero = "\000\000\000\000\000\000\000"}
recv_buffer = "2\000\001\001h", '\000' <repeats times>, "\002", '\000' <repeats times>, "@", '\000' <repeats times>, "\002\000\000\000\060\000\000\000[\000\000\000|\000\000\000w\000\000\000n", '\000' <repeats times>, "@\005\002\000\000\000\000\000\300\n\000\fR\177\000\000\030\000\000\000\000\000\000\000 \000\000\fR\177\000\000\200\376z\216R\177\000\000\066\246I\216R\177", '\000' <repeats times>"\260, ]x\205R\177\000\000\060;\342\362\377\177\000\000\300ix\205R\177\000\000\004\000\000\000\000\000\000\000\a\000\000\000\000\000\000\000\024BL", '\000' <repeats times>, " \316O\000\000\000\000\000\000\000\000\000\067\001\000\000t\245O\000\000\000\000\000\030\000\000\000\000\000\000\000\205mB", '\000' <repeats times>...
recv_len =
rsp_sn =
thire_addr_len =
__PRETTY_FUNCTION__ = "mec_udp_client_chan_pkg_recv"
(gdb) f 切换栈,用up与down,栈顶就是当前栈
# 0x00007f5276acd3da in msrm_rm_ok_unit (msg_sn=) at chan_flt_comserver.c:
if(NULL != Msrm[msg_sn].pdatabuf) free(Msrm[msg_sn].pdatabuf);
(gdb) info locals 打印当前函数的局部变量与值
p_mec_send_task = 0x7f5200004210
__PRETTY_FUNCTION__ = "msrm_rm_ok_unit"
(gdb) info args 打印当前函数的参数名与值
msg_sn =
(gdb) up
# 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:
msrm_rm_ok_unit(rsp_sn);
(gdb) up
# 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:
ret = a.start_routine(a.data);
(gdb) up
# 0x00007f528ee8f9d1 in start_thread () from /lib64/libpthread.so. (gdb) frame
# 0x00007f5276aefd08 in mec_udp_client_chan_pkg_recv (p=0x0) at chan_flt_comserver.c:
msrm_rm_ok_unit(rsp_sn);
(gdb) l list: 列出当前位置之后的10行代码;list line_number: 列出line_number之后的十行代码
if(recv_buffer[] == '')
{
rsp_sn = CHARS4TOINT(recv_buffer+);
if( != rsp_sn)
{
msrm_rm_ok_unit(rsp_sn);
}
else
{
mec_mantenance_refresh();
(gdb) info locals 列出当前函数的局部变量
thire_addr = {sin_family = , sin_port = , sin_addr = {s_addr = }, sin_zero = "\000\000\000\000\000\000\000"}
recv_buffer = "2\000\001\001h", '\000' <repeats times>, "\002", '\000' <repeats times>, "@", '\000' <repeats times>, "\002\000\000\000\060\000\000\000[\000\000\000|\000\000\000w\000\000\000n", '\000' <repeats times>, "@\005\002\000\000\000\000\000\300\n\000\fR\177\000\000\030\000\000\000\000\000\000\000 \000\000\fR\177\000\000\200\376z\216R\177\000\000\066\246I\216R\177", '\000' <repeats times>"\260, ]x\205R\177\000\000\060;\342\362\377\177\000\000\300ix\205R\177\000\000\004\000\000\000\000\000\000\000\a\000\000\000\000\000\000\000\024BL", '\000' <repeats times>, " \316O\000\000\000\000\000\000\000\000\000\067\001\000\000t\245O\000\000\000\000\000\030\000\000\000\000\000\000\000\205mB", '\000' <repeats times>...
recv_len =
rsp_sn =
thire_addr_len =
__PRETTY_FUNCTION__ = "mec_udp_client_chan_pkg_recv"
(gdb) info frame
Stack level , frame at 0x7f5285785df0:
rip = 0x7f5276aefd08 in mec_udp_client_chan_pkg_recv (chan_flt_comserver.c:); saved rip 0x4c5654
called by frame at 0x7f5285785ec0, caller of frame at 0x7f5285784d80
source language c.
Arglist at 0x7f5285785de0, args: p=0x0
Locals at 0x7f5285785de0, Previous frame's sp is 0x7f5285785df0
Saved registers:
rbx at 0x7f5285785dd8, rbp at 0x7f5285785de0, rip at 0x7f5285785de8
(gdb) frame
# 0x00000000004c5654 in dummy_start (data=0x1b02a90) at utils.c:
ret = a.start_routine(a.data);
(gdb) info f 打印当前栈层的详细信息
Stack level , frame at 0x7f5285785ec0:
rip = 0x4c5654 in dummy_start (utils.c:); saved rip 0x7f528ee8f9d1
called by frame at 0x7f5285786000, caller of frame at 0x7f5285785df0
source language c.
Arglist at 0x7f5285785eb0, args: data=0x1b02a90
Locals at 0x7f5285785eb0, Previous frame's sp is 0x7f5285785ec0
Saved registers:
rbp at 0x7f5285785eb0, rip at 0x7f5285785eb8 (gdb) help all Command class: aliases ni -- Step one instruction
rc -- Continue program being debugged but run it in reverse
rni -- Step backward one instruction
rsi -- Step backward exactly one instruction
si -- Step one instruction exactly
stepping -- Specify single-stepping behavior at a tracepoint
tp -- Set a tracepoint at specified line or function
tty -- Set terminal for future runs of program being debugged
where -- Print backtrace of all stack frames
ws -- Specify single-stepping behavior at a tracepoint Command class: breakpoints awatch -- Set a watchpoint for an expression
break -- Set breakpoint at specified line or function Command class: data append -- Append target code/data to a local file
display -- Print value of expression EXP each time the program stops
dump -- Dump target code/data to a local file
dump binary -- Write target code/data to a raw binary file
dump binary memory -- Write contents of memory to a raw binary file Command class: files add-symbol-file -- Load symbols from FILE
add-symbol-file-from-memory -- Load the symbols out of memory from a dynamically loaded object file
cd -- Set working directory to DIR for debugger and program being debugged
list -- List specified function or line
--where与bt是一样的 (gdb) help f
Select and print a stack frame.
With no argument, print the selected stack frame. (See also "info frame").
An argument specifies the frame to select.
It can be a stack frame number or the address of the frame.
With argument, nothing is printed if input is coming from
a command file or a user-defined command. (gdb) help where
Print backtrace of all stack frames, or innermost COUNT frames.
With a negative argument, print outermost -COUNT frames.
Use of the 'full' qualifier also prints the values of the local variables. (gdb) help bt
Print backtrace of all stack frames, or innermost COUNT frames.
With a negative argument, print outermost -COUNT frames.
Use of the 'full' qualifier also prints the values of the local variables. (gdb) help l
List specified function or line.
With no argument, lists ten more lines after or around previous listing.
"list -" lists the ten lines before a previous ten-line listing.
One argument specifies a line, and ten lines are listed around that line.
Two arguments with comma between specify starting and ending lines to list.
Lines can be specified in these ways:
LINENUM, to list around that line in current file,
FILE:LINENUM, to list around that line in that file,
FUNCTION, to list around beginning of that function,
FILE:FUNCTION, to distinguish among like-named static functions.
*ADDRESS, to list around the line containing that address.
With two args if one is empty it stands for ten lines away from the other arg. (gdb) help down
Select and print stack frame called by this one.
An argument says how many frames down to go. (gdb) help up
Select and print stack frame that called this one.
An argument says how many frames up to go. (gdb) up
#6 0x00000000004c5654 in dummy_start (data=0x10da9f0) at utils.c:895
895 ret = a.start_routine(a.data);
(gdb) up
#7 0x00007ff1927279d1 in start_thread () from /lib64/libpthread.so.0
(gdb) up
#8 0x00007ff191da0b5d in clone () from /lib64/libc.so.6
(gdb) up
Initial frame selected; you cannot go up. ======================================== (gdb) info line
Line 7278 of "chan_flt_cservice.c" is at address 0x7ff18bd174fb <thd_immediate_client+491> but contains no code.
(gdb) info locals
i = 3 '\003'
j = 0 '\000'
pstCliData = {0x7ff114001260, 0x7ff114000a20, 0x7ff120000a20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
pstRsp = 0x7ff114001260
__PRETTY_FUNCTION__ = "thd_immediate_client" (gdb) info arg
p = 0x0 (gdb) info source
Current source file is chan_flt_cservice.c
Compilation directory is /usr/local/src/asterisk-1.4.21.2/channels
Located in /usr/local/src/asterisk-1.4.21.2/channels/chan_flt_cservice.c
Contains 11643 lines.
Source language is c.
Compiled with DWARF 2 debugging format.
Includes preprocessor macro info.
core文件创建在什么位置
在进程当前工作目录的下创建。通常与程序在相同的路径下。但如果程序中调用了chdir函数,则有可能改变了当前工作目录。这时core文件创建在chdir指定的路径下。有好多程序崩溃了,我们却找不到core文件放在什么位置。和chdir函数就有关系。当然程序崩溃了不一定都产生core文件。
什么时候不产生core文件
在下列条件下不产生core文件:
( a )进程是设置-用户-ID,而且当前用户并非程序文件的所有者;
( b )进程是设置-组-ID,而且当前用户并非该程序文件的组所有者;
( c )用户没有写当前工作目录的许可权;
( d )文件太大。core文件的许可权(假定该文件在此之前并不存在)通常是用户读/写,组读和其他读。
一、要保证存放Coredump的目录存在且进程对该目录有写权限。存放Coredump的目录即进程的当前目录,一般就是当初发出命令启动该进程时所在的目录。但如果是通过脚本启动,则脚本可能会修改当前目录,这时进程真正的当前目录就会与当初执行脚本所在目录不同。这时可以查看”/proc/<进程pid>/cwd“符号链接的目标来确定进程真正的当前目录地址。通过系统服务启动的进程也可通过这一方法查看。
二、若程序调用了seteuid()/setegid()改变了进程的有效用户或组,则在默认情况下系统不会为这些进程生成Coredump。很多服务程序都会调用seteuid(),如MySQL,不论你用什么用户运行mysqld_safe启动MySQL,mysqld进行的有效用户始终是msyql用户。如果你当初是以用户A运行了某个程序,但在ps里看到的这个程序的用户却是B的话,那么这些进程就是调用了seteuid了。为了能够让这些进程生成core dump,需要将/proc/sys/fs /suid_dumpable文件的内容改为1(一般默认是0)。
三、要设置足够大的Core文件大小限制了。程序崩溃时生成的Core文件大小即为程序运行时占用的内存大小。但程序崩溃时的行为不可按平常时的行为来估计,比如缓冲区溢出等错误可能导致堆栈被破坏,因此经常会出现某个变量的值被修改成乱七八糟的,然后程序用这个大小去申请内存就可能导致程序比平常时多占用很多内存。因此无论程序正常运行时占用的内存多么少,要保证生成Core文件还是将大小限制设为unlimited为好。
什么情况下产生core文件
当我们的程序崩溃时,内核有可能把该程序当前内存映射到core文件里,方便程序员找到程序出现问题的地方。最常出现的,几乎所有C程序员都出现过的错误就是“段错误”了。也是最难查出问题原因的一个错误。下面我们就针对“段错误”来分析core文件的产生、以及我们如何利用core文件找到出现崩溃的地方。当一个程序崩溃时,在进程当前工作目录的core文件中复制了该进程的存储图像。core文件仅仅是一个内存映象(同时加上调试信息),主要是用来调试的。
当程序接收到以下UNIX信号会产生core文件:
名字 |
说明 |
ANSI C POSIX.1 |
SVR4 4.3+BSD |
缺省动作 |
SIGABRT |
异常终止(abort) |
. . |
. . |
终止w/core |
SIGBUS |
硬件故障 |
. |
. . |
终止w/core |
SIGEMT |
硬件故障 |
. . |
终止w/core |
|
SIGFPE |
算术异常 |
. . |
. . |
终止w/core |
SIGILL |
非法硬件指令 |
. . |
. . |
终止w/core |
SIGIOT |
硬件故障 |
. . |
终止w/core |
|
SIGQUIT |
终端退出符 |
. |
. . |
终止w/core |
SIGSEGV |
无效存储访问 |
. . |
. . |
终止w/core |
SIGSYS |
无效系统调用 |
. . |
终止w/core |
|
SIGTRAP |
硬件故障 |
. . |
终止w/core |
|
SIGXCPU |
超过CPU限制(setrlimit) |
. . |
终止w/core |
|
SIGXFSZ |
超过文件长度限制(setrlimit) |
. . |
终止w/core |
在系统默认动作列,“终止w/core”表示在进程当前工作目录的core文件中复制了该进程的存储图像(该文件名为core,由此可以看出这种功能很久之前就是UNIX功能的一部分)。大多数UNIX调试程序都使用core文件以检查进程在终止时的状态。core文件的产生不是POSIX.1所属部分,而是很多UNIX版本的实现特征。UNIX第6版没有检查条件(a)和(b),并且其源代码中包含如下说明:“如果你正在找寻保护信号,那么当设置-用户-ID命令执行时,将可能产生大量的这种信号”。4.3 + BSD产生名为core.prog的文件,其中prog是被执行的程序名的前1 6个字符。它对core文件给予了某种标识,所以是一种改进特征。表中“硬件故障”对应于实现定义的硬件故障。这些名字中有很多取自UNIX早先在DP-11上的实现。请查看你所使用的系统的手册,以确切地确定这些信号对应于哪些错误类型。
下面比较详细地说明这些信号。
• SIGABRT 调用abort函数时产生此信号。进程异常终止。
• SIGBUS 指示一个实现定义的硬件故障。
• SIGEMT 指示一个实现定义的硬件故障。
EMT这一名字来自PDP-11的emulator trap 指令。
• SIGFPE 此信号表示一个算术运算异常,例如除以0,浮点溢出等。
• SIGILL 此信号指示进程已执行一条非法硬件指令。
4.3BSD由abort函数产生此信号。SIGABRT现在被用于此。
• SIGIOT 这指示一个实现定义的硬件故障。
IOT这个名字来自于PDP-11对于输入/输出TRAP(input/output TRAP)指令的缩写。系统V的早期版本,由abort函数产生此信号。SIGABRT现在被用于此。
• SIGQUIT 当用户在终端上按退出键(一般采用Ctrl-/)时,产生此信号,并送至前台进
程组中的所有进程。此信号不仅终止前台进程组(如SIGINT所做的那样),同时产生一个core文件。
• SIGSEGV 指示进程进行了一次无效的存储访问。
名字SEGV表示“段违例(segmentation violation)”。
• SIGSYS 指示一个无效的系统调用。由于某种未知原因,进程执行了一条系统调用指令,
但其指示系统调用类型的参数却是无效的。
• SIGTRAP 指示一个实现定义的硬件故障。
此信号名来自于PDP-11的TRAP指令。
• SIGXCPU SVR4和4.3+BSD支持资源限制的概念。如果进程超过了其软C P U时间限制,则产生此信号。
• SIGXFSZ 如果进程超过了其软文件长度限制,则SVR4和4.3+BSD产生此信号。
摘自《UNIX环境高级编程》第10章 信号。
在软件开发的过程中,无论如何努力,bug几乎都是必不可少的。当某些bug发生时,该进程会产生coredump文件。通过这个coredump文件,开发人员可以找到bug的原因。但是coredump的产生,大都是因为程序crash了。
1. 死锁
有些bug是不会导致进程crash的,比如死锁——这时,程序已经不正常了,可是却没有coredump产生。如果环境又不允许gdb调试,难道我们就束手无策了吗?针对这种情况,一般情况下,对于这样的进程,可以利用watchdog监控它们,当发现这些进程很长时间没有更新其heartbeat时,可以给这些进程发送可以导致其产生coredump的信号。根据linux的信号默认的处理行为,SIGQUIT,SIGABRT, SIGFPE和SIGSEGV都可以让该进程产生coredump文件。这样我们可以通过coredump来得知死锁是否发生。当然,如果进程添加了这些信号的处理函数,那么就不会产生coredump了。
2.获取指定位置快照:
还有一种情况,进程并没有死锁或者block在某个位置,但是我们需要在某个指定位置进行调试,获取某些变量或者其它信息。但是,有可能是客户环境或者生产环境,不允许我们进行长时间的检测。那么,我们就需要通过coredump来获得进程在运行到该点时的快照。
这个时候,可以利用gdb来产生手工产生coredump。在attach上这个进程时,在指定位置打上断点,当断点触发时,使用gdb的命令gcore,可以立即产生一个coredump。这样,我们就拿到了这个位置的进程快照。1. 欲查看多线程程序中所有线程的调用栈信息====================================
gdb attach xxx
set height 0
thread apply all bt
detach
q
2. CPU占用率过高问题分析方法
====================================
shell下执行:ps -eLfP 找出cpu占用率高的线程
UID PID PPID LWP PSR C NLWP STIME TTY TIME
root 1431 1270 1751 5 90 64 Dec24 ? 00:00:00
^^^^ ^^ ^^
thread core cpurate
使用gdb:
gdb attach 1431 <==== 登录cpu占用率高的进程
set height 0
i thread <==== 打印该进程的所有线程
找到 : LWP 1751 线程
17 Thread 855356656 (LWP 1751) 0x2ac30994 in pthread_cond_wait@@GLIBC_2.3.2 () from /lib/libpthread.so.0
^^ ^^^^^^^^
打出cpu占用率高的 任务的调用栈:
thread 17 <============ 切换到该线
[root@109-com1 ~]# gstack 428
#0 0x00007fdca018f2f8 in poll () from /lib64/libc.so.6
#1 0x00007fdca089cdcd in main ()
[root@109-com1 ~]# pstack 428
#0 0x00007fdca018f2f8 in poll () from /lib64/libc.so.6
#1 0x00007fdca089cdcd in main ()
yum install gdb
gstack 30222
pstack 30222
ps -eLo pid,lwp,pcpu | grep 30222
[root@109-com1 ~]# ps -eLo pid,lwp,pcpu | grep 11167
$ gdb /path/to/rsyslogd
$ core /core.1234
$ info thread
$ thread apply all bt full
$ q # quits gdb
truss和strace用来 跟踪一个进程的系统调用或信号产生的情况,而 ltrace用来 跟踪进程调用库函数的情况。truss是早期为System V R4开发的调试程序,包括Aix、FreeBSD在内的大部分Unix系统都自带了这个工具;而strace最初是为SunOS系统编写的,ltrace最早出现在GNU/Debian Linux中。这两个工具现在也已被移植到了大部分Unix系统中,大多数Linux发行版都自带了strace和ltrace,而FreeBSD也可通过Ports安装它们。
ltrace is a program that simply runs the specified command until it exits.
It intercepts and records
the dynamic library calls which are called by the executed process
and
the signals which are received by that process
Its use is very similar to strace
intercepts vt. 拦截;截断;窃听
[root@localhost ~]# rpm -qf /usr/bin/strace
strace-4.5.19-1.17.el6.x86_64
[root@localhost ~]# rpm -qf /usr/bin/ltrace
ltrace-0.5-23.45svn.el6.x86_64
truss, strace, ltrace是三个常见的调试工具。
他们有三个最常用的参数
-f: 除了跟踪当前进程外,还跟踪子进程
-o: 将输出信息写到文件file中
-p pid: 绑定一个由PID对应的进程
Example:
truss -o ls.txt ls -al
strace -f -o vi.txt vi
ltrace -p 123