记录一个栈错误问题的解决

记录一个栈错误问题的解决

问题  

   上周开始,我们一个已经在线运行了快2年的游戏突然频繁宕机,宕机前刚好上了一个资料片,提交了大批量的代码。
比较麻烦的是宕机的core文件里没有任何有效CallStack信息。在随后的多次宕机core文件里也都找不到有效的CallStack信息,定位问题变得无从入手。

原因
 

   根据经验,这是一个典型的栈破坏问题。一旦栈破坏了函数返回值后,堆栈完全是错乱的,得不到任何有效信息。
最开始我建议项目组的同事查看最近提交的代码,看看能否找到线索。不过由于近一个月提交的代码实在太多,大海捞针了一段时间后,
毫无头绪。
   栈覆盖一般是因为memcpy或者是循环赋值语句导致的,一般栈覆盖的层次不会太多,所以从底部往上找,应该能找到些有效的线索。
不过,由于服务器函数经常会有Package的临时变量,导致函数栈很大,从下往上找线索也很困难,很多似是而非的合法地址很容易分散精力。

解决

  按照上面的分析,从底部往上找是大海捞针,那么从顶部往下找如何呢?
这里先说明下一般函数堆栈帧的建立(未优化情况下的用户函数):
push rbp
mov rbp, rsp
从这里可以看出,本层函数的返回值是存储在 [rbp + 8],而上层函数的rbp地址则存储在 [rbp]。
所以,从下网上找的时候,可以根据rbp逐步找到上层函数和上层函数的堆栈帧。

那么如何往下找呢,假如知道了一个上层函数的rbp,如何获取下层函数呢,
这里有个小窍门,gdb7.X的版本有一个find功能,可以在内存区域搜索数值,
从上往下找的时候,可以在堆栈查找本层 rbp的存放地址,从而确定下层函数rbp的存放地址。
举个例子:

#0  0x00007ffff77d7830 in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff77d76ec in sleep () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x000000000040070a in test1 () at main.cpp:9
#3  0x0000000000400715 in test () at main.cpp:14
#4  0x000000000040072b in main (argc=1, argv=0x7fffffffe648) at main.cpp:19

这是一个典型的CallStack,让我们先找到 0x000000000040072b的堆栈信息吧。
先 info r 查看当前的寄存器信息:
得到 rsp为 0x7fffffffe358

find $rsp, +0x300, 0x000000000040072b
0x7fffffffe548
1 pattern found.

只有一个地址,那么存放rbp的地址就是
0x7fffffffe540了,
继续 find $rsp, +0x300,  0x7fffffffe540
0x7fffffffe530
1 pattern found.
验证下是否正确:
x/10xg 
0x7fffffffe530

0x7fffffffe530: 0x00007fffffffe540       0x0000000000400715
0x7fffffffe540: 0x00007fffffffe560      0x000000000040072b
0x7fffffffe550: 0x00007fffffffe648      0x0000000100000000

看到了吧,就是这样找到了下一级的函数。
真实环境中往往没这么简单,有时候会找到好几个地址,这个时候需要自己逐个去伪存真了。

 

你可能感兴趣的:(记录一个栈错误问题的解决)