Believe youself!
分类: LINUX
2015-04-17 15:48:28
直接输入指令:
kill -s SIGSEGV $$
以前写的一个文档,有同学反馈不能用,恰好手头有一个堆栈被破坏的core文件要查看,跑了一遍重新更新了一下原来的文档。
-----------------------------------------------------
调试core文件的时候,偶尔会遇到调用堆栈被破坏的情况,一大串问号令人头痛。类似下面:
echou大师介绍了一种手动恢复调用堆栈的方法,cesc研究了一下,跟大家share下。
源文档见http://devpit.org/wiki/x86ManualBacktrace
首先回顾一些背景知识:
%esp 是堆栈指针寄存器,它指向当前堆栈储存区域的顶部.
?p 是基址寄存器,它指向当前堆栈储存区域的底部.
%eip 是指令指针,是下一条要执行的指令地址.
当发生函数调用时,程序如下操作:首先把参数压入堆栈;然后保存指令寄存器(EIP)中的内容做为返回地址(RET);第三个放入堆栈的是基址寄存器(EBP);然后把当前的栈指针(ESP)拷贝到EBP,做为新的基地址;最后为本地变量留出一定空间,把ESP减去适当的数值。如下所示:
Low addresses
-----------------------------------
0(%esp) | 栈顶
---------|-------------------------
-n(?p) | 被调函数的栈空间,局部变量在此分配,这段是可变的
0(?p) |上层函数调用的栈基地址
4(?p) |返回地址
n(?p) |一系列函数参数
-----------------------------------
High addresses
思路就是:通过查看寄存器内容,一步步得到每一层调用的返回地址,最后恢复函数调用链。
1. 首先,查看寄存器信息,找到ebp ,即当前函数调用堆栈的基址
这就是当前调用栈:
StackFrame Pointer | Instruction Pointer
------------------------------------------
0xb50c41a8(ebp) | 0xb7b15197(eip)
2. 然后,查看ebp中的地址内容,获取上层调用的ebp,eip
0xb50c41a8 这地址存放的就是上层调用栈的基址,0xb50c41a8 存放上层调用的EIP
看一下内存地址 0xb50c41a8附近的内容,在里面找上层?p 和 ?p+4的内容,下面已经用方框标出
注意,这里的地址内容是小端字节序,整理后是这样的
0xb50c41a8: 0xb50c41b8 0xb79ec77f ……
下面这就是我们恢复的coredump时候的几层堆栈信息:
StackFrame Pointer | Instruction Pointer
-----------------------------------------------
0xb50c41a8 0xb7b15197
0xb50c41a8: 0xb50c41b8 0xb79ec77f
0xb50c41b8: 0xb50c4218 0xb79e0822
0xb50c4218: 0xb50c4258 0xb79e0251
0xb50c4258: 0xb50c4278 0xb79df41c
0xb50c4278: 0xb50c42a8 0xb7e2cd86
0xb50c42a8: 0xb50c5468 0x0805be44 ……
这样找下去,可以一直找到最上一层调用,到达栈底;不过,一般不需要找到头
3. 有了每一层调用发生时的返回地址(EIP), 直接用指令格式查看内存内容就行了:
0xb79ec77f <_ZN7log4cpp3NDC3getEv+31>: add $0x4,%esp
前半段就是当前调用函数symbol name。走到这里,调用堆栈已经恢复了。
二
在Unix系统下,如果send、recv、write在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。此种情况应用就很难查出处理进程为什么退出。
SIGPIPE信号:
对 一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法 时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全 关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出。如果对SIGPIPE进行忽略处理,二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE.
处理方法:
在初始化时调用signal(SIGPIPE,SIG_IGN)忽略该信号(只需一次),SIGPIPE交给了系统处理。此时send、recv或write函数将返回-1,errno为EPIPE,可视情况关闭socket或其他处理
SIGPIPE被忽略的情况下,如果服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理: signal(SIGCHLD,SIG_IGN); 交给系统init去回收。这样子进程就不会产生僵尸进程了。
Coredump生成过程:
系统关键/核心进程,产生严重的无法恢复的错误,为了避免系统相关资源受到更大损害,操作系统都会强行停止运行,并将当前内存中的各种结构、核心进程出错位置及其代码状态,保存下来,以便以后分析。最常见的原因是指令走飞,或者缓冲区溢出,或者内存访问越界。
进程退出,未发现core的原因有一下几种:
1、core文件的生成开关被关闭
a) 查看core文件的生成开关:
boss@Tencent:~> ulimit -c
0
b) 打开core文件的生成开关:
ulimit -c unlimited
2、core文件的生成开关已被打开,当前路径下找不到core文件
a) /proc/sys/kernel/core_pattern文件可以控制core文件保存位置和文件名格式
echo "core" >/proc/sys/kernel/core_pattern 当前目录生成core
b) 系统生成的core文件以pid作为扩展名称
echo "1" > /proc/sys/kernel/core_uses_pid
3、系统中存在某个逻辑上return错误地使用了exit、_exit、abort
exit、_exit和abort它们会直接退出进程,而不是返回堆栈。此情况系统是系统被正常的退出了,而不是出现异常。
4、系统中的异常信号没有进行处理,操作系统结束了此进程
操作系统对信号有以下几种默认操作,对于A类信号如果进程不做处理,进程会被操作系统终止,也不会产生core。
信号类型 |
A 缺省的动作是终止进程 |
A类信号 |
SIGHUP 1 A 终端挂起或者控制进程终止 |
SIGABRT |
abnormal termination (abort) |
SIGBUS |
hardware fault |
SIGEMT |
hardware fault |
SIGFPE |
arithmetic exception |
SIGILL |
illegal instruction |
SIGIO |
asynchronous I/O |
SIGIOT |
hardware fault |
SIGQUIT |
terminal quit character |
SIGSEGV |
invalid memory reference |
SIGSYS |
invalid system call |
SIGTRAP |
hardware fault |
SIGXCPU |
CPU limit exceeded (setrlimit) |
SIGXFSZ |
file size limit exceeded (setrlimit) |