记录一则线上bug

来源:互联网 发布:php include没用 编辑:程序博客网 时间:2024/04/29 06:25

最近线上出现一次崩溃事故,core在了dynamic_cast类型转化上面,之前一直跑的好好的,第一反应是存在野指针了。

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);         }    }
0 0
原创粉丝点击