二分法調試代碼具體指什麼?
大約2個月前,我看過一篇帖子,詢問程序員新人如何找bug的。很多人提到了二分法排錯,我不太確定到底是個什麼概念。
它與沿著調用樹追蹤Bug有什麼區別?如果能描述一下操作過程更好。
舉個例子,我寫了一個一萬行的函數然後崩潰了,而且由於某些神奇的原因VC++就是不告訴我崩在了哪裡,那怎麼辦呢?
我先注釋掉後面一半的代碼,然後崩潰了,就證明蹦在這裡了,如果沒崩潰,就證明崩在了後面。於是你立刻就把範圍縮小了一半。剩下如法炮製,直到定位到一行為止。
==========================================================
我還有一次,是跟一個人合作。對方一直在改,我一個feature寫得比較久,當人家checkin了幾十次之後,我merge下來,跑了一次test,居然崩潰了。我在指責了那個人每次checkin前都不跑test之後,我就開始了二分調試法:
首先我知道,version 100是好的,version200 是不好的,所以我就把version150弄下來跑一下,發現沒崩潰,就把version 175弄下來跑一下,發現崩潰了,就把version 163弄下來跑一下,發現崩潰了……如法炮製,最後定位到了一個version,然後把diff貼在email裡面,發郵件再譴責一次,勒令對方修改……當然那個diff為什麼會造成問題我沒看,我還得繼續做下一個feature……
git bisect
git提供的基於二分法高效查找工具,它可以使用一個測試腳本,根據腳本的返回值指定版本是否正確,然後git在提交日誌中版本區間自動查找bug,可以二分排除問題出在哪一個版本 版本A沒有問題 版本B有問題 版本A和B中間再測試有沒有問題就把搜索範圍縮小一半
被 @趙越 小越越邀請了. 獻醜一答.
我就舉個例子好了.
對象生存周期問題,不知何時被 delete 了,然後在某個地方用了被析構的對象的指針,程序 coredump 了。你想知道到底在哪裡被析構了,當時用的辦法是在程序不同位置,『摸』一下,那塊內存(比如讀出幾個位元組阿,拷貝一下阿,用指針調調對象的方法啦之類的),沒core 就說明在那之後被析構的,core 了就說明在那之前被析構的,如法炮製,二分搜索,很快定位在哪裡被析構的,然後再改 bug.
二分調試就是通過某種特徵(比如程序崩潰、某個變數的值/內存中的數據、是否出現某條日誌、是否出現某個現象,以及任何有用的特徵),加上一個能把問題可能出現的空間劃分兩半的一個點(一行assert、一個斷點、一行打日誌的代碼、一個版本號、等等),二者結合就能把問題可能出現的範圍縮小(比如能判斷出錯誤代碼出現在那行assert之前等之類),跟二分搜索一樣。反覆如此即可快速定位錯誤出現的位置。是為二分調試大法,畢生絕學,每次遇到棘手的bug,最後用二分基本都能最後搞定,所以bug穩定復現很重要阿。其實簡單一點說,就是試圖把影響問題的因素縮小再縮小,以期能夠迅速找到問題的原因.
比如某次提交代碼之後,修改了A/B/C三處地方,導致了原來沒有的BUG出現,如果沒有頭緒,那麼嘗試先把A/B兩處回滾,看看僅有C的情況如何,如果還是沒有重現,繼續加上某處修改.
再或者,某個問題不確定什麼情況下出現,假設輸入範圍在[0,100]時會出現,那麼首先嘗試[50,100],不行再嘗試另一半區間,以此類推.原來我那種專門用在linux系統的調試方法叫二分法,linux真是讓人愛,讓人恨,給你足夠的自由度,但同時也不會告訴你哪裡出錯了,就算越界溢出也不會馬上崩潰,而是到函數返回的時候才崩,崩的原因還不一定是因為函數內部出錯,所以當從上而下的調試已經解決不了問題的時候,我就用了大範圍屏蔽,然後逐步縮小屏蔽範圍,最後定為出BUG的地方,我就不說當找出BUG又把它解決的感覺有多麼的爽了
推薦閱讀:
※zipline和rqalpha對比?
※面向對象編程為什麼沒有在科學計算領域獲得普及?
※學 C 語言時,有沒有遇到過讓你「痛不欲生」、「揪心」或「不得要領」的術語?現在又是怎麼理解它的?
※寫程序需要編譯器,編譯器是程序,輸入輸出也需要驅動,驅動也是程序,那麼第一個在電子計算機運行的程序是怎麼產生的?
※Scheme語言的優勢?