记录一则线上bug
2017-03-07 20:41
155 查看
最近线上出现一次崩溃事故,core在了dynamic_cast类型转化上面,之前一直跑的好好的,第一反应是存在野指针了。
经过仔细排查并没有发现释放完后,没有在容器中删除的情况。
其中的GetPlayers大概是这种结构:
_players是一种map类型,上面的函数实际上就是把map中的数据复制一份到vector中,现在要做的很显然就是把_players打印出来,嗯,标准的gdb并不能打印出来需要的(值,数据)信息,下面的是红黑树中的一些基本元素,要解析出来可以试着强制转换才能打印。
有工具是可以直接打印stl的,需要去下载一个文件,可以去参考一下http://blog.csdn.net/luoleicn/article/details/5968038
然后我们就可以打印map中的东西了:
让人惊讶的蛋疼的事情发生了,Map的空的!
难道多线程下被其他线程改写了?可实际上我们的游戏主逻辑是单线程的,所有的数据库线程读取都是使用回调的。然后又去仔细研究了一下回调,目前的的回调是在epoll的主循环中处理的,这里会处理所有已完成的数据库操作的队列。所以实际上还是在一个线程中完成的。
顺序执行感觉根本不该出现这种情况,在这种束手无策的情况下,只能一句句代码开始分析,找了一两个小时终于发现问题的根源出现在ProcessPlayerQuit上,这个函数调用的层次非常的深,这个函数会清除room中player信息,并且处理一些玩家在不在房间或者不在游戏的情况,在其中某一个分支上竟然会再次调用到KickAllPlayersInRoom。
在循环之初就已经清除掉了room._players之中的元素,第二轮调用到的时候dynamic_cast尝试解引用一个已经删除的元素就core掉了。
现在的问题就有点像下面这种情况。
void RoomHandler::KickAllPlayersInRoom(Room* room) { vector<IPlayer*> players = room->GetPlayers(); for (size_t i=0; i<players.size(); i++) { ProcessPlayerQuit(dynamic_cast<Player*>(players[i]), room); } }
经过仔细排查并没有发现释放完后,没有在容器中删除的情况。
其中的GetPlayers大概是这种结构:
std::vector<IPlayer*> Room::GetPlayers() { std::vector<IPlayer*> v; for( PlayerContainer::iterator it = _players.begin(); it != _players.end(); ++it){ v.push_back( it->second ); } return v; }
_players是一种map类型,上面的函数实际上就是把map中的数据复制一份到vector中,现在要做的很显然就是把_players打印出来,嗯,标准的gdb并不能打印出来需要的(值,数据)信息,下面的是红黑树中的一些基本元素,要解析出来可以试着强制转换才能打印。
(gdb) print room._players $8 = { _M_t = { _M_impl = { <std::allocator<std::_Rb_tree_node<std::pair<unsigned int const, IPlayer*> > >> = { <__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<unsigned int const, IPlayer*> > >> = {<No data fields>}, <No data fields>}, members of std::_Rb_tree<unsigned int, std::pair<unsigned int const, IPlayer*>, std::_Select1st<std::pair<unsigned int const, IPlayer*> >, std::less<unsigned int>, std::allocator<std::pair<unsigned int const, IPlayer*> > >::_Rb_tree_impl<std::less<unsigned int>, false>: _M_key_compare = { <std::binary_function<unsigned int, unsigned int, bool>> = {<No data fields>}, <No data fields>}, _M_header = { _M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x954e7d0, _M_right = 0x954e7d0 }, _M_node_count = 0 } } }
有工具是可以直接打印stl的,需要去下载一个文件,可以去参考一下http://blog.csdn.net/luoleicn/article/details/5968038
然后我们就可以打印map中的东西了:
(gdb) pmap room._players <int> //只考虑第一个元素 Map size = 0
让人惊讶的蛋疼的事情发生了,Map的空的!
难道多线程下被其他线程改写了?可实际上我们的游戏主逻辑是单线程的,所有的数据库线程读取都是使用回调的。然后又去仔细研究了一下回调,目前的的回调是在epoll的主循环中处理的,这里会处理所有已完成的数据库操作的队列。所以实际上还是在一个线程中完成的。
顺序执行感觉根本不该出现这种情况,在这种束手无策的情况下,只能一句句代码开始分析,找了一两个小时终于发现问题的根源出现在ProcessPlayerQuit上,这个函数调用的层次非常的深,这个函数会清除room中player信息,并且处理一些玩家在不在房间或者不在游戏的情况,在其中某一个分支上竟然会再次调用到KickAllPlayersInRoom。
在循环之初就已经清除掉了room._players之中的元素,第二轮调用到的时候dynamic_cast尝试解引用一个已经删除的元素就core掉了。
现在的问题就有点像下面这种情况。
vector<IPlayer*> players = room->GetPlayers(); //从 room._players获取玩家 for (size_t i=0; i<players.size(); i++) { //某一个分支会出现 //第二次到这的时候room._players就已经清空掉了。 for (size_t j =0; j<players.size(); j++) { //room._players.erase(uid); } }
相关文章推荐
- 记录微软的BUG一则--Repeater里的RadioButtonList不可以局部刷新
- 记录一次使用Glide的BUG
- bug记录:Mybatis的sql传空值报错(未完待续)
- 记录下IE7下的input标签bug
- 记录:做Hibernate+Struts2学生管理系统遇到的BUG
- 记录一次bug解决过程:resultType和手动开启事务
- 遇到数个bug,记录一下
- 测试通过!为何线上还有很多BUG?实践中的质量如何控制?
- bug:配置secondarynamenode && 斯塔尼亚聊天记录
- centos 6.5 + haproxy 1.4搭配之 haproxy不记录日志一则轻笔记
- 2016年12月06日15:40:20-今天bug记录
- 记录一次linux线上服务器被黑事件
- ie6,ie7,ie8 css bug兼容解决记录
- 记录一个Bug,注意0不可以做除数
- 记录一个android SharedPreferences的bug
- NetCMS使用BUG记录及解决方法
- 线上Java系统的调试经验一则
- Android记录一个setTextColor常见的一个bug
- 记录下类似BUG的一件事情
- 20180221.微博发布功能,后端会话不能正确读取BUG记录