Visual Studio原生開發的10個除錯技巧

JingerJoe發表於2013-08-09

【感謝@_La_Isla_Bonita 的熱心翻譯。如果其他朋友也有不錯的原創或譯文,可以嘗試推薦給伯樂線上。】

最近碰巧讀了Ivan Shcherbakov寫的一篇文章,《11個強大的Visual Studio除錯小技巧》。這篇文章只介紹了一些有關Visual Studio的基本除錯技巧,但是還有其他一些同樣有用的技巧。我整理了一些Visual Studio(至少在VS 2008下)原生開發的除錯技巧。(如果你是工作在託管程式碼下,偵錯程式會有更多的特性,在CodeProject中有介紹它們的文章),下面是我的整理的一些技巧:

  1. 異常中斷 | Break on Exception
  2. Watch視窗中的偽變數 | Pseudo-variables in Watch Windows
  3. 符號越界後檢視堆物件 |
  4. 檢視陣列的值
  5. 避免進入不必要的函式
  6. 從程式碼啟動偵錯程式 | Launch the debugger from code
  7. 在Output視窗列印
  8. 隔離記憶體洩漏
  9. 除錯發行版 | Debug the Release Build
  10. 遠端除錯

 

技巧1: 異常中斷

在處理被呼叫之前,異常發生時可以 啟動偵錯程式進行中斷,可以讓你在異常發生後立即除錯程式。操作呼叫棧便於你去查詢異常發生的根本原因。

Vistual Studio允許你去指定想要中斷的異常型別或者特殊異常。選擇選單Debug>Exceptions彈出對話方塊,你可以指定原生的(或者託管的)異常,除了偵錯程式自帶的一些預設異常,你還可以新增自己的自定義異常。

下面是一個std::exception 異常丟擲時偵錯程式中斷的例子。

更多閱讀:

 

技巧2:Watch視窗中的偽變數

Watch視窗或QuickWatch對話方塊提供一些特定的(偵錯程式可識別的)變數,被稱為偽變數。文件包含以下:

  • $tid—–當前執行緒的執行緒ID
  • $pid——程式ID
  • $cmdline———-啟動程式的命令列字串
  • $user———-正在執行程式的賬戶資訊
  • $registername—–顯示暫存器registername 的內容

不管怎麼樣,關於最後一個錯誤的偽變數是非常有用的:

  • $err——–顯示最後一個錯誤的錯誤碼
  • $err,hr—顯示最後一個錯誤的錯誤資訊

更多閱讀:偽變數

 

技巧3:符合越界後檢視堆物件

有時候,在除錯符號越界後,你還想檢視物件的值,這個時候,watch視窗中的變數是被禁用的,不能再檢視(也不能更新),儘管物件仍然存在。你如果知道物件的地址,可以繼續充分地觀察它。你可以將地址轉換為該物件型別的指標,放在watch窗中。

下面的例子中,當單步跳出do_foo()之後,_foo不能再被訪問。但是,將它的地址轉換為foo*後,就可以繼續觀察這個物件。

 

技巧4:檢視陣列的值

如果你在操作一個很大的陣列(我們假設至少有幾百個元素吧,但是可能更少),在Watch視窗中展開陣列,查詢一些特定範圍內的元素很麻煩,因為你要不停地滾動.如果陣列是分配在堆上的話,你甚至不能在watch視窗中展開陣列元素.對此,有一個解決辦法。你可以使用(array+ <offset>),<count> 去檢視從<offset>位置開始的特定範圍的<count>元素(當然,這兒的陣列是你的實際物件)。如果想檢視整個陣列,可以簡單使用array,<count>.

如果你的陣列是在堆上,你可以在watch視窗中將它展開,但是要檢視某個特定範圍的值,用法稍有不同:((T*) array + <offset>),<count>(注意這種用法對於堆上的多維陣列也有效)。但是這種情況下,T是指陣列元素的型別。

如果你在用MFC,並使用其中的’array’容器,像 CArray, CDWordArray,CStringArray等等。你當然可以使用同樣的過濾方法。除此之外,你必須檢視array的m_pData成員,它是儲存資料的真實快取。

 

技巧5:避免進入不必要的函式

很多時候,你在除錯程式碼時可能會進入到你想跳過的函式,像建構函式,賦值操作或者其他的。其中最困擾我的是CString建構函式。下面是一個例子,當你準備單步執行take_a_string()函式時,首先進入到CString的建構函式。

幸運的是可以告訴偵錯程式去跳過哪些方法,類或者整個名稱空間。實現它的方法也已經改變了,回到使用VS6的日子,通常是通過autoexp.dat檔案來指定的。Vistual Studio 2002改成了使用登錄檔設定。想要跳過一些函式,你需要在登錄檔裡新增一些值(詳情如下):

  1. 實際位置取決於你使用的Vistual Studio版本和作業系統平臺(x86或x64,因為登錄檔只能在64位的Windows下瀏覽)
  2. 值的名字是數字,代表規則的優先順序;數字越大,優先順序越高。
  3. 值資料是一個正規表示式的REG_SZ值,用於指定怎樣過濾和執行。

為了避免進入任何CString方法,我新增了下面的規則:

 

有了這個,即使你強制進入上例中的take_a_string(),偵錯程式也會跳過CString的建構函式。

更多閱讀:

技巧6:從程式碼啟動偵錯程式 Launch the debugger from code

你可能很少需要將偵錯程式附加到程式中,但你不能在Attach視窗這樣做(可能因為中斷髮生太快而沒有捕獲到),你也不能一開始就在偵錯程式中啟動程式。你可以在程式中產生中斷給偵錯程式一個機會通過呼叫內部的_degbugbreak()來附加。

實際上還有其他的方法來完成,例如觸發中斷3,但這僅僅適用於x86平臺(C++64位不再支援ASM)。另外還有DebugBreak()函式,但它的使用不怎麼簡便,所以這裡推薦使用內部方法。

   程式執行內部方法時會停止執行,這時你就有機會將偵錯程式附加到該程式。

 

 

 

更多閱讀:

技巧7:在output視窗列印

通過呼叫DebugOutputString可以在偵錯程式的output視窗顯示一段特定的文字。如果沒有附加的偵錯程式,該函式什麼也不做。

 

更多閱讀:

技巧8:隔離記憶體洩漏

記憶體洩漏是在原生開發中的一個很重要的問題,要檢測記憶體洩漏是一個很嚴峻的挑戰,尤其是在大型專案中。Vistual Studio可以提供檢測記憶體洩漏的報告,還有其他的一些應用程式(免費的或商業的)也可以幫助你檢測記憶體洩漏.有些情況下,在一些記憶體分配最終會導致洩漏時,可以使用偵錯程式去中斷。但是你必須找到可再現的分配編號(儘管沒那麼容易)。如果能做到這一點,執行程式時偵錯程式才會中斷。

 我們來看下面的程式碼,分配了8個位元組,卻一直沒釋放分配的記憶體。Visual Studio提供了造成記憶體洩漏的物件的報告,多執行幾次,會發現一直是同一個分配編號(341)。

在一個特定的(可復現的)位置中斷的步驟如下:

  1. 確定你有足夠的關於記憶體洩漏的報告模式(參考 使用CRT庫檢測記憶體洩漏)
  2. 多次執行程式直到你能在程式執行結束後的記憶體洩漏報告裡找到一個可復現的分配編號,例如上個例子中的(341)
  3. 在程式一開始的地方設定一個斷點以便你能夠儘早地進行中斷。
  4. 當最初的中斷髮生時,watch視窗的Name欄裡會顯示:{,,msvcr90d.dll}_crtBreakAlloc,在Value欄裡寫入你想要查詢的位置編號
  5. 繼續除錯(F5)
  6. 程式執行到指定位置會停止,你可以使用呼叫棧被指引找到被該位置觸發的那段程式碼。

遵循這些步驟, 在上個例子中,使用分配的編號(341)就可以識別記憶體洩漏的起因。

 

技巧9:除錯發行版

除錯和釋出是兩個不同的目的。除錯配置是用於開發的,而釋出配置,顧名思義,是用來作為程式的最終版本,因為它必須嚴格遵循釋出的質量要求,該配置包含優化部分和除錯版本的中斷除錯的設定。而且,有時候,要像除錯除錯版本一樣去除錯發行版。要做到這一點,你需要在配置裡做一些改變。但是這種情況下,你就不再是在除錯發行版,而是除錯和發行的混合版。

 

你還應該做一些事兒,以下是必須要做的:

  1. 配置C/C++ >General>Debug Information Format 應該為 “Program Database(/Zi)”
  2. 配置C/C++ >Optimization>Optimization 應該為”Disabld(/Od)”
  3. 配置Linker>Debugging>Generate Debug Info 應該為”Yes/(DEBUG)”

如圖所示:

 

更多閱讀:怎樣除錯發行版

技巧10:遠端除錯

另一個重要的除錯就是遠端除錯,這是一個更大的話題,多次被提到,這裡我只做一下簡單的概括:

  1. 你需要在遠端機器上安裝遠端除錯監控
  2. 遠端除錯監控必須以管理員身份執行,並且使用者必須屬於管理員組
  3. 在你執行監控時,會開啟一個新的服務,該服務的名字必須用Visual Studio的Attach to Progress視窗的Qualifier組合框的值。

 

 

  1. 遠端和本地機器上的防火牆必須允許Visual Studio和遠端除錯監控之間能夠通訊
  2. 想要除錯,PDB檔案是關鍵;為了能夠讓VisualStudio自動載入它們,必須滿足以下條件:

1)本地的PDB檔案必須可用(在遠端機器的相同路徑下放置一個對應的模組)。

 2) 遠端機器上的託管PDB文化必須可用。

遠端除錯監控下載:

更多閱讀:

結束語

Ivan Shcherbakov那篇文章和我這篇文章提到的除錯技巧,在大多數的除錯問題中都是必不可少的。想要知道更多的關於除錯技巧的知識,建議閱讀文章中提供的額外閱讀。

Visual Studio原生開發的20條除錯技巧(下)

相關文章