軟體調試器奇怪現象——編譯優化
一、問題:
一版軟體編譯完後,程序員都會用調試工具(UDE,Lauterbach等)把執行文件Hex,S19刷入控制器CPU,然後單步調試一下執行邏輯,這是最基本的測試方法。但有的時候會發現一些奇怪的現象:輸入的條件與輸出的結果邏輯上不相符。例如,圖-1,變數ISO14229_len的值明明等於6,即紅框中的條件成立,但是程序卻執行到else裡面的邏輯了。為什麼呢?
二,分析:
第一問:會不會是高優先順序任務干涉共有變數ISO14229_len造成的?如圖-2所示,假設邏輯運行到斷點之前條件判斷的確小於2,在進入斷點前的一刻,由於其它高優先順序任務中斷,把變數改成了6,從而造成這樣的假象?對此,在紅框條件判斷的前面加一條語句,強制把ISO14229_len賦值等於6,並關中斷。發現執行結果還是在會進入else里。可見不存在高優先順序任務干涉共有變數ISO14229_len。
第二問:會不會是因為寫完程序後為了快速測試,選擇的是增量編譯,導致生成可執行文件不正確?如圖-3所示,M1,M2都引用了頭文件A.h,但是只有A.h, M1.c發生了變更,增量編譯僅更新M1.obj, M2.obj還是舊值。對此,首先刪除了所有舊的.obj,然後Rebuild All,但是發現S19文件跟增量編譯的結果一樣。說明不是這個原因引起的。
第三問:會不會是編譯器優化導致的?如圖-4,設有兩個函數Func_A(), Func_B()它們都會調用同一條語句Proc_X(1,2,3),對於該條語句,兩個函數里編譯器聲稱的彙編代碼完全不一樣。這樣當全局變數Value_A == 1, Value_B != 2時,運行結果是執行JMP Proc_X斷點就會返回到Func_A()的else里,給人的感覺是Func_A里的else成立了。實際上,這是一種假象!
三,驗證:
實測結果,的確是編譯器優化後導致的。打斷點測試本例圖-1中省去的邏輯被執行了,而且它裡面的邏輯結構與圖-4是相似的。
四,思考:
C語言雖然作為底層語言,但它實際是面向對象的,這個對象是編程者,測試者,及軟體架構師。為了程序的可讀性和較好的維護性,MISRA-C規則里禁止使用goto語句,這是很好的習慣,行業內大家都遵守。但是它帶來的結果是C程序的規模迅速增大。如果C語言的每一句都生成彙編代碼,那麼最終生成的可執行文件也會迅速增大,對於只有幾兆位元組左右的程序存儲空間來說隨著軟體規模的增大可能很快不夠用了,另外時間上的執行效率也會降低,這顯然是不現實的。所以好的編譯器會儘可能的優化源C代碼,一些優化規則,如:
1.只做了初始化的變數,常量全部被刪除,最後在map文件里找不到地址。
2.只有賦值操作,沒有讀操作的變數全部被刪除,最後在map文件里找不到地址。
3.邏輯和參數上完全一樣的多處語句,只對一處生成一段彙編代碼,其它幾處全部用Jump跳轉過去。
五,結論:
軟體測試過程中如果發現邏輯執行順序不對,並不是真正的不對,而是編譯器優化後的假象。相信編譯器不會出錯,真有錯誤也只能是編程錯誤。熟悉一些編譯優化規則有助於儘快查明問題原因。


TAG:南工電驅視界 |