天天看點

《Effective Debugging:軟體和系統調試的66個有效方法》一第4條:從具體問題入手向上追查bug,或從高層程式入手向下追查bug

本節書摘來自華章出版社《effective debugging:軟體和系統調試的66個有效方法》一書中的第1章,第1.4節,作[希]迪歐米迪斯·斯賓奈裡斯(diomidis spinellis),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

要想确定問題的來源,通常有兩種辦法。一種是從問題的具體表現入手,向上追查其來源,還有一種是從應用程式或系統的頂層入手,逐漸向下探查,直至找到其根源。對于某種類型的問題來說,其中一種方法的效果通常要比另一種更好,但是如果你在采用某個方法時遇到了困境,那麼不妨試試另一個方法。

如果問題表現得很明确,那我們就應該從發生問題的地方入手,向上追查bug。這可以分成三種情況。

第一種情況是程式崩潰。在這種情況下,為了便于排查問題,我們通常可以考慮用調試器來運作程式,也可以在它崩潰的時候把調試器連接配接到程式上面,或取得記憶體轉儲資訊(參見第35條)。我們要檢查各變量在程式崩潰時的取值,看看有沒有null值、損壞的值或未初始化的值,這些都有可能是引發崩潰的原因。對于某些系統來說,我們可以通過0xbaadf00d(代表bad food)這樣的特殊位元組值來找出尚未初始化的變量。維基百科的magic number詞條列出了很多這樣的特殊值。找到了取值不正确的變量之後,就應該設法查出導緻此現象的原因,為此,我們可以試着在程式發生崩潰的這個例程之内探查,也可以沿着調用棧向上尋找不正确的參數或與崩潰有關的其他因素(參見第3條和第32條)。

如果這些辦法找不到原因,那麼可以用調試器來多次調試程式,每次都在有可能發生運算錯誤的地方附近設定斷點。像這樣反複地設定斷點并沿着調用序列上移,或許可以幫助我們查出問題的原因。

第二種情況是程式當機(freeze),這與程式崩潰有所差別,是以我們向上排查所用的辦法也稍有不同。我們可以用調試器來運作程式,或将其連接配接到程式上面,然後用相應的調試器指令來中斷其執行過程(參見第30條),或使程式生成記憶體轉儲資訊(參見第35條)。有時你會發現,程式所執行的某些代碼,并不是該程式自身的代碼,而是某個程式庫中的例程。無論中斷發生在何處,我們都可以沿着調用棧向上排查,以便确定導緻程式當機的那個循環。檢查該循環的終止條件,并試着找出它永遠無法得到滿足的原因。

第三種情況是程式在出現問題時發出了錯誤消息,此時我們首先應該在程式的源代碼裡找到消息文本的位置。這可以通過fgrep-r指令輕松地實作(參見第22條),該指令能夠在任意深度和複雜度的目錄結構中快速定位到待搜尋的詞句。對于當今很多本地化的軟體來說,該指令所定位到的内容,通常并不是發出錯誤消息的那行代碼,而是與錯誤消息相對應的那個字元串資源檔案。例如,如果你住在講西班牙語的地方,并且正在調試inkscape繪圖程式中與“ha ocurrido un error al procesar el archivo xcf”錯誤消息有關的問題,那麼用fgrep-r指令搜尋inkscape的源代碼之後,它就可能會把你引向名為es.po的西班牙語字元串翻譯檔案:

《Effective Debugging:軟體和系統調試的66個有效方法》一第4條:從具體問題入手向上追查bug,或從高層程式入手向下追查bug

從字元串翻譯檔案中,我們可以得知與錯誤消息相對應的源碼位置(對于上例來說,就是share/extensions/gimp_xcf.py檔案的第43行)。然後,我們可以在發出錯誤消息的源代碼這裡設定斷點,或在它之前插入log語句,以檢查程式運作到此處所發生的問題。在這種情況下,我們有可能也要後退幾行或沿着調用棧向上回溯幾層,才能夠找到問題的根源。如果你要搜尋的是非ascii文本,那麼請確定指令行的locale(區域)設定與源代碼所用的文本編碼(如utf-8)相符。

如果無法确定與故障有關的代碼到底在哪裡,那我們就應該從頂層系統開始,逐漸向下查找故障原因。從定義上來說,這種故障通常屬于系統的湧現屬性(emergent property),也就是無法與某個具體部分直接對應起來的屬性,例如,性能問題(軟體占用的記憶體過多或響應時間過長)、安全問題(web應用程式的頁面遭到破壞)以及可靠性問題(軟體無法提供預期的web服務)等。

要想由上而下地排查錯誤,我們需要把整個程式分成多個部分,然後分别判斷每一部分在引發目前故障的各種因素中可能占多大的比例。對于性能問題來說,常見的辦法是做profile(性能分析),也就是用一些工具和程式庫來幫助我們尋找占用cpu資源及記憶體過多的例程。對于安全問題來說,我們要檢查代碼中有哪些地方可能出現常見的安全漏洞,如緩沖區溢出、代碼注入以及跨站腳本攻擊等。面對這類問題,我們也可以求助于一些代碼分析工具(參見第51條)。最後,對于無法提供web服務的問題來說,我們需要審視内部和外部的各種依賴關系,看看它們有沒有在正常地運作。

要點

如果能夠明确指出故障的原因,那麼應該從下往上查找錯誤,例如,在程式崩潰、程式當機以及程式發出錯誤消息等情況下,就應該如此。

如果故障的原因很難鎖定,那麼應該從上往下查找錯誤,例如,在遇到性能問題、安全問題以及可靠性問題的時候,就應該如此。

繼續閱讀