2012-07-11 157 views
3

我正在用包含圖形文件解析器的C++/Qt編寫程序。我使用g++編譯該項目。在開發過程中,我經常比較不同編譯器標誌之間關於優化和調試信息以及Qt調試標誌(打開/關閉qDebug()和Q_ASSERT())的低級別解析器層的性能。編譯器優化使程序崩潰

現在我面臨一個問題,其中唯一正確運作的版本是沒有任何優化。所有其他版本,即使是-O1,似乎都以另一種方式工作。他們由於不滿意的斷言而崩潰,當編譯時沒有-O...標誌時,它們得到滿足。該代碼不會產生任何編譯器警告,即使使用-Wall

我很確定我的程序中有一個錯誤,這似乎只對啓用優化有害。問題是:即使在調試程序時我也找不到它。解析器似乎從文件中讀取錯誤的數據。當我運行一些簡單的測試用例時,它們運行得很完美。當我運行一個更大的測試用例(一個直接從文件讀取的圖上的路由計算)時,文件中有一個不正確的讀取,我無法解釋。

我應該從哪裏開始追蹤這種未定義行爲的問題? 這種不同的行爲可能涉及哪些優化方法?(我可以一個接一個地啓用所有的標誌,但我不知道那麼多的編譯器標誌,但是我知道它們有很多,所以這將需要很長時間。)只要我知道哪種類型的錯誤是,我相信我遲早會發現它。

如果你能告訴我哪種編譯器優化方法可能成爲這類問題的候選者,你可以幫助我很多。

+3

聽起來像某處的未定義的行爲。 – 2012-07-11 00:17:28

+0

@ R.MartinhoFernandes是的,但我怎麼找到它?也許使用valgrind? – leemes 2012-07-11 00:18:08

+1

是的,valgrind會警告你未經初始化的值。 – MatijaSh 2012-07-11 00:22:32

回答

15

在優化版本中通常會出現一些類錯誤,這些類通常不會出現在調試版本中。

  1. 未初始化變量。編譯器可以捕獲一些但不是全部。看看你所有的構造函數,看看全局變量。等等。特別尋找未初始化的指針。在調試版本中,內存被重置爲零,但在發佈版本中,它不是。

  2. 使用超出範圍的臨時設備。例如,當您返回一個函數中的本地臨時引用時。這些通常在調試版本中工作,因爲堆棧被填充得更多。臨時人員傾向於在堆疊上存活一段時間。

  3. 陣列超出了臨時書寫。例如,如果在一個函數中創建一個數組作爲臨時函數,然後在結尾寫入一個元素。同樣,堆棧在調試時會有額外的空間(用於調試信息),並且溢出不會影響程序數據。

+1

非常感謝。這不僅適用於這種情況,而且適用於未來的情況,當我再次遇到這樣的問題時! – leemes 2012-07-11 00:45:44

1

1)在破損版本上使用valgrind。 (對於這個問題,可以嘗試在工作版本上使用valgrind,也許你會幸運的。)

2)使用「-O1 -g」構建系統,並使用gdb執行程序。在碰撞時,哪個變量具有不正確的值?重新運行你的程序並記錄該變量被寫入的時間(或者當它不是,本應該是的時候)。

2

您可以從優化版本中禁用優化,以幫助更輕鬆地調試優化版本。

-g -O1 -fno-inline -fno-loop-optimize -fno-if-conversion -fno-if-conversion2 \ 
    -fno-delayed-branch 

這應該讓你在調試器中的代碼更容易跟蹤。

另一個建議是,如果斷言你沒有給你足夠的信息導致什麼問題,你應該考慮添加更多的斷言。如果您擔心性能問題或斷言混亂,則可以將它們包裝在宏中。這使您可以將調試斷言與最初添加的斷言區分開來,以便稍後禁用或從代碼中刪除它們。

+0

謝謝。 Q_ASSERT就是這樣一個宏,這取決於'#define'的存在。宏在發佈構建模式下被禁用(對編譯器使用-D標誌)。 – leemes 2012-07-11 00:43:54

+0

@leemes:嘿。我只是想要添加新的跟蹤,而不是已經存在的新跟蹤。問候 – jxh 2012-07-11 02:10:33