你有哪些解決bug的技巧?

常見的解決bug的方法有:

1)調試;

2)排除法;

3)列印。

你還有哪些解決bug的技巧,能夠快速定位並解決問題?


誒,被邀請了是不是應該先答一句:謝邀思密達。

咳咳,這部分裝完了,盡量撤點乾貨吧。

1.不聊語言的話,從debug方法來看。

2.具體到語言,聊聊debug這些東西。

-------------------------------------------------------

不聊語言的話,從debug方法來看:

1.printf大法好。

2.斷點調試也不錯的。

從bug粗線的問題原因來看:

1.編譯層的問題。

其實就是語法,語義的問題。

比如類型轉換搞錯了,比如你把一堆非全數字的String值轉成int,分分鐘跪掉。

其實這個是最簡單的bug,這裡最多的就常見的就是空指針了。

2.邏輯層的問題。

邏輯層問題就是你代碼語法語義都對,但因為你個人邏輯錯了或者對代碼理解不清,導致的錯誤,但是這裡問題就多了,而且還容易各種debug崩潰。

比如這種的。

你會發現尼瑪怎麼flag分分鐘都是true。那你少寫了一個else能不出bug么?

還比如這種的。

你會發現尼瑪怎麼數據就是對不上!那你沒整明白in.readline()的用法能不出bug么?

還比如這種的。

你從PHP伺服器上抓時間戳數據。在Android上直接轉換。

你會發現尼瑪數據就是不對,那PHP時間戳轉化到java需要補全後三位這事你不知道能不出bug么?

下面具體聊聊debug這些東西。樓主分類提到了前端開發跟Android開發,那就聊聊這倆吧。

--------------------------------------------------------

1.善用各種工具。

比如Android開發的printf就是LogCat。最開心看到紅色了好么!因為出來這個基本IDE已經幫你定位好了。

前端開發就依瀏覽器不同效果不一了,其實都是用開發者工具調試。我比較喜歡用chrome。分分鐘找錯誤。

-------

一年前的答案了。最近準備更新下。先mark下


硬看,就是盯著代碼看,讓代碼在腦子裡面執行。

------

說明一下,這裡主要指的是工具幫不上忙和已經通過工具得到一些基本信息之後的情況,我想有相關經驗的朋友肯定明白是怎麼回事。我就不說一些系統性的方法了,比方用自動化測試在 revision 裡面折半重現,困難的 bug 一般沒有這麼好的條件去搞。

推薦一個日本人的書《Debug Hacks》,中國人裡面似乎有個非常有名的捉蟲能手,據說也出了書,請知道的知友指教。


小黃鴨debug法呀!買一隻小黃鴨,like this:

Sorry,mistaken……這個買不起

其實是這樣的:

右上角右下角不知道是什麼東西忽略她。。。

然後每次遇到bug的時候!!!!

關鍵的來了!!!!

。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。

。。。。。。。。。。。。。。。。。。。。。。。

把整個代碼的邏輯講給小黃鴨聽……然後你就發現錯誤了。

:)

不要打我,親測好使


如果是一些運行時異常的話,分析 Log 輸出的 Exception 會很有幫助,但是如果是業務上面的 Bug,這就需要盯著代碼一步步的在腦子中執行業務了。當然還有最恐怖的一種,數據異常(比如一個 int i 被修改成其他的值),只能 Debug 一步步的調試每一項數據了。


第一步,最好是加一個能重現問題的單元測試,然後才想怎樣修正。這樣也確保同一問題再將來能被自動發現。


調試的技巧有很多,我貢獻一些:

  1. 單步跟蹤,這個大家都會,也有一些高級技巧,比如設置下一個執行點(Set Next Statement)。有時候你單步跟蹤錯過了一個地方,重新再來很麻煩,就可以用這個功能,把執行點重新設回去。這個技巧在解決一些不好重現的問題比較有幫助。VC、XCode等IDE都有這個功能。
  2. 條件斷點,比如判斷某個變數等於某個值的時候才斷下來。
  3. 直接修改變數值或者內存數據。有時候在某些變數為特殊值時會出bug,但是條件不好滿足,可以直接改。
  4. Attach進程。
  5. 打log,這個大家也都會。很多時候單步調試不方便,又不知道問題出在哪裡,可以多打一些log。有時候出問題的地方不好確定,範圍又太廣,沒辦法到戶打log,可以用二分法來,通過幾次迭代迅速縮小範圍。有時候打log會嚴重影響性能,導致功能不能正常運行,可以寫個高性能的內存log模塊來調試。比如我以前調試網卡驅動,中斷觸發非常頻繁,不可能通過單步調試或者普通的log查問題,於是寫了一個微秒級的內存log模塊,解決了很多問題。
  6. log輸出到串口。這個嵌入式開發中基本上必備
  7. 點LED。在嵌入式開發中,有時候log沒法輸出到串口,比如在bootloader中,串口還沒初始化,或者快下電的時候串口不可用了,基本上只能靠點LED。
  8. 示波器、邏輯分析儀。嵌入式開發中有的時候不是你的程序邏輯有問題,而是時序控制不對或者匯流排有干擾,需要用專用設備看。
  9. 反彙編。C/C++代碼中往往有很多宏開關,有時候一時半會兒搞不清楚哪些代碼是有效的,可以用IDA Pro反彙編,直接對著彙編代碼看。
  10. 直接通過十六進位編輯器修改彙編指令。有時候修改源代碼再編譯打包很不方便,可以直接改編譯出來的程序。
  11. 遠程調試,有時候程序只有在特定的設備上運行才出現bug,本機沒法復現。可以用這招。
  12. 打點。做互聯網開發,很多時候你沒法在問題的第一現場,可以上傳一些重要數據到服務端,做在線分析。
  13. crash自動捕捉、上報。可以用一些崩潰工具比如breadpad、bugly、bugtrap等自動捕捉崩潰,通過分析崩潰信息定位問題。

總之,調試其實沒有一定之規,想像力很重要。


Debug 的過程實際就是定位 bug、解決 bug 的過程,但是實際中遇到的 bug 各種各樣,五花八門,例如有編譯錯誤,軟體本身運行時異常,軟體輸出錯誤,還有涉及到其他模塊,比如硬體的錯誤,另外可能還會有隨機錯誤,軟體本身也不同,所以 debug 根本沒有固定而通用,一招走遍天下的方法。

想要排除錯誤,找到 bug, 只能根據平時積攢的一些經驗,從頂至下,慢慢一點點的摸索和排除。

但是還是有一些常見的方法可供參考。

這裡我總結了一下我工作中所用到的一些方法,介紹給大家。

1. IDE 調試

最常用的 ID E 就是 Visual Studio , 它的 debug 功能實際上非常強大,很羨慕那些用 VS 工作的小夥伴。

首先,它可以加斷點,單步調試。

通過單步調試,代碼邏輯,執行順序,以及各種中間結果一目了然。單步調試對於那種代碼本身出錯的 bug, 很容易定位解決。

其次,斷點下的很多分析工具。

比如 watch 窗口看各種變數,結構體等;通過 memory 看內存; 還可以看堆棧狀態;線程狀態等等。

VS debug 功能不止這些,還有很多很多。

2. 列印 log

Linux 下沒有好用的 ID E 怎麼辦? gdb 有的時候也不容易觀察像大的結構體這種變數值,那就列印 log 吧。

列印 log 幾乎是所有程序員都經常用的 debug 方法。

把有 N 多變數的結構體值列印到文件里,會比用 gdb 方便許多。

另外,通過 log 可以很容易看到函數的執行順序。

3. 列印中間結果

列印內容不僅限於結構體變數,還有各種中間結果,臨時數據,例如視頻編碼過程中的 MV, DCT 結果,熵編碼結果,重建數據等等。

4. 將錯誤結果和標準 (golden) 結果進行比對

假如有正確的輸出結果可供參考,可以拿來對比。 通過用 Beyond Compare 比較 十六進位碼,很容易定位到錯誤在哪裡,錯了哪些位元組。

雖然這不能立刻讓你發現代碼中的錯誤,但是通過輸出結果中的錯誤,會讓你對 bug 本身有大體的了解,這樣可以縮小排查範圍。

最後還是想說一句,實際情況往往會很複雜, bug 也是各種各樣,最終還是要隨機應變,結合實際情況進行 debug。

Debug 過程很痛苦,有時一個 bug 往往需要耗費一周甚至更長的時間,但是 Fix 了一個 bug 的那種快感也是不言而喻的。


我說的可能不是debug,主要是生產環境出問題時的查錯。

首先,開發的時候就做好準備。

包括但不限於:

好好記錄日誌。一定要記錄方法的入參、出參、異常信息。

準備好日誌下載、分析的工具。必要時候日誌拆分、分析腳本要能寫出來。

前兩天上線時,不停的收到報錯郵件。而且郵件里發送來的errmsg居然是null。

當時的debug的關鍵三步,第一是日誌下載。生產環境有比較完善的日誌機制,我把報錯前一個小時的都扒了下來。第二是日誌分析,對比日誌流,發現第x行日誌列印之後,第x+n行沒有列印。於是很清楚的確定,問題就在這n行之內。最後再查看第x行記錄的入參數據——入參是個javabean,日誌里把其中所有非null的數據都記錄了下來。於是很清楚的看到「feeExp=無」。

從日誌下載到找到「無」,前後不超過10分鐘。

然後我讓系統管理員修改了對應的配置數據,都沒驗證結果就陪媳婦看電影去了。

沒然後了,那之後直到現在,只發了一封錯誤郵件。後面說。

生產環境查錯,好的日誌真的是關鍵。曾經翻一份10G+的日誌,滿篇是「xx方法開始執行」「xx方法執行完畢」,什麼數據都沒有,真的有提刀砍人的心。

小tip,用log4j記錄異常日誌的時候,一定要用logger.error("error message in string",exception)這個重載方法。這個方法會把exception的異常信息堆棧一股腦列印出來。雖然不太友好,但是猴哥們應該都知道它的便利性。

在此基礎上,如果對系統業務、代碼流程熟悉,可以走很大一條捷徑。感謝那些好好設計代碼結構的人。這樣,出問題的時候,有時即使沒有日誌或者不看日誌,也可以很輕鬆的就把問題鎖定在三、四行代碼裡面。

今天上午收到了另一封報錯郵件。這次有errmsg了,是「Integer can not cast into Double」。

因為是我自己寫的代碼,我對流程非常熟悉,並且我清楚的知道只有一個地方可能產生Integer強轉Double——雖然不好意思承認,但確實是開發和測試時考慮不周全導致的。於是再次找管理員確認了一個配置數據,讓她把一個「5」改成了「5.00」。再然後,我去寫別的代碼了。

如果上面的步驟沒法查出並解決問題,那麼,你得能夠重現錯誤。

這時候一個UAT環境的重要性就凸顯出來了。在上家工作的時候,由於是接手別人做了一半的系統,很多時候沒日誌可查(查了也只會有提刀砍人的衝動)。因此,大多數時候用戶報告bug時,我們第一步都是去UAT環境上設法重現。UAT上是開了log4j的debug日誌的(生產上考慮硬碟空間和性能原因,最多開到info,有的甚至只有error),有用的信息總比生產上要多一些。而且=。=偶爾出現UAT無法重現的問題,我們會以此為借口告訴用戶改不了……

UAT、INT、最後是到自己開發環境上重現,然後修改。

運氣好的話,在重現過程中你能看到出錯時列印出來的異常堆棧信息。由於沒有生產環境那麼大的系統壓力,這時候就可以細細地慢慢地美美地……咀嚼一下這些信息了。

我以前一直覺得看異常堆棧很簡單,以至於跟同事急過眼,得找機會跟人家道歉去=。=剛才嘗試整理看堆棧所需的能力(科技樹?),發現能輕鬆看懂堆棧信息的猴哥,絕對不是一個簡單的機槍兵或者大G。第一得看得懂英語,或者能藉助各類詞典看懂那段英語;其次要懂異常機制,得從那一堆的at……和cause by ……中找到需要自己去關注、或者說自己有能力去關注的點;而且常常還得懂一些編譯啊classloader啊jvm啊伺服器啊相關的東西;好記性或者爛筆頭是個優勢,因為好多問題會重複出現;此外如果能夠擁有腳踏實地的想像力能獲得額外加成,因為有些異常信息真的太匪夷所思了。

上上次上線時候,我就遇到了這麼個匪夷所思的問題。需要上線的代碼在INT環境上跑的非常順溜,上線的時候報了個NoClassDefFoundError。嘛……英語很好懂,no found的那個類也很清楚的跟在error的後面,但是……INT上真的非常順溜啊……

這時,如果對系統業務、代碼流程熟悉,又可以走很大一條捷徑。很多次跟同事一起查錯,他們還沒看完日誌我就猜到問題所在了,基本就是因為我走了這條捷徑。

總的來說,我查bug,基本上是依賴程序在運行中留下的信息。大多數是我自己設置的「打卡機」,程序運行到這裡就得打一次卡。有些是系統提供的幫助。當然會犧牲一些空間和時間的性能。不過目前來看,大多數是值得的。何況這些數據還可以拿來做點性能分析,甚至大數據分析呢。

藉助這些信息,加上自己對系統、代碼的掌握,大多數bug都能很迅速地定位、並最終解決。

學計算機的第一天老師就教我,「軟體=數據+流程」。我到現在才算有了點切身的體會。日誌里記錄的是動態的數據,代碼記錄的是靜態的流程。二者到手,bug無憂。

debug這個工具,我在追查生產環境的bug時很少使用。真的,大多數情況下,日誌+代碼就足夠了。

我只有在開發期間,調試一些複雜的演算法,或者調試大批量數據時會用。複雜演算法是因為我人腦性能低,對代碼流程分析不過來。大數據量則是沒法對各種數據狀態一一去分析。幸運——或者不行——的是,我基本沒遇到過多少需要debug的情況。認認真真debug的事兒,做的應該不超過十次。

想了想,多線程並發也許是個不好用日誌+代碼來分析的東西。不過……我寫的多線程……還沒查過錯……

哈哈一股濃濃的優越感是不是……不過說真的,代碼較少出生產bug+出了bug能很快排查並解決,作為一隻猴子我真的挺有成就感的。


不請自來

其實我是個寫代碼的

---------------

1.復現問題

2.列印大法好

3.readelf好

4.objdump好

5.gdb好

下面,沒了

我一般不調試,不上gdb,多線程環境,調試很難解決問題,必須看日誌,看現場


找出重現方法,加上列印日誌。


常規的:代入法,控制變數法,斷點調試大法,stackoverflow大法,列印大法等等等等............

但是最重要的還是睡一覺,起床了再看大法........。有時候腦子就容易一下子陷入死胡同,興許換個時間段打開方式都不一樣了呢!


該bug是否重現?

上次正常之後,有什麼新的調整?

列印法;

置換法,一塊一塊的換;


上次調了好久BUG調不出來看了一個小時黃旭東視頻之後10分鐘就調出來了。。。

感覺每次啟動DEBUG模式時都會有新感覺。。。

傻傻的做動態調試不如多花些時間在靜態分析上。。。

想想錯誤產生的位置什麼的。。。


最近在玩無鎖並發,gdb都不好使,只能運行的時候收集一個非常簡略的信息,最後一起printf。


頻繁的提交到代碼倉庫,dailybuild。出現bug時,拿舊版驗證下,快速找出哪個版本引入的 bug。然後查下版本差異,基本上 bug立刻顯現。

以上對自己能維護的代碼有用。如果是第三方的代碼,需要結合列印,排除,重現規律,合理猜想求證。

另外如果是mcu編程,模擬器單步跑一遍,比什麼都管用,不要太相信直覺。


1.斷點

2.打log

3.查dump

4.問以下其他人有沒有遇到過類似的

5.請測試幫忙找規律

6.自己開腦洞找規律

7.把項目移交給別人


經驗告訴我,如果當bug出現,那麼有問題的地方往往不止一處。

所以修改完後記得多留個心眼


在軟體產品的運營維護階段,軟體工程師的一項重要工作就是解決軟體的Bug。在學校的時候,大家學完一門課程,然後考試通過就萬事大吉了。但在實際的軟體開發項目中,將軟體成功交付給客戶,只是「萬里長征走完了第一步」,後面還有大量的工作要做,例如:解決軟體故障、新增功能、版本升級等。作為一名合格的軟體工程師,一定要學會準確、迅速地定位並解決軟體出現的各種問題。

為什麼解決軟體問題的能力如此重要?因為軟體項目的成功率不容樂觀。大部分的軟體項目都會出現超期完成甚至是失敗的情況。既然出了問題,那麼就要及時將之解決掉。

軟體故障的分類

根據軟體故障的嚴重程度,將其分為以下三類:

1.嚴重故障

這類故障一般會導致軟體產品無法正常使用,需要立即解決。

如果出現了此類問題,那麼軟體工程師就要放下手中的工作,全力以赴將問題處理掉。

2.一般故障

這類故障雖然不會導致軟體產品無法正常使用,但會影響某些功能流程,會影響到用戶的體驗。

如果出現了此類問題,那麼軟體工程師在手中任務不緊張的情況下,需要抽時間來處理掉,不能讓問題一直遺留下去。

3.輕微故障

這類故障幾乎不會對軟體產品產生不良影響,用戶也很少能夠感覺得到故障的存在。

對於追求高質量和良好用戶口碑的公司,在後續產品功能升級的時候,會附帶將該類問題一起處理掉。

請參考:如何解決軟體故障?


單元測試+斷言;

可視化debug;

二分法debug;


控制變數法,數學歸納法。


推薦閱讀:

一個獨立遊戲製作人需要哪些知識?需要應用哪些工具?
24歲了,該怎樣選擇接下來的編程之路?
你為什麼想打產品經理?
三年經驗的軟體工程師和十年經驗的軟體工程師有什麼本質的差別?

TAG:程序員 | 軟體開發 | 編程 | 軟體調試 |