網(wǎng)站可以微信支付是怎么做的百度熱詞
如何在apache Arrow定位與解決問題
最近在執(zhí)行sql時(shí)做了一些batch變更,出現(xiàn)了一個(gè) crash問題,底層使用了apache arrow來實(shí)現(xiàn)。本節(jié)將會從0開始講解如何調(diào)試STL源碼crash問題,在這篇文章中以實(shí)際工作中resize導(dǎo)致crash為例,引出如何進(jìn)行系統(tǒng)性分析,希望可以幫助大家~
在最后給社區(qū)提了一個(gè)pr,感興趣可以去查閱。
https://github.com/apache/arrow/pull/40817
背景
最近想修改一下arrow batch的大小,當(dāng)調(diào)整為65536后發(fā)現(xiàn)crash,出現(xiàn):
terminate?called?after?throwing?an?instance?of?'std::length_error'what():??vector::_M_default_append
然后通過捕獲異常gdb找到異常位置,最后拿到堆棧,發(fā)現(xiàn)位置是在join里面構(gòu)建哈希表側(cè)的partition數(shù)組出了問題:
prtn_state.key_ids.resize(num_rows_before?+?num_rows_new);
即問題轉(zhuǎn)化為:resize操作為何引發(fā)throw?
研究了一下STL代碼發(fā)現(xiàn),會遇到兩種場景,先把STL代碼精簡一下貼出來給大家看看:
if?(__navail?<?__n)?{const?size_type?__len?=_M_check_len(__n,?"vector::_M_default_append");}size_type?_M_check_len(size_type?__n,?const?char*?__s)?const?{if?(max_size()?-?size()?<?__n)__throw_length_error(__N(__s));
}
其中最核心的就是_M_check_len
函數(shù),看到這個(gè)判斷能想起哪兩種場景呢?
場景1:內(nèi)存確實(shí)不足了,超過了vector的max_size,此時(shí)會拋這個(gè)異常。
場景2:
__n
傳遞的是一個(gè)負(fù)數(shù),由于是size_t類型,則會變?yōu)槌笾?#xff0c;從而拋出異常。
場景1在我們系統(tǒng)當(dāng)中通過查看內(nèi)存不會遇到,于是轉(zhuǎn)到場景2,首先是猜測是個(gè)負(fù)數(shù),然后搞了個(gè)log包,上去測試發(fā)現(xiàn)確實(shí)是這個(gè)問題,可以看到rows_new變?yōu)樨?fù)數(shù)了。
part?id?15,?dop_?=?105,prtnid?+?1?ranges?=?0,prtnid?ranges?=?61434,?part?size:0,?rows_new:?-61434,?cap:?0
既然這里知道原因了,那么下一步就得繼續(xù)分析為何會產(chǎn)生負(fù)數(shù)?
num_rows_new是有分區(qū)的range決定的,下面有個(gè)公式計(jì)算產(chǎn)生了負(fù)數(shù)
int?num_rows_new?=locals.batch_prtn_ranges[prtn_id?+?1]?-?locals.batch_prtn_ranges[prtn_id];
繼續(xù)跟進(jìn)找到PartitionSort的Eval,里面有幾處非常需要注意:
ARROW_DCHECK(num_rows?>?0?&&?num_rows?<=?(1?<<?15));
首先第一個(gè)是這個(gè)斷言,我明明傳遞的是65536,明顯大于這里的32768,為何沒有斷言成功?事后發(fā)現(xiàn)這里是release包,只會報(bào)warning,不會fatal。
隨后繼續(xù)往下看,看到了一個(gè)比較明顯的類型uint16_t
,這個(gè)玩意就是在計(jì)算sum,而要讓num_rows_new為負(fù)數(shù),只有兩種可能:
場景1: locals.batch_prtn_ranges[prtn_id + 1] < locals.batch_prtn_ranges[prtn_id]
場景2: ?locals.batch_prtn_ranges[prtn_id + 1] 是負(fù)數(shù)且locals.batch_prtn_ranges[prtn_id]是負(fù)數(shù)或者locals.batch_prtn_ranges[prtn_id + 1] 是負(fù)數(shù)且locals.batch_prtn_ranges[prtn_id]也是負(fù)數(shù)并且大于前者。
uint16_t?sum?=?0;
for?(int?i?=?0;?i?<?num_prtns;?++i)?{uint16_t?sum_next?=?sum?+?prtn_ranges[i?+?1];prtn_ranges[i?+?1]?=?sum;sum?=?sum_next;
}
看了這段代碼可以知道,場景1排除了,因?yàn)槭亲栽龅?#xff0c;最差情況是相等,那么就只能場景2,變?yōu)樨?fù)數(shù)就不用說了,又碰到了溢出問題,所以可以推測uint16_t溢出了,這個(gè)值我們知道是65535,而65536剛好超過它,所以有問題!
至此,這一輪的debug調(diào)試與分析到此結(jié)束~
往期干貨:
熱度更新,手把手實(shí)現(xiàn)工業(yè)級線程池
快速拿下面試算法