2009年5月14日 星期四

Debug 和 Release的不同

有時候我們執行程式時會遇到Debug版可以跑,但是Release版本反而不能跑了,為什麼呢?
首先, 先了解DebugRelease的主要差異:

Debug版:包含Microsoft格式的除錯資訊,沒有進行最佳化,檔案體積較大
Release版:不包含除錯資訊,有針對執行速度最佳化,檔案體積較小

事實上Debug Release 並沒有本質的界限,他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動.

Debug各參數的定義:
Release各參數的定義:


那些情況下Release版的程式會出錯?
  • Runtime Library差異(MDd和MD)
  Runtime Library 包含了除錯用的訊息,並採用了一些保護機制以幫助發現錯誤,所以性能上較不如Release的版本。編譯器提供的 Runtime Library通常很穩定不會造成 Release 版本錯誤;倒是由於 Debug Runtime Library 加強了對錯誤的檢測,如Heap的記憶體分配,有時會出現Debug 有錯但 Release 正常的現象。這裡要注意的是,如果 Debug 有錯,即使 Release 正常,程式保證是有 Bug 的,只不過可能是 Release 版的某次執行沒有表現出來而已。

  • 最佳化的影響(O1和O2)
  在函式的呼叫過程中,所有呼叫的訊息(回傳位址、參數)以及自動變數都是放在堆疊中的。如果函式的宣告跟實作不同(參數、回傳值、呼叫方式),就會產生錯誤,但 Debug 方式下,堆疊的存取透過 EBP 暫存器儲存的位址來實現,如果沒有發生陣列越界之類的錯誤(或是越界「不多」),函式通常能正常執行;Release 方式下,最佳化會省略 EBP 堆疊基本位址的指標,這樣通過一個全域指標存取堆疊,可能就會造成回傳位址錯誤導致整個程式崩潰。

  當您建置專案進行偵錯時,您是使用偵錯記憶體配置器 (Debug Memory Allocator)。這表示所有的記憶體配置周圍都放置了保護位元組。這些保護位元組會偵測記憶體覆寫。由於Release的組建與Debug版的堆積配置不同,在Debug中的記憶體覆寫可能不會造成任何問題,但在Release的組建中卻可能產生災難性的影響。
  • 變數的初始化(GZ的選項)
  Deug通常會將記憶體和變數做初始化,包括用 0xCC 初始化所有自動變數,0xCD(Cleared Data)初始化Heap中分配的記憶體(即動態分配的記憶體,例如 new ),0xDD ( Dead Data ) 填充已被釋放的Heap記憶體(例如delete),0xFD(deFencde Data)初始化受保護的記憶體(debug 版在動態分配記憶體的前後加入保護記憶體以防止越界存取).

  Release 版中未初始化的變量是隨機的(沒有初始化),這有可能使指針指向一個有效地址而掩蓋了非法訪問.

  • DEBUG 與 NDEBUG
  當定義了 _DEBUG 時,ASSERT() 函式會被編譯,而 NDEBUG 時不被編譯。此外,TRACE() 巨集的編譯也受 _DEBUG 控制有些人會自行定義#ifdef _DEBUG 之類的條件編譯,必須注意。


解決Release 版出錯的問題
  • 逐項測試Release的編譯選項
  前面已經提過,DebugRelease只是一組編譯選項的差別,實際上並沒有什麼定義能區分二者。我們可以修改Release版的編譯選項來縮小錯誤範圍。如上所述,可以把Release 的選項逐個改為與之相對的Debug選項,如/MD改為/MDd、/O1改為/Od,或運行時間最佳化改為程序大小最佳化。 注意,一次只改一個選項,看改哪個選項時錯誤消失,再對應該選項相關的錯誤,針對性地查找。
  • 對變數作初始化
  • 檢查ASSERT(),TRANCE()和#ifdef _DEBUG 之類的條件編譯的使用
  • 注意VC的警告訊息
  在 Debug 版中使用 /W4 警告級別,這樣可以從編譯器獲得最大限度的錯誤訊息,比如 if( i =0 )就會引起 /W4 警告。不要忽略這些警告,通常這是你程式中的 Bug 引起的。但有時 /W4 會帶來很多冗長的訊息。我們可以用:

   #progma warning(disable: 4702) //禁止
   //…
   #progma warning(default: 4702)
   //重新允許來暫時禁止某個警告,或使用

   #progma warning(push, 3) //設置警告級別為 /W3
   //…
   #progma warning(pop) //重設為 /W4
   //暫時改變警告級別,有時你可以只在認為可疑的那一部分程式碼使用
  • Dll的使用
  將其他不同版本的Dll混合造成不一致的現象稱為“動態連結庫的地獄(Dll Hell)”
  http://www.codeproject.com/dll/The_DLL_Hell.asp
  http://www.developerfusion.co.uk/show/1719/9/

  如果你的程式使用DLL時請注意,不能將debugrelease版的DLL混合在一起使用。
  • 利用Debug工具
   DebugView : 可用來捕捉OutputDebugString(),可以在VC的環境外執行程式查看除錯訊息.
   Gimpel Lint :靜態代碼檢查工具,不過要花$.
   SmartChecker: Rum-Time錯誤檢查工具
   BoundsChecker: Rum-Time錯誤檢查工具



1 則留言:

  1. 這是一個很好的開始,每次你都可以有很好的見解,也希望你的知識可以「影響」這個世界。

    看了這篇,突然想起來一件事,我一直很想知道,卻一直很懶得去查,visual studio 中各個選項的意義與用法,至少至少,在 debug, release 中預設的那幾個,另外,像 /clr 之類的....

    有空的話出一篇心得文吧

    回覆刪除