dump core = 核心转储,也就是我们平时在Linux上最容易遇到的段错误
这种错误一般是因为你访问了一些不可越界的内存导致的,在操作系统上有als地址随机化与虚拟地址保护,我们不能访问越过我们自己程序内存边界的地址
如果访问了一般就会出现这种错误:
当然,当你尝试修改一些只读区域的内存也会出现这种情况,这是因为内核发现你在尝试做一些违规行为,那么内核会拦截并杀死你的程序。
当我们在写大工程的时候,出现这种情况的确让人很头疼,在大工程下,文件那么多,较为优秀的工程里模块化的占有率也比较高,很难查出问题出现在那个模块上,也很难定位到某个文件。
这种情况下一般只是一行代码导致的,如果我们能够快速定位到文件且定位到出错代码行,岂不美哉?
这个时候GDB就出现了,一般大多数人称为 GDB dump core调试
就是先让Linux内核生成核心转储文件,该文件里包含了程序出错时产生的堆栈调用,还有一些错误信息。
前提是这个程序在编译时没有经过任何优化,且编译时,gcc/g++必须+"-g"选项,让编译器生成地址符号表还有一些调试信息。
这样我们才能精确定位到函数以及文件名。
生成dump core文件方法:
ulimit -c unlimited使用ulimit -c选项来生成core文件,到程序可执行目录下使用这个命令,后面的unlimited代表无限制大小
可以把unlimited替换成大小,以字节为单位,限制core文件生成的大小
如果改为0,则不生成。
先到我们出错的debug程序目录下使用此命令:
我们在运行出错程序:
使用ls命令查看是否产生文件:
产生的core就是我们的核心转储文件。
我们是不能打开的,这个文件只有gdb能够识别,所以我们要使用gdb来调试它
gdb 程序 core文件
gdb ./Weye_RadarServer core成功调试:
下面的缺少raise.c文件无需理会,这个是因为gdb缺失glibc库,可以忽略这个问题。
其中这一行也明确表示出,收到了SIGABRT信号,被操作系统直接杀死,所以可以肯定是访问了不属于我们自己的内存地址。
如果你比较幸运,在gdb刚调试时,就会定位出代码行。
接着,我们要用”bt“命令查看堆栈情况
第一行的raise.c这个错误先忽略
我们往下看,gdb定位出三个报错文件的行号以及文件
分别对应#8,#9,#10
使用frame命令查看堆栈调用情况,先看#10的main
是第十一行代码出了错
我们调用了init这个函数出了错!
是init那么我们看第#9,这里是init的实现
这里我们的init又调用了start_init这个函数,是start_init函数出了错,这个时候我们就可以看第8行,刚好是start_init的段错误
good,找到了,是tree.hpp的第82行代码出了错
std::string _temp_type(attr_xml.lookUp_kid_node(ZMQ_HAND,ZMQ_PROTOCOL).Char);代码定位到这一行
这行代码就是使用c++的string类,并且传递一个构造参数
参数使用的是函数返回值
这样基本上可以确定是lookUp_kid_node在调用的时候返回了一个不确定的地址或者返回了NULL,同时_temp_type去做构造,字符串复制了,这个地址是不正确的,所以发生了越界!
这个函数使用的是atrr_xml模块,一个用于解析xml文件的
排错:
1.确定xml模块是正确的,并且测试通过的
2.确定xml模块已经打开xml文件
3.确定xml文件是否正确,查找的字段是否存在
这里我依次排查最后发现问题出现在xml文件字段上
在宏定义里定义里字段值:
xml文件字段:
ProtoCol字段在宏定义里定义成了ProtoCOl,最后的这个O写成了大写的...
导致xml模块无法解析到,返回了NULL,然后又被string类去构造,导致构造了个空地址,产生了段错误,NULL也是一个地址,属于地址为0的这块内存,这块内存操作系统是不可能分配给我们的,它归操作系统内核1G空间。
当我把字段修改完后程序正常运行:
在补充一个点,core文件其实你也可以不用生成,用gdb启动,在用r跑起来,到出错段gdb停下来后,就可以用上面的方法排查了。
修改core文件名称以及路径方法:
修改文件命令: echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern或者:sysctl -w kernel.core_pattern=/corefile/core.%e.%p.%s 可以将core文件统一生成到/corefile目录下,产生的文件名为core-命令名-pid-时间戳 以下是参数列表: %p - insert pid into filename 添加pid(进程id) %u - insert current uid into filename 添加当前uid(用户id) %g - insert current gid into filename 添加当前gid(用户组id) %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 添加导致产生core的命令名
ps:记录一次调试经历。