GDB实践:一场std::sort引发的coredump
来源:互联网 发布:e4a数据库 编辑:程序博客网 时间:2024/05/19 11:49
以前只掌握gdb一些基础知识,还没有真正"实战"过。刚好最近同事一个进程coredump了,原因比较深,正好利用这个机会来分析下
// @ 运行:gdb [可执行程序] -c [coredump文件]gdb edu_info_recommend_svr -c core_edu_info_recomm // @ 查看堆栈信息:bt (backtrace)(gdb) bt 10#0 0x00007fa0809b6144 in __strcmp_sse42 () from /lib64/libc.so.6#1 0x00000000004aa8f7 in Json::Value::CZString::operator< (this=<value optimized out>, other=<value optimized out>) at /data/home/yaaf_proj/workspace/yaaf-ng/deps/libjsoncpp/json_value.cpp:191#2 0x00000000004abf1a in operator() (this=<value optimized out>, key=<value optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_function.h:230#3 find (this=<value optimized out>, key=<value optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_tree.h:1424#4 find (this=<value optimized out>, key=<value optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_map.h:659#5 Json::Value::operator[] (this=<value optimized out>, key=<value optimized out>) at /data/home/yaaf_proj/workspace/yaaf-ng/deps/libjsoncpp/json_value.cpp:1195#6 0x0000000000438448 in Cmp6 (CourseInfo1=..., CourseInfo2=...) at /data/home/tinshuang/work/edu_proj/edu_info_recommend_svr/worker.cpp:226#7 0x0000000000448a73 in __unguarded_partition<__gnu_cxx::__normal_iterator<Json::Value*, std::vector<Json::Value, std::allocator<Json::Value> > >, Json::Value, bool (*)(Json::Value const&, Json::Value const&)> (__first=..., __last=..., __depth_limit=10, __comp=0x438430 <Cmp6(Json::Value const&, Json::Value const&)>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_algo.h:2230#8 std::__introsort_loop<__gnu_cxx::__normal_iterator<Json::Value*, std::vector<Json::Value, std::allocator<Json::Value> > >, long, bool (*)(Json::Value const&, Json::Value const&)> (__first=..., __last=..., __depth_limit=10, __comp=0x438430 <Cmp6(Json::Value const&, Json::Value const&)>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_algo.h:2301#9 0x0000000000448aea in std::__introsort_loop<__gnu_cxx::__normal_iterator<Json::Value*, std::vector<Json::Value, std::allocator<Json::Value> > >, long, bool (*)(Json::Value const&, Json::Value const&)> (__first=..., __last=..., __depth_limit=10, __comp=0x438430 <Cmp6(Json::Value const&, Json::Value const&)>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_algo.h:2302(More stack frames follow...)
从总的堆栈信息看,很多和STL与Json.Cpp相关,最终core在了Json.Cpp调用strcmp()上面。STL和Json.Cpp都是久经考验的第三方库,怎么可能会core?所以开始时很疑惑
// @ 切换当前栈:f (frame)(gdb) f 1#1 0x00000000004aa8f7 in Json::Value::CZString::operator< (this=<value optimized out>, other=<value optimized out>) at /data/home/yaaf_proj/workspace/yaaf-ng/deps/libjsoncpp/json_value.cpp:191191 return strcmp( cstr_, other.cstr_ ) < 0;// @ 打印相关的源代码:l (list)(gdb) l187bool 188Value::CZString::operator<( const CZString &other ) const 189{190 if ( cstr_ )191 return strcmp( cstr_, other.cstr_ ) < 0;192 return index_ < other.index_;193}// @ 查看当前栈|函数的入参:info args(gdb) info argsthis = <value optimized out>other = <value optimized out>
可以看到,堆栈#1core在了json_value.cpp内的strcmp(cstr_, other.cstr_)里,但是打印参数this和other都是<value optimized out>。这是因为程序在编译时加了-O2优化,一些变量会存储在CPU的寄存器中,没有放入内存。所以这里我再往上层的堆栈查看
(gdb) f 2#2 0x00000000004abf1a in operator() (this=<value optimized out>, key=<value optimized out>) at /usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_function.h:230230 { return __x < __y; }(gdb) l225 template<typename _Tp>226 struct less : public binary_function<_Tp, _Tp, bool>227 {228 bool229 operator()(const _Tp& __x, const _Tp& __y) const230 { return __x < __y; }231 }; (gdb) info argsthis = 0x3277aa0__x = @0x7f9fbfeb2250__y = @0x31c26b0// @ 打印变量:p (print)(gdb) p __x$1 = (const Json::Value::CZString &) @0x7f9fbfeb2250: { cstr_ = 0x50477b "sub_bgtime", index_ = 0}(gdb) p __y$2 = (const Json::Value::CZString &) @0x31c26b0: { cstr_ = 0x0, index_ = 0}
可以看出,这一层堆栈core在了STL比较2个Json::Value::CZString类型变量的大小上。翻了Json.cpp的源代码,其实Json::Value::CZString只有2个变量:
{ const char * cstr_ ; int index_ }
其中cstr_表示CZString的具体字符串内容,但是可以发现,__y的cstr_=0x0,即指向NULL!(不是指向空串,空串是类似cstr_=0x50477b ""的形式)
再结合#0~#5和Json.Cpp的源代码,core的原因就比较清楚了:Json.Cpp调用[]在找"sub_bgtime"这个Key,其内部是用到std::map::find()来查找,std::map内部维护了一棵红黑树,红黑树在拿"sub_bgtime"和原有的Key(可简单看成const char*类型)做比较时,有一个Key=NULL,所以在调用strcmp("sub_bgtime",NULL)时,strcmp内部访问NULL引发了Segmentation fault段错误
所以现在问题就是为什么这个Json会有一个Key=NULL呢?从代码上看,是core在了对vector<Json::value> vec_course_list做std::sort(),以为是vec_course_list里面的Json数据有问题,所以这里从vec_course_list的堆栈内继续分析下去
(gdb) f 13#13 0x0000000000442132 in InfoWorker::GetWillCourseListOnClient (this=0x15fb1e0, req_body=<value optimized out>) at /data/home/tinshuang/work/edu_proj/edu_info_recommend_svr/worker.cpp:894// @ 设置打印选项:set print [pretty|union|static-members...] [on|off] (这里为了显示方便)(gdb) set print union on(gdb) set print static-members off // @ 打印STL::vector:pvec (原生gdb对打印STL容器支持不是很好,网上这个叫dbinit_stl_views的小插件蛮好用的)// 可以看到这个vector拥有193个元素(Json),第1个Json值存储的内存地址为value_.map_,即0x13fe450(gdb) pvec vec_course_list 0elem[0]: $416 = { value_ = { int_ = 20964432, uint_ = 20964432, real_ = 1.0357805635774896e-316, bool_ = 80, string_ = 0x13fe450 "\020w\r\002", map_ = 0x13fe450 }, type_ = Json::objectValue, allocated_ = -1, comments_ = 0x0}Vector size = 193Vector capacity = 256Element type = Json::Value *// 按照其数据类型,把这个Json::Value内容打印出来。这里就可以看到这个Json::Value的各个Key了(gdb) p *(Json::Value::ValueHolder::ObjectValues *) 0x13fe450$419 = std::map with 18 elements = { [{ cstr_ = 0x19598f0 "apply_num", index_ = 1 }] = { value_ = { int_ = 1401, uint_ = 1401, real_ = 6.9218596982358641e-321, bool_ = 121, string_ = 0x579 <Address 0x579 out of bounds>, map_ = 0x579 }, type_ = Json::uintValue, allocated_ = -1, comments_ = 0x0 }, ....当然这里把vector里面193个Json::Value的每个Key都搞出来工作量很大,现在还没什么好方法。所以先结合代码逻辑和程序日志,发现vector内各个Json::Value其实都是正常的,不可能存在Key=NULL的情况。
到这里,就开始怀疑是std::sort有问题了,或者是我们使用不当。看了STL这块的源代码,比较晦涩,所以Google了下"stl sort core",发现这个问题在网上已经被很多人提及到:std::sort()在排序时,如果比较函数对相等的元素返回true,会导致程序coredump
原因分析:std::sort()的排序分2种,当元素个数>16(_S_threshold)时选择快速排序,<=16个则选择插入排序(对象少时快排性能不理想)。按照快排原理,每次都是遍历所有值和一个中间值比较,小的放左边,大的放右边。从下面STL源代码可看出,std::sort()在遍历比较时,是没有边界保护的。如果比较相等的元素返回真,则在极端情况下(如所有元素相等,__pivot为最小|最大值时)会出现访问越界,导致coredump:
/// This is a helper function... template<typename _RandomAccessIterator, typename _Tp, typename _Compare> _RandomAccessIterator __unguarded_partition(_RandomAccessIterator __first, _RandomAccessIterator __last, _Tp __pivot, _Compare __comp) { while (true) { while (__comp(*__first, __pivot)) ++__first; --__last; while (__comp(__pivot, *__last)) --__last; if (!(__first < __last)) return __first; std::iter_swap(__first, __last); ++__first; } }
- GDB实践:一场std::sort引发的coredump
- std::sort引发的core
- std::sort需要注意的问题(coredump)
- malloc引发的coredump
- GDB的使用+coredump
- GDB之coredump的学习
- 一场precision引发的血案
- 一场大雨引发的故事
- 一场争论引发的思考
- 调试引发的一场腥风血雨
- 一场大雨引发的思考
- SwipeRefreshLayout 引发的一场血案
- gdb coredump
- gdb coredump
- std::sort引发的core (这个分析还是很不错的!!!)
- 引发Coredump的代码测试例子
- C++的std::sort
- std::sort的兄弟姐妹
- HDOJ 2553 皇后问题(1、回溯法 2、位运算)
- Hadoop MapReduce编程入门案例
- IOS学习笔记-3
- 【SAE 部署 JavaWeb 项目报 404 错误】
- Spring Mvc全局异常捕获
- GDB实践:一场std::sort引发的coredump
- HDU 3714 Error Curves
- duilib 响应windows原生消息和自定义消息
- Red Hat Linu分区方案
- xp/win7修改远程桌面端口号3389的方法,超级详细的哦!
- ExtJS学习-----------Ext.Array,ExtJS对javascript中的Array的扩展(实例)
- Vim打造强大的编辑器
- 贝叶斯定理
- UITableViewCell 的长按事件处理