1.coredump介绍及产生原因
什么是coredump:
程序由于各种异常或者bug导致在运行过程中异常退出或者中止, 此时系统会生成core文件, 其中包含程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种函数调用堆栈信息等.程序coredump原因:
<1>.内存访问越界
<2>.多线程程序使用了线程不安全的函数
<3>.多线程读写的数据未加锁保护
<4>.非法指针
<5>.堆栈溢出
2.GDB调试coredump步骤
- 设置coreDump文件大小, 默认是0:
首先在当前会话执行命令:ulimit –c,若为0,则不会产生对应的coredump,需要进行修改和设置:
ulimit -c 1024(字节)
ulimit -c unlimited (可以产生coredump且不受大小限制) - 程序gcc/g++编译时加 “-g”参数.
- 经过上述两步, 再运行程序coredump后, 会在当前目录生成core.xxx文件.
- 开始调试, 执行命令:gdb [可执行文件] [core.xxx文件]
- 输入命令: bt或back trace, 查看调用堆栈(会显示每个调用帧及帧号)
- 输入命令: f或frame [num], 查看对应堆栈帧
- 输入命令: args:查看函数参数; locals:查看本地变量
3.GDB断点调试命令
- gdb [可执行文件] : 开始调试
- run: 重新运行程序
- break/b [fileName] : [lineNum]/[funcName] :设置断点
- continue/c: 运行至断点处, 相当于VS中的F5
- next: 过程调试, 相当于VS中的F10
- step: 单步调试, 相当于VS中的F11
- print [variableName]: 查看变量值
- list/l : 显示当前代码段
- info break/b: 查看所有断点
- delete/d [breakNum]: 删除对应断点
- clear: 删除所有断点
4. GDB调试守护进程
使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试该进程,子进程会不受干扰地运行下去。如果你事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号并终止。那么该如何调试子进程呢?
上网查了下, 一共有3种方法, 这里采用attach的方法:
其它方法详见链接
- ps -ef :查看所需调试的子进程号, pidNum
- 运行gdb, 并执行命令:(gdb)attach [pidNum]
附加到该子进程后,一个新的问题是:子进程一直在运行,attach上去后都不知道运行到哪里了.
一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。:
至于这段代码所采用的条件,看你的偏好了。比如我们可以检查一个指定的环境变量的值,或者检查一个特定的文件存不存在。以文件为例,其形式可以如下:#include <unistd.h>//linux下 access()函数头文件 void debug_wait(char* fileName) { while(1) { if (!access(fileName, 0))//access检查文件,存在返回0 睡眠一段时间; else break; } }
当attach到进程后,在该段代码之后设上断点,再把该文件删除就OK了。当然你也可以采用其他的条件或形式,只要这个条件可以设置/检测即可。
Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。接下来就可以继续打断点调试了
info threads:当前运行线程, 可以使用thread [num] 切换线程
set scheduler-locking off|on, on 可以保证断点只让当前线程命中, 默认是off(所有线程都可命中断点)