簡體   English   中英

在調試模式下不存在發​​布版本中的錯誤的常見原因

[英]Common reasons for bugs in release version not present in debug mode

僅在發布編譯模式下出現但在調試模式下不出現的錯誤和異常程序行為的典型原因是什么?

很多時候,在 C++ 的調試模式下,所有變量都是空初始化的,而除非明確說明,否則在發布模式下不會發生同樣的情況。

檢查任何調試宏和未初始化的變量

您的程序是否使用線程,那么優化也會導致發布模式出現一些問題。

還要檢查所有異常,例如與發布模式沒有直接關系,但有時我們只是忽略一些關鍵異常,例如 VC++ 中的內存訪問沖突,但至少在其他操作系統(如 Linux、Solaris)中同樣可能存在問題。 理想情況下,您的程序不應捕獲諸如訪問 NULL 指針之類的關鍵異常。

一個常見的陷阱是在 ASSERT 中使用具有副作用的表達式。

過去,我被許多錯誤所困擾,這些錯誤在 Debug 版本中很好,但在 Release 版本中會崩潰。 有許多根本原因(當然包括本主題中已經總結的那些),並且我已經被以下所有原因所吸引:

  • #ifdef _DEBUG中的成員變量或成員函數,以便類在調試版本中具有不同的大小。 有時#ifndef NDEBUG用於發布版本
  • 同樣,有一個不同的#ifdef恰好只出現在兩個版本之一中
  • 調試版本使用系統庫的調試版本,尤其是堆和內存分配函數
  • 發布版本中的內聯函數
  • 頭文件的包含順序。 這應該不會導致問題,但是如果您有諸如#pragma pack類的東西尚未重置,那么這可能會導致嚴重的問題。 使用預編譯頭和強制包含也會出現類似問題
  • 緩存:您可能有一些代碼,例如僅在發布版本中使用的緩存,或不同的緩存大小限制
  • 項目配置:調試和發布配置可能有不同的構建設置(使用 IDE 時可能會發生這種情況)
  • 由於僅調試代碼而發生的競爭條件、計時問題和其他副作用

我多年來積累的一些用於深入調試/發布錯誤的技巧:

  • 如果可以,嘗試在調試版本中重現異常行為,甚至更好的是,編寫一個單元測試來捕獲它
  • 想想兩者之間的不同之處:編譯器設置、緩存、僅調試代碼。 嘗試暫時最小化這些差異
  • 創建關閉優化的發布版本(這樣您更有可能在調試器中獲得有用的數據)或優化的調試版本。 通過最小化調試和發布之間的更改,您更有可能能夠隔離導致錯誤的差異。

其他差異可能是:

  • 在垃圾收集語言中,收集器通常在釋放模式下更積極;
  • 內存的布局可能經常不同;
  • 內存可能會以不同的方式初始化(​​例如,可以在調試模式下歸零,或者在發布時更積極地重新使用);
  • 局部變量可能會被提升為在 release 中注冊值,這可能會導致浮點值出現問題。

是的!,如果您有條件編譯,則可能存在計時錯誤(優化的發布代碼節,未優化的調試代碼)、內存重用與調試堆。

它可以,特別是如果您在 C 領域。

一個原因可能是 DEBUG 版本可能會添加代碼來檢查雜散指針並以某種方式保護您的代碼免於崩潰(或行為不正確)。 如果是這種情況,您應該仔細檢查從編譯器獲得的警告和其他消息。

另一個原因可能是優化(通常對發布版本打開,在調試時關閉)。 代碼和數據布局可能已經優化,而您的調試程序只是,例如,訪問未使用的內存,而發布版本現在正在嘗試訪問保留內存甚至指向代碼!

編輯:我看到其他人提到了它:當然,如果不在調試模式下編譯,您可能有整個代碼部分被有條件地排除在外。 如果是這樣,我希望這真的是調試代碼,而不是對程序本身的正確性至關重要的東西!

CRT 庫函數在調試與發布(/MD 與 /MDd)中的行為不同。

例如,調試版本通常會預填充您傳遞給指定長度的緩沖區以驗證您的聲明。 例子包括strcpy_sStringCchCopy ,等等。即使琴弦較早終止,您szDest最好是N久字節!

當然,例如,如果您使用類似的結構

#if DEBUG

//some code

#endif

在非 void 函數中,所有執行路徑都應以 return 語句結束。

在調試模式下,如果您忘記用 return 語句結束這樣的路徑,那么該函數通常默認返回 0。

但是,在發布模式下,您的函數可能會返回垃圾值,這可能會影響程序的運行方式。

您需要提供更多信息,但是是的,這是可能的。 這取決於您的調試版本的作用。 您可能會進行日志記錄或額外檢查,這些檢查不會被編譯到發布版本中。 這些僅用於調試的代碼路徑可能會產生意想不到的副作用,這些副作用會以奇怪的方式改變狀態或影響變量。 調試構建通常運行速度較慢,因此這可能會影響線程和隱藏競爭條件。 與發布編譯的直接優化相同,發布編譯可能(盡管現在不太可能)將某些內容作為優化短路。

沒有更多細節,我會假設“不正常”意味着它要么不編譯,要么在運行時拋出某種錯誤。 檢查您是否有依賴編譯版本的代碼,通過#if DEBUG語句或通過標記有Conditional屬性的方法。

在 .NET 中,即使您不使用#if DEBUG類的條件編譯,編譯器在發布模式下的優化仍然比在調試模式下更加自由,這也可能導致僅發布錯誤。

有些編譯器優化可能會破壞有效代碼,因為它們過於激進。

嘗試在啟用較少優化的情況下編譯您的代碼。

這是可能的,如果你有條件編譯,使得調試代碼和發布代碼不同,並且代碼中有一個只在發布模式下使用的錯誤。

除此之外,這是不可能的。 調試代碼和發布代碼的編譯方式有所不同,代碼在調試器下運行與否的執行方式也有所不同,但如果這些差異中的任何一個導致性能差異以外的任何其他原因,那么問題就一直存在。

在調試版本中,錯誤可能不會發生(因為時間或內存分配不同),但這並不意味着錯誤不存在。 也可能還有其他與調試模式無關的因素改變了代碼的時序,導致錯誤發生與否,但歸根結底,如果代碼正確,錯誤就不會發生在任何情況下。

所以,不,調試版本不能僅僅因為您可以運行它而不會出錯。 如果在發布模式下運行時發生錯誤,那不是因為發布模式,而是因為錯誤從一開始就存在。

這是可能的。 如果它發生並且不涉及條件編譯,那么您可以非常確定您的程序是錯誤的,並且僅由於偶然的內存初始化甚至內存布局而在調試模式下工作!

我剛剛在調用未恢復寄存器先前值的匯編函數時遇到過這種情況。

在“發布”配置中,VS 使用 /O2 進行編譯,這優化了代碼的速度。 因此,一些局部變量僅映射到與上述函數共享的 CPU 寄存器(用於優化),從而導致嚴重的內存損壞。

無論如何,看看您是否沒有在代碼中的任何地方間接弄亂 CPU 寄存器。

我記得不久前我們在 c/c++ 中構建 dll 和 pdb。

我記得這個:

  • 添加日志數據有時會使錯誤移動或消失,或者出現完全其他的錯誤(因此這不是一個真正的選擇)。
  • 其中許多錯誤與 strcpy 和 strcat 中的 char 分配以及 char[] 數組等有關...
  • 我們通過運行邊界檢查器並簡單地修復內存分配/釋放問題來清除一些。
  • 很多時候,我們系統地檢查了代碼並修復了字符分配。
  • 我的兩點是它與內存分配和管理以及調試模式和釋放模式之間的約束和差異有關。

然后繼續經歷那個循環。

我們有時會臨時交換 dll 的調試版本的發行版,以免在處理這些錯誤時推遲生產。

另一個原因可能是數據庫調用。 您是否在同一線程中多次保存和更新同一記錄,有時是為了更新。 更新失敗或未按預期工作可能是因為之前的創建命令仍在處理中,而對於更新,db 調用未能找到任何記錄。 這不會在調試中發生,因為調試器確保在登陸前完成所有掛起的任務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM