Segmentation fault段错误调试总结
Segmetation fault也叫做段错误,引发的原因有好多,这里我们只说一下段错误发生时的调试方法。
方法1:加打印printf。
这是最基本的往往也很有效的方法,在哪里Core掉就会在哪里停止打印–一目了然。同时这种方法也存在一个致命缺陷:如果恰巧Core掉的地方没加打印而程序代码又非常庞大又可能是多线程的,那查找问题等同于大海捞针。
方法2:gdb调试。
加gdb调试往往能在Core dump时抓到,甚至能抓到哪一个文件哪个类哪个函数哪一行,甚是精确。要确保GDB能抓到可用信息要做一些准备:
- 加-g 参数,这样才会有调试信息。 我想是个程序员就应该知道吧。
- 在Makefile 中加上 -fstack-protector 和-fstack-protector-all 信息,确保函数调用栈不丢失,当然只能是一定程度的不丢失,要完成保留住是不太可能的,但起码可以得到栈顶函数。
有了上面两点对大多数的Segmentation fault都能抓住,但是函数调用栈彻底乱掉或者在动态库so中Core而这个库编译时没有加-g参数,这些情况就gdb就无能为力了。
方法3:手动获取函数调用栈。
这种方法其实是借住两个系统函数backtrace和backtrace_symbol来获取函数调用栈的,把这两个函数放在信号处理函数中:当收到 SIGSEG时在信号处理函数中调用这两个函数打印函数调用栈,在没用GDB调试的时候这种方法可以代替gdb的一部分功能,这听起来是不是非常酷啊,来看一看实现吧:
1 | #include <signal.h> |
当然这种方法在没有GDB时候会大显身手,经过实验就是有gdb的时候这种方法有时比gdb抓到调用栈要多一层;当然这种方法和用gdb调试一样要加-g
和栈保护参数-fstack-protector
和 -fstack-protector-all
。其缺点就是抓到的调用栈无效,这是什么意思呢?有时发生core dump,能定位到甚至哪一行,但是那一行根本没有明显的错误;或者追到没有调试信息的动态库里如glibc。当然这些情况大多数调试方法都无能为力,只能依靠程序员的经验了。
方法4:经验之谈。
如果我们的程序是多线程的,发生core dump用以上方法均无效,除了仔细排查代码外,还有这么一方法让我们缩小范围。