調試(Debugging)調試方法

 

測試主要是發現錯誤,調試(也稱糾錯)則是肯定錯誤的緣由和準確位置,並加以糾正。 程序員

調試是包含2個步驟,從執行了一個成功的測試用例、發現了一個問題以後開始。第1步,肯定程序中可疑錯誤的準確性質和位置;第2步,修改錯誤。錯誤定位是一項技術活,是有必定難度的。目前有4種常見方法。 小程序

1. 蠻幹(brute force)(也叫暴力法)

調試程序的最爲廣泛的模式是所謂的「暴力」方法。這種方法之因此流行,主要有2個緣由: 工具

1)它不須要過多的思考,是耗費腦力最少的方法。新手上路,都喜歡這種方法。 測試

2)人遇到bug,可能會自尊心受損、熱情耗盡、也可能迷失方向,(總之心情很差,須要發泄啊),因此使用暴力虐待程序。 .net

暴力調試方法,主要有3種類型: debug

(1)利用內存信息輸出來調試 設計

使計算機將存儲器的所有內容和地址打印出來,而後在這數以萬計的數據中尋找錯誤所在。這種方法也叫主存信息轉儲,雖然有時能得到成功,但更多的是白白浪費時間、紙張和人力。 調試

(2)在程序中各點設置打印語句 對象

本人,大學時代經常使用這個方法,一個print,輸出變量值。。。 ip

(3)使用自動化的調試工具進行調試

自動化調試工具的工做機制,相似於在程序中插入打印語句,可是不修改代碼。

還有一個效率改進型的暴力法。

(4)2分查找(對分查找法)

對分查找法。這種方法主要用來縮小錯誤的範圍。若是已經知道程序中的變量在若干位置的正確取值,能夠在這些位置上給這些變量以正確值,運行程序觀察輸出結果,若是沒有發現問題,則說明從賦予變量一個正確值開始到輸出結果之間的程序沒有出錯,問題可能在除此以外的程序中,不然錯誤就在所考察的這部分程序中。對含有錯誤的程序段再使用這種方法,直到把故障範圍縮小到比較容易診斷爲止。

我的經驗,暴力調試方法有很多缺點:無關數據量很大;有時須要修改代碼(可能忘記刪除調試代碼);效率底下……

這些暴力調試方法的主要問題在於:它們都忽略了思考的過程

咱們能夠在調試程序和偵破謀殺案之間找出類似點來。實際上,在幾乎全部的謀殺懸念小說中,謎案都是經過仔細分析線索,將表面上不重要的細節全聯結起來而最終偵破的。這不是一個使用蠻力的方法,要使用蠻力的是尋覓障礙物或搜尋財寶。

還有一些證據代表,不管調試小組成員是富有經驗的程序員仍是學生,肯動腦筋而不是依賴別人幫助的人可以更快、更準確地發現程序的錯誤。所以,咱們建議僅在下列狀況下使用暴力調試方法:

(1)其餘的方法都失敗了(能夠結合「2分查找」來逐步縮小出錯範圍)

(2)做爲思考過程的補充(輔助方法),而不是替代方法

2. 消除緣由(cause eliminatio)

經過思考列出可能的緣由,逐個排除,最後找出問題所在。這種方法又分爲概括法和演繹法。

2.1概括法(induction

概括的思考過程就是從特殊到通常,從線索(即錯誤的症狀,多是一個或多個測試用例的結果)出發,尋找線索之間的聯繫、歸納出共同特色、得出通常規律。概括的過程以下圖所示。

clip_image002

它分爲以下4個主要步驟。

(1)肯定相關的數據

調試人員犯的一個主要錯誤是未能將全部可用的數據或症狀都考慮進去。第一步是列舉出全部知道的程序執行正確和不正確之處,這些不正確之處便是症狀,讓咱們相信確實存在錯誤。那些類似而不相同、且未引發症狀出現的測試用例提供了額外的有價值的線索。

(2)組織相關數據

概括意味着從特殊到通常,所以第2步是組織這些相關數據,以便觀察線索間的模式。

最重要的是搜尋矛盾。

(3)做出假設(注1)

研究線索之間的聯繫,利用線索的結構中可能的模式做出一個或多個關於錯誤緣由的假設。若是沒法做出假設,就須要更多的數據,這些數據能夠經過設置和執行附加的測試用例來獲得。若是有多個假設存在,首先選擇最有可能的一個。

注1)所謂假設,就是對於懷疑所產生的問題,提出一種推測性的或可能性的說明。

(4)證實假設

此時,常犯的主要錯誤是跳過這一步而進入結論階段,並企圖肯定錯誤所在,這是糾錯中常常發生的問題。可是在修改錯誤以前,證實這些假設的合理性是極其重要的,忽視這一點將致使只能肯定問題的一個徵兆或一部分徵兆。假設的證實是靠比較它與最初的線索或數據,確信這些假設能夠徹底解釋這些線索的存在。若是沒法解釋,要麼這些假設是無效的或不完整的,要麼還有更多的錯誤存在。

2.2演繹法(deduction)

演繹的過程是從一些廣泛的理論或前提出發,使用排除和精煉的過程,達到一個結論(錯誤的位置)。過程以下圖所示。

clip_image004

舉個謀殺犯的例子,與概括過程相反,首先從一系列嫌疑人入手,經過排除(花匠有當時不在現場的合理證詞)和提煉(罪犯多是紅色頭髮)的過程,判斷出管家可能犯了罪。演繹的步驟以下:

(1)列舉出全部可能的緣由或假設

第1步是創建1份全部想象獲得的錯誤線索的清單,線索不須要有完整的解釋;它們純粹是一些推測,幫助咱們組織和分析現有的數據。

(2)利用數據排除可能的緣由

詳細檢查全部的數據,尤爲尋找存在矛盾的地方,而後儘可能排除全部可能的緣由,僅留下1條。若是全部的緣由都排除掉了,須要增長額外的測試用例,獲得更多的數據來設計新的推測。若是剩下的緣由多餘1個,那麼首先選擇最有可能的緣由,即主要假設。

(3)提煉剩下的假設

此時的可能緣由也許是正確的,但可能不夠具體,不能指出錯誤來。所以,須要使用現有的線索來提煉這個準則。舉例來講,咱們可能首先想到「對文件中最後一行的處理可能存在錯誤」,將其更加具體化,提煉爲「對文件最後一行的文件結束指示器的處理,可能存在錯誤」。

(4)證實剩下的假設

這個重要步驟與概括法中的第4個步驟相同。

3. 回溯法調試

在小程序中定位錯誤的一種有效方法是沿着程序的邏輯結構回溯不正確的結果,直到找到程序邏輯出錯的位置。也即,從程序產生不正確結果的地方開始,從該處觀察到的結果推斷出程序變量應該是些什麼值。在頭腦中,從這個位置開始逆向執行程序,重複使用「若是程序在此處的狀態時這樣的,那麼程序在上面位置的狀態就必然是那樣的」過程,就能很快定位出錯誤。

適用對象:小程序;代碼很是獨立的模塊等。

4. 測試法調試

最後一個「思惟型」的調試方法是使用測試用例。考慮下面兩種類型的測試用例:供測試的測試用例,其目的是暴露出之前還沒有發現的錯誤;供調試的測試用例,其目的是提供有用的信息,供定位某個被懷疑的錯誤之用。換句話說,當發現了某個被懷疑的錯誤的症狀以後,咱們須要編寫與原先有所變化的測試用例,儘可能肯定錯誤的位置。

實際上,這種方法不是一個徹底獨立的方法;它經常結合概括法一塊兒使用,以得到進行假設和/或證實假設所需的信息。它能夠和演繹法一塊兒使用,以排除嫌疑的緣由,提煉剩下的假設,並/或證實假設。

5. 建議

若是,曾經遇到過相似錯誤,或者有過相似經驗,建議使用演繹法。

              ↓↓

若是錯誤歷來沒有遇到過,請使用概括法。

             ↓↓

下列場合,建議使用回溯法

1)程序比較小

2)模塊比較獨立

3)已初步知道出錯代碼位置(經過查看log等,已初步知道代碼範圍)

↓↓

若是,概括法、演繹法、回溯法都沒法奏效,請使用暴力法(死馬當活馬醫)。此時,請結合對分查找法,這樣能夠提升效率0.5~1倍。

附錄

爲方便打印和查看,作一個debugging 總體圖。

clip_image006

相關文章
相關標籤/搜索