做編譯器的人如果遇到了bug,他們怎麼判斷是編譯器自己的問題還是編譯這個編譯器的編譯器的問題?
我們公司有自己的編譯器,恰好我還給編譯器組提過bug。並且還幫他們分析出來了。
編譯器的bug我見過的有以下幾種:
表達式/宏展開/跳轉/循環等解析的時候出錯。
複雜的宏/函數在優化的時候出了問題。
------------------------------
第一類問題屬於編譯器自己的問題,這個很容易定位,比如:* (i++) * a &>&> 5 這種,只要用一個printf列印出來就可以看出來是不是編譯器的問題。如果題主學過編譯原理就知道這種如果出錯的話,會有大面積的錯亂,特別容易發現。因為這種錯誤會導致很多地方出錯。
第二類問題則比較難以定位,因為這種優化問題對應的代碼都特別長(我當時是處理一個2000多行的代碼)。但有一個技巧就是改變編譯參數。每個編譯器都有優化等級和一些編譯選項,當打開/關閉某些開關的時候發現代碼執行的結果不一樣了,那麼就可以懷疑編譯器是不是有問題了,當然如果是多任務的話需要考慮時序問題,但如果一個單一的函數,沒有時序和調度的問題,改變優化開關就出問題,那麼編譯器有問題的可能性是很大的。
這個時候,要具體定位問題就很麻煩了,一般的做法是抽出一個跟平台不相關的代碼,然後把不必要的硬體操作等都去掉,把調用外部的函數都改成代碼編譯出問題的返回值,這時候函數的執行流程就會變得清晰。然後就可以判斷具體編譯器哪裡出了問題了。
但可惜的是多數情況下,寫代碼的人要分析到彙編級來查看代碼是否有誤,然後才能定位是不是編譯器的問題。作為一個開發者,我很痛恨編譯器bug。
但題主你知道嗎,最可怕的不是編譯器bug,是未知的硬體bug,我見過一款內存重複讀寫上萬次以後某位的0變成了1,這種硬體bug才是最頭疼的事情,尤其是硬體bug發生在一個高層應用代碼的環境里的時候,要最終定位特別困難。
------------------------------------------
硬體bug的那個問題:用CPU級的調試工具(很貴),當軟體出問題的時候把整個系統都停止,然後dump內存,發現鏈表有一個節點出了問題,發現每次出問題的節點數據只有一個bit不同,後來決定對那片內存反覆讀寫N次,結果就出問題了。也算碰運氣吧。不過硬體行為跟手冊不一致的這種事情還挺多的,都憑各種奇怪的經驗。
我來談談吧,至少是做過正規商業編譯器開發的,拋磚引玉吧。
編譯器有Bug那實在太正常了,沒有Bug那真的才不正常,尤其是C++編譯器,無論多號稱完全支持,都總能找到攻破其的辦法,攻破的神器就是模板。有一段時間我最喜歡做的事情就是去找Visual C++ Compiler、GCC、Clang的Bug,非常有意思。當然,具體的攻破例子這裡就不說了,也與問題無關。
要判斷是編譯器自身的問題還是編譯這個編譯器的編譯器有問題,我覺得問題分為兩部分吧。
第一部分是確定是否是編譯器自身的問題。這個確認過程一般是Tester跑測試用例然後發生了與預定行為不一致的情況,然後由Tester(有時候與Developer一起)來確認是屬於編譯器的Bug。而編譯器的Bug我們一般也是有嚴重程度劃分,最嚴重的自然是用戶有時候都能碰見的ICE(Internal Compiler Error)。而編譯器的Bug範圍也很廣,有編譯器的指定選項未達到預定效果的,同時有編譯代碼的時候產生與語言標準不一致的地方,做編譯器的人有兩份必備資料,一份資料是編譯原理,另一份資料就是語言標準。語言標準其實也是非常博大精深或者晦澀的東西,所用的詞語都是需要你去細細捉摸的,然後來判定到底是哪一個出了問題。我想不是做編譯器的人可能也很少會去研究什麼叫做Declaration,Declarator等等專業術語。而確認了編譯器自身的Bug後,就是去fix掉,這自然就是Developer的職責了,也就和做其他軟體類似了。而至於流程可以參考我在另一個問題的回答。有哪些軟體相關的公司,開發流程比較規範?第二部分就是確定是否是編譯這個編譯器的編譯器有問題,那麼這裡我就舉例子吧。在Linux平台下,很多編譯器都是用GCC來編譯自己寫的編譯器(自舉也有,不過大部分都是先用GCC編譯,到一定版本後再自舉),那麼這個時候也許會發生一些「意外」,似乎表現與預想行為不一致。那麼這個時候,要來確認是GCC的問題,第一步就是要排除是我們編譯器的問題。那麼這個確認過程就是上面第一部分回答的。而如果排除自身的問題後,那麼要確認是GCC的問題,需要首先與以前的版本比較。比如用以前的GCC版本編譯是不是可以達到預想的結果,如果可以達到,自然可能是GCC修改了一些地方,然後我們沒有跟進。如果什麼公開的修改都沒有,那麼就是升級後出現了一些問題,那麼我們有一個任務當然就是去報GCC的Bug,這也是一件很有意思的過程。而報GCC的Bug,一般我們會把測試用例Deduce到很小的部分,不會涉及我們編譯器的代碼,而只是一些通用的標準C/C++代碼。
好了,大致解決思路與方案就是如此。測試啊,調試啊,看編譯出來的彙編符不符合預期啊....... 你做別的程序也要面對這個情況的
你可把方法名寫成注釋啊。只要不怕長。
推薦閱讀:
※學習編譯原理只是研究lex和yacc嗎?
※sibling call是什麼?
※現在的編譯器的inline策略是怎樣的?
※devcpp編譯生成一個無許可權運行的exe,並且無法再次修改編譯也無法刪除exe,如何解決?
※誰看完過龍書虎書鯨書?全部看完是不是就有能力寫一個C語言的編譯器了?