問(wèn)題
上周開(kāi)始,我們一個(gè)已經(jīng)在線運(yùn)行了快2年的游戲突然頻繁宕機(jī),宕機(jī)前剛好上了一個(gè)資料片,提交了大批量的代碼。
比較麻煩的是宕機(jī)的core文件里沒(méi)有任何有效CallStack信息。在隨后的多次宕機(jī)core文件里也都找不到有效的CallStack信息,定位問(wèn)題變得無(wú)從入手。
原因
根據(jù)經(jīng)驗(yàn),這是一個(gè)典型的棧破壞問(wèn)題。一旦棧破壞了函數(shù)返回值后,堆棧完全是錯(cuò)亂的,得不到任何有效信息。
最開(kāi)始我建議項(xiàng)目組的同事查看最近提交的代碼,看看能否找到線索。不過(guò)由于近一個(gè)月提交的代碼實(shí)在太多,大海撈針了一段時(shí)間后,
毫無(wú)頭緒。
棧覆蓋一般是因?yàn)閙emcpy或者是循環(huán)賦值語(yǔ)句導(dǎo)致的,一般棧覆蓋的層次不會(huì)太多,所以從底部往上找,應(yīng)該能找到些有效的線索。
不過(guò),由于服務(wù)器函數(shù)經(jīng)常會(huì)有Package的臨時(shí)變量,導(dǎo)致函數(shù)棧很大,從下往上找線索也很困難,很多似是而非的合法地址很容易分散精力。
解決
按照上面的分析,從底部往上找是大海撈針,那么從頂部往下找如何呢?
這里先說(shuō)明下一般函數(shù)堆棧幀的建立(未優(yōu)化情況下的用戶函數(shù)):
push rbp
mov rbp, rsp
從這里可以看出,本層函數(shù)的返回值是存儲(chǔ)在 [rbp + 8],而上層函數(shù)的rbp地址則存儲(chǔ)在 [rbp]。
所以,從下網(wǎng)上找的時(shí)候,可以根據(jù)rbp逐步找到上層函數(shù)和上層函數(shù)的堆棧幀。
那么如何往下找呢,假如知道了一個(gè)上層函數(shù)的rbp,如何獲取下層函數(shù)呢,
這里有個(gè)小竅門,gdb7.X的版本有一個(gè)find功能,可以在內(nèi)存區(qū)域搜索數(shù)值,
從上往下找的時(shí)候,可以在堆棧查找本層 rbp的存放地址,從而確定下層函數(shù)rbp的存放地址。
舉個(gè)例子:
#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
這是一個(gè)典型的CallStack,讓我們先找到0x000000000040072b的堆棧信息吧。
先 info r 查看當(dāng)前的寄存器信息:
得到 rsp為0x7fffffffe358
find $rsp, +0x300, 0x000000000040072b
0x7fffffffe548
1 pattern found.
只有一個(gè)地址,那么存放rbp的地址就是0x7fffffffe540了,
繼續(xù) find $rsp, +0x300, 0x7fffffffe540
0x7fffffffe530
1 pattern found.
驗(yàn)證下是否正確:
x/10xg 0x7fffffffe530
0x7fffffffe530: 0x00007fffffffe540 0x0000000000400715
0x7fffffffe540: 0x00007fffffffe560 0x000000000040072b
0x7fffffffe550: 0x00007fffffffe648 0x0000000100000000
看到了吧,就是這樣找到了下一級(jí)的函數(shù)。
真實(shí)環(huán)境中往往沒(méi)這么簡(jiǎn)單,有時(shí)候會(huì)找到好幾個(gè)地址,這個(gè)時(shí)候需要自己逐個(gè)去偽存真了。
posted on 2013-12-02 20:51
feixuwu 閱讀(793)
評(píng)論(0) 編輯 收藏 引用