一次堆疊溢位的分析

2021-06-22 12:57:52 字數 2610 閱讀 4392

在專案開發過程中,我會經常查一些引起程式崩潰的問題。就在前段時間,測試組反饋了乙個現象,當對某乙個功能進行拷機的過程中,大約進行了半個小時之後,程式就會引起崩潰,經過數次的重複測試,均出現了以上現象。經過對崩潰日誌的初步分析,我發現,雖然每次崩潰的地方一致,但通過閱讀原始碼,我發現那些地方一般是不會出現問題的,難道讀**不夠仔細,我仔仔細細地分析了一遍發生崩潰的上下文,都是非常正常的呼叫情況。難道又出現了其它模組的干擾,造成主線程記憶體錯亂了,經常查詢崩潰的人都知道,如果出現了這種情況,那查詢起來無異於登天,雖然有豐富的定位崩潰的經驗,但每次遇到這樣的問題,你的經驗往往只能起到一丁點的輔助作用。

就在我一籌莫展的時候,測試人員邊進行拷機,邊隨口說了一聲,「這個視窗怎麼我剛移動了位置,現在又回到了原來位置?」,每當山窮水復疑無路的時候,任何一絲的細節都可能成為突破的關鍵,我說,「可能新彈的視窗又把原來的視窗置位了吧。」,測試人員便應聲到,「這樣互動好像不是很友好吧!」,而此時我關心的並不是互動的問題,而是這個崩潰何解,便隨意答應了一下,說「那我去看一下**。」

開啟原始碼,簡單幾句**出現在了眼前:

bool ***::yyy( wparam wparam, lparam lparam, bool& bhandled )

cstdstring strinfo = wparam ? string_join_discuss_succ : string_join_discuss_fail;

cmessageboxdlg dlg( idr_xml_msg_notify_dlg, 255, false );

dlg.enbaleautoclose( false );

dlg.setinfo( strinfo, string_tip, g_pmainlogic->getmainhwnd(), id_ok );

dlg.create( g_pmainlogic->getmainhwnd(), string_tip, ui_wndstyle_box, ws_ex_toolwindow );

dlg.centerwindow();

dlg.showmodal();

bhandled = true;

return true;

}

這個函式便是彈出框起始的地方,拷機會定時有訊息產生,也就是這個函式會被定時呼叫,該函式的大致意思是:

1:如果有彈出框並且彈出框是顯示的,就發個 wm_close 訊息,關掉它。

2:新建乙個彈出框,並呼叫showmodal 顯示。

看似簡單並且無差錯的邏輯,卻引起了我的警覺,我下意識地點開了 showmodal 的原始碼,因為 showmodal 在這個版本產生了無數個問題,糾其原因,就是 dui 的 showmodal 也接管了訊息迴圈,並且做了簡單的邏輯處理,**如下:

uint cwindowwnd::showmodal()

; while( ::iswindow(m_hwnd) && ::getmessage(&msg, null, 0, 0) )

if( !cpaintmanagerui::translatemessage(&msg) )

if( wm_quit == msg.message )

}::enablewindow( hwndparent, true );

::setfocus( hwndparent );

if( wm_quit == msg.message )

return nret;

}

通過**可以看到,訊息迴圈會判斷是不是視窗,並且取出乙個訊息,然後處理掉,當接收到 wm_close 訊息後,在 wm_close 訊息中我們的通常處理都會是關掉視窗,然後導致 iswindow 判定失敗,退出訊息迴圈。也算是非常正常的邏輯。

但結合上段**的呼叫就會發現,這其中是有問題的。

有幾點要明確:

1.  ***::yyy 被呼叫的時候,肯定是在乙個訊息迴圈裡;

2.  當 sendmessage 傳入 wm_close 的時候,會直接去呼叫視窗過程,並處理wm_close分支的業務,一般是銷毀窗體,此時也還在***::yyy 被呼叫時的訊息迴圈裡;

3.  但 sendmessage 返回,接著呼叫 showmodal 時,依然是在前兩步相同的訊息迴圈裡,一直沒有出訊息迴圈。

問題就這樣產生了,showmodal 會建立乙個訊息迴圈,且 showmodal 不返回,堆疊繼續向下漲,當 ***::yyy 相關的訊息再產生的時候,就會在上乙個 showmodal 產生的訊息迴圈裡處理 ***::yyy ,然後繼續 sendmessage and showmodal 再建立,周而復始,卻一直沒有退出過乙個訊息迴圈,當然也就沒有返回過函式,我們知道,函式只呼叫不返回,當然堆疊會一直向下漲,最後造成堆疊溢位。

回憶一下,測試描述的問題是,隔一定時間,程式必崩,這也符合了崩潰場景,因為拷機是以固定時間來產生這個訊息,而呼叫環境相同,那麼棧增長速度必然相同,棧大小固定,那麼隔固定時間後,也必然會引起棧溢位,至此,這個崩潰被定位,那解決方法就很簡單了,我們採用了一位同事的非常好的建議,像這種不需要使用者確定的通知型訊息,根本不用模態框來完成,所以直接使用了 pop 框通知一下,最後也由那位同事(weilaitao)行了**的修改。

堆疊溢位的問題有時是非常頭疼的,而像這種由特殊業務造成的間接性的棧溢位也是少見,且查且珍惜。

記一次記憶體溢位(PermGen Space)的坑

環境 jdk1.6 使用技術 urlclassloader 事件描述 使用urlclassloader類載入器,實現熱部署。定時任務載入jar包,任務執行300次左右就會報 permgen space 分析過程 1.檢視記憶體使用情況 jmap heap pid jdk8以後檢視元空間大小 jsta...

SQLServer的一次堵塞分析

sqlserver的一次堵塞分析 2010 08 27 今天工作人員突然報告說某個介面無法正常開啟了,第乙個想到的便是sqlserver又發生堵塞了。在sqlserver中,做了乙個5分鐘執行一次的定時任務,定期掃瞄堵塞情況 不過五分鐘有些太久了。就執行了一下查詢堵塞的指令碼,看看目前系統裡正在發生...

記一次線上int溢位問題

今天吃完飯會辦公室,突然接到群裡的bug,線上預算資料出現了負值,如圖 於是一路奔回辦公室,各種分析日誌邏輯,分析了5個小時無果,因為印象中int的範圍是21億,而負值末尾是非零,因此一開始就否定了是int溢位的問題 後來還是其他小夥伴看到了 問題,原 如下 左邊是修改前的,右邊是修改後的 我的入參...