程式除錯技術 跳出死迴圈

2021-04-06 16:12:50 字數 2693 閱讀 8978

前言

程式設計師最痛苦的事莫過於深陷於bug的泥潭,我也沒少在這上面摔跤。這裡,我把自己的一些經驗教訓總結出來,涉及的內容包括死迴圈、死鎖、記憶體洩漏以及記憶體訪問錯誤等,如果能對朋友們有所幫助,那就再好不過了。不過,我不打算按照循序漸進的方式來撰寫這些文章,而是想到哪寫到哪,也許到最後才會形成乙個完整的系列。

不管是單執行緒還是多執行緒程式,死迴圈都算是相對比較容易解決的,但也有一些技巧在裡面,本節就將對這個問題作以簡單的總結。

如何判斷死迴圈

死迴圈是比較容易觀察出來的,其表象主要如下:

1. cpu占用一直居高不下,從任務管理器看基本上一直處於100%狀態。

2. 程式長時間執行始終未能進入預期狀態。

另外,我們還可以從函式呼叫棧(call stack)和程式輸出的除錯資訊來判斷程式是否進入了死迴圈。

簡單程式的死迴圈除錯方法

這裡所謂的簡單程式主要指單執行緒程式,其除錯方法也很簡單,只需要單步跟蹤就行。對於vc6來說,在程式入口處設定一斷點(快捷鍵為f9),然後f5除錯執行,接著再使用f10或者f11單步跟蹤,你很容易定位發生死迴圈的地方。這些除錯命令以及快捷鍵在debug選單下都有,我們只需熟悉它即可,其它偵錯程式比如.net的使用方法也和此類似。

為了加快除錯速度,我們可以先在自己認為可能出現死迴圈地方的前後位置加上斷點,如果不能確定,那麼就在程式執行路徑上依次設定幾個斷點,然後f5執行。當程式到斷點停下來後再按f5,直到不能繼續前進為止,那麼說明死迴圈出現在上乙個斷點之後,記下其位置。接下來,去掉之前的所有斷點,再次除錯執行,當執行到前面那個斷點後,再單步跟蹤。這是一種快速分段定位方法,適合於很多問題的除錯。

複雜程式的死迴圈除錯方法

這裡所謂的複雜程式主要指大型的多執行緒程式,死迴圈的發生往往是在程式執行過程中,有些情況還不是每次都發生,而是只在某種特定條件下才出現。遇到這種情況,我們就應抓住機會,力爭一出現問題就將其抓住,為此,首先要搭好測試環境,最好能夠除錯執行程式,否則出現了問題也只能束手無策。

出現了死迴圈又如何定位呢?多執行緒程式有多條執行路徑,不像單執行緒程式那樣可以一次單步跟蹤到底,因此我們的首要任務是判斷是哪個執行緒出現了死迴圈。

為此,我們需要借助外部工具。這個工具不用到處找,windows就有自帶。選擇「控制面板/管理工具」中的「效能」工具,如下圖:

沒有用過該工具的朋友可以先自行熟悉一下。在右側視窗中單擊右鍵,選擇「新增計數器」,或者直接單擊工具欄中的「+」按鈕,將會彈出乙個設定視窗,其中的「效能物件」下拉列表中給出了效能計數器的分類,左側列表框中則給出了相應的效能計數器,右側列表框則是監視物件。

為了更好的講解除錯過程,我們將以乙個具體程式為例。先使用vc6嚮導建立乙個名為infiniteloop的控制台程式,其**如下:

#include

#include

#include

int g_loop = 1; 

// 迴圈控制標誌

unsigned

__stdcall test_thread(

void* ) 

// 測試執行緒

return 0; }

int main(

int argc, 

char* argv)

system(

"pause"); 

// 暫停主線程,等待按鍵結束無限迴圈

g_loop = 0;

waitforsingleobject(hthread, infinite); 

// 等待測試執行緒結束

closehandle(hthread); 

// 關閉執行緒控制代碼

printf(

"program exits now./n");

return 0; }

這是乙個最簡單的多執行緒程式,主線程(即main函式)等待使用者按任意鍵建立乙個測試執行緒,然後再次等待按任意鍵退出整個程式,而測試執行緒則是乙個無限迴圈,它將使cpu的佔用率為100%。

我們先開啟前面的效能分析工具,然後f5除錯執行程式。這裡,我們需要知道的是目標程式每個執行緒的cpu占用情況,因此新增計數器時,效能物件選擇「thread」,計數器選擇「% processor time」,監視物件選擇除錯程式的兩個執行緒:「infiniteloop/0」和「infiniteloop/1」,注意這裡的數字0和1只是執行緒編號,跟執行緒id和控制代碼沒有關係。見下圖:

新增計數器後,我們將看到顯示視窗中動態的描繪出一幅曲線圖(見下圖)。雙擊位置最高的那條曲線,下面計數器列表中的高亮條自動定位到了例項1上,這樣我們便確認了占用cpu最高的是1號執行緒。

接下來,我們需要得到1號執行緒的threadid,為此需要再新增乙個計數器。這次,我們只選擇1號執行緒例項,計數器選id thread,如下圖:

新增完id thread計數器後,選擇「id thread 1」例項,可以看到其值為1444,這便是threadid,如下圖。需說明的是圖中位置顯示的值對於不同的計數器物件具有不同的含義,請參考相關幫助文件。

獲得了執行緒id,就可以暫停程式了,選擇debug選單下的break指令即可。然後再選擇debug選單下的threads調出執行緒視窗,如下:

到此為止,我們就可以按照前面處理單執行緒的方式進行除錯了。

總結本節內容表面是講述死迴圈的除錯技巧,實則介紹了windows效能工具以及vc6執行緒視窗的使用(.net的執行緒視窗使用起來更方便),有心的朋友一定能從效能工具中找多更多對自己有價值的功能。

此外,這裡的執行緒cpu佔用率分析方法不只適用於死迴圈,其它異常的高cpu佔用率問題也可以採用。

(freefalcon於2006.04.09)

UWP中如何利用非同步程式設計跳出死迴圈

uwp中,我設定了兩個按鈕,單擊第乙個按鈕進行乙個迴圈,單擊第二個按鈕停止這個迴圈,但是不管怎麼處理按下第乙個按鈕以後都成了乙個死迴圈,程式呈現卡死狀態。這就讓我想到必須要用非同步程式設計來解決這個問題,網上uwp的教程什麼的實在太少了,在msdn裡面研究了一天到底怎麼寫非同步,終於是讓我給解決了!...

使用oracle,程式退出死迴圈

在程式中使用sqldriverconnect連線oracle資料庫 沒有關閉sql控制代碼就退出程式了。結果在debug模式下出現死迴圈,還是在exitprocess函式裡,以前沒有碰到開啟後不關閉程式會無法退出,也沒見過exitprocess不能退出來的。猜想是不是oracle的dll沒做好退出這...

php程式死迴圈導致502報錯

請求訪問返回502錯誤提示,嘗試去除可疑 段,發現程式正常,可疑 段如下圖箭頭指向 當時腦熱的以為是本地php5.5.18版本過低,不支援陣列語法 實際php 5.4 開始支援的 於是公升級為php7.0.5版本,然而發現依舊502。最後仔細觀察nginx錯誤提示,nginx慢日誌如下圖所示,發現到...