1播瞳、問題現(xiàn)象
線上服務(wù)每隔一個多月會出現(xiàn)一次core,core在一個多線程庫的queue里面免糕。
queue使用循環(huán)數(shù)組實(shí)現(xiàn)赢乓,里面主要有以下數(shù)據(jù)成員:
{size_t len;/*數(shù)組長度*/ olatile size_t head;/*隊(duì)列頭*/ volatile size_t tail;/*隊(duì)列尾*/ T* array;/*數(shù)組指針*/}
core的調(diào)用棧位置:array[tail] = std::move(val); core在向隊(duì)列尾添加消息的語句。
dmesg信息:segfault at 2872fa0 ip 000000000047e9e3 sp 00007fcc94ffd360 error 4石窑,非法地址是2872fa0牌芋,core的位置是47e9e3。
2松逊、使用objdump命令將可執(zhí)行文件反匯編躺屁。
array[tail] = std::move(val);語句對應(yīng)的匯編代碼如下:
%rbx存的是queue對象,(%rbx)對應(yīng)qlen经宏,0x8(%rbx)對應(yīng)head犀暑,0x10(%rbx)對應(yīng)tail,0x18(%rbx)對應(yīng)array烁兰。
%rax中存的是無效地址2872fa0耐亏,%rax = array + 8*tail。
3沪斟、查看core文件中寄存器的內(nèi)容广辰,使用info registers命令。
4主之、查看%rbx指向的內(nèi)存的內(nèi)容轨域,從中可以得到queue中成員變量的值,使用x命令杀餐。
可以看出:tail = 4712592干发,array = 4713248,array + 8 * tail = 4713248 +? 8 * 4712592 = 42413984 = 2872fa0史翘,正好是引起core的無效地址枉长。
進(jìn)一步發(fā)現(xiàn) qlen = 4708768冀续,head = 4708864,發(fā)現(xiàn)可疑的地方必峰,首先qlen沒有這么大洪唐,我們設(shè)置的qlen應(yīng)該等于遠(yuǎn)小于這個值,另外qlen也不應(yīng)該小于head和tail吼蚁,這時候考慮可能由兩種情況導(dǎo)致的:(1)queue對象的內(nèi)容被飛踩了(2)queue對象是不是獲取錯了凭需。考慮到queue對象被飛踩定位比較難肝匆,所以先檢查queue對象獲取的是否正確粒蜈。
5、進(jìn)一步檢查代碼旗国,發(fā)現(xiàn)多線程庫中會有多個Consumer枯怖,每個Consumer對應(yīng)一個queue對象,將一個消息發(fā)送給哪個queue是由我們業(yè)務(wù)根據(jù)輪詢選擇的能曾,輪詢的代碼(worker++%worker_num)度硝,而worker定義成int型,int型當(dāng)加到最大值后會出現(xiàn)翻轉(zhuǎn)變成負(fù)數(shù)寿冕,對負(fù)數(shù)取余還是負(fù)數(shù)蕊程,將一個負(fù)數(shù)付給一個ulong型就會變成一個很大的數(shù),導(dǎo)致獲取的queue對象不正確驼唱,最終導(dǎo)致core存捺。
6、進(jìn)一步驗(yàn)證曙蒸,用f命令進(jìn)入業(yè)務(wù)層代碼的frame,用info locals命令查看worker變量的值岗钩。
worker為負(fù)數(shù)纽窟,驗(yàn)證了問題。