如何提高 Debug 效率

大家好,我是 Z 哥。

可以不誇張地說,程序員可能有一半的時間都在修 bug。雖說,根據 28 原則大部分 bug 都可以在搜索引擎上搜到(業務性 bug 除外),但是往往剩下的那 20%bug 會花費我們 80% 的時間。

雖然解決這個問題最好的是方式減少產生 bug,但是再怎麼減都不可能減到 0,我們還是有必要提高自己 Debug 的效率。

因爲在 Debug 方面,水平高的人可能在效率上能領先至少一個數量級。我在這個行業從業將近 9 年,我見過太多這樣的案例了。網上也流傳過一個傳播度很高的案例,有一個阿里內部的團隊排查好幾天未果的一個問題,去請教多隆大神,分分鐘就搞定了。

很多人的 Debug 能力之所以長期止步不前,在我看來主要原因是兩個。

不同的編程語言,主流的 IDE 都不同,所以我主要就第二點展開說說我的經驗。

有些經驗的程序員都知道,Debug 的過程最重要的不是怎麼修復 bug,而是怎麼找到產生 bug 的地方。所以,下面要講的思路主要也是圍繞排查 bug 相關。

/01  縮小問題範圍 /

縮小問題範圍的方式有很多,本質上其實是從當時的環境中找到與問題更高相關的變量。最常見的變量主要在以下這些:

建議你先從這幾個變量進行驗證。然後再弄清楚上一次正常操作與當前出現 bug 的操作之間的這段時間發生過什麼。大多數情況下,問題的根源就藏在這裏面。那種潛藏很久才遇到的疑難問題,畢竟是少數。

/02  提煉並優化每一類 bug 的標準處理流程 /

工作中很多流程需要 SOP,在修復 bug 這件事上也可以這麼做,如此可以將每一次修復一個疑難 bug 的過程給沉澱下來。

常見的 bug 類型主要有 4 類:

  1. 輸出與預期不符

  2. 程序報錯

  3. 程序明顯響應慢

  4. 程序 crash

每一類都有適合它們的排查方式,如果你總是用同一個套路去排查這 4 類問題,效率自然不會太高。

01 輸出與預期不符

這種 bug 最頭疼,爲什麼呢?因爲它不像那種異常、報錯的 bug,有堆棧信息,可以快速縮小排查範圍,甚至直接定位到產生的地方。

那麼怎麼辦呢?如果這個問題在測試環境,那麼最簡單,直接單步調試走起,這個時候如果對 IDE 的調試工具掌握得越深入,效率也會越高,比如條件變量、多線程調試等等。

這裏多說一句,強烈建議每個人掌握自己所用 IDE 的條件變量和多線程調試這兩種方法,在當前的大環境下,整個軟件開發領域的大型項目和多線程運用都比幾年前高得多。

如果沒法單步調試的情況,那麼只能通過多打一些日誌,來達到接近單步調試的效果。不過這點需要你做一些預判,在一些代碼分支、可疑位置打上日誌即可,畢竟編寫記錄日誌的代碼也需要時間不是。

02  程****序報錯

這種 bug 對有些經驗的程序員來說是最簡單了,因爲直接告訴了你產生異常的代碼位置。

但是對新手就不同了,很多新手會拿着描述異常的一堆文字去搜索引擎搜,比如(NullPointer Exception),搜出來 N 多文章,一篇一篇看下來並嘗試都發現不能解決自己的問題,其實就是由於自己還沒習慣於去看堆棧信息。因爲別人的 NullPointer Exception 和你的 NullPointer Exception 產生的原因並不一樣。

堆棧信息中記錄了整個調用鏈路,所以通過這裏你可以看到完整的方法調用順序。

不過值得提醒的是,在日常編寫的代碼的時候,千萬不能隨意的 try catch 代碼塊,然後 throw 一個 new exception,因爲這會導致堆棧信息不完整。

03  程序明顯響應慢

這種問題一般是在產生資源競爭,或者資源緊張的時候發生。排查他們的難度也比較高。

如果說前面兩類問題中,高水平和低水平的區別只在於解決效率的高低上,那麼這個問題對低水平的程序員們來說可能是不管花多少時間都找不到問題的原因。

不過不要緊,我建議你以後遇到這種情況,優先從以下這幾個指標入手。

對於 TCP 連接,你身邊得常備一份 netstat 命令手冊,然後敲入命令,分別查看連接數是否接近到了 65535?TIME_WAIT、CLOSE_WAIT 狀態的連接是不是過多?

大多數情況下,TCP 連接相關的問題主要就是兩個:

  1. 連接使用完後未及時釋放

  2. 針對高頻調用未使用長鏈接,而使用了短鏈接。此時一旦下游服務響應慢就會快速打滿 65535 個連接。

對內存問題的分析,主要是就是通過分析 GC 來進行,主要關注是否有什麼類型的對象佔用內存過大了。如果存在過大的情況,主要原因是以下兩個:

  1. 某個大對象應該是共享使用的,在代碼裏不小心寫成了每一個實例各自一份。

  2. 某個對象分配的時候不小心帶上了 static 關鍵字,導致 GC 一直無法回收爲其分配的內存。

不同的編程語音有不同的 GC 分析工具,需要熟練掌握。

對線程的分析,和 TCP 連接類似,主要集中在線程的數量和狀態上。線程並不是數量越多、性能就越好。數量越多,可能花在線程之間的上下文切換上的時候就比實際執行代碼邏輯還要多。

另外,是不是有大量線程 blocked 或者 deadlocked 了?隨便挑其中一個線程,分析其當前的堆棧信息,就是問題所在。

04  程序 crash

導致 crash 的主要原因有兩點。

  1. 是由於前面提到的原因 3 未及時察覺,導致程序運行直到資源耗盡,由操作系統干預強行終止運行。

  2. 代碼中存在未捕獲的 exception。

第一點參照原因 3 的處理方式。

第二點就很簡單了,在代碼的最外層做一個大大的 try catch,然後打上日誌將堆棧信息記錄下來,發佈到線上,自然就能看到是那裏出現的問題,然後轉到原因 2 的處理方式。

最後,提高 Debug 能力必須要多實踐。所以,我是非常建議你在條件允許的情況下,勇敢地去挑起排查線上疑難雜症的任務,甚至並不是你直接負責的功能模塊。

可能在外人看來你在幫別人 “擦屁股”,但這會讓你的 Debug 能力得到明顯的提升,並且容易形成對你的依賴,讓你越來越強。

好了,總結一下。

這篇呢,Z 哥和你分享了我對提高 Debug 效率這件事的看法。

想要提高 Debug 的效率,一是要對 IDE 工具有熟練的瞭解,知道一些高階的使用方式。二是要對 Debug 有一套自己的思路。

對於第二點,我建議的步驟是:

  1. 縮小問題範圍

  2. 提煉並優化每一類 bug 的標準處理流程

Bug 主要分爲 4 類,我在文中給出的思路也區分了這 4 類:

  1. 輸出與預期不符

  2. 程序報錯

  3. 程序明顯響應慢

  4. 程序 crash

希望對你有所幫助。

在我看來,Debug 是一件很好玩,也很有成就感的事情,不亞於設計一個項目的框架。而且 Debug 還能讓你真正地體會到什麼是 “魔鬼藏在細節裏”。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/26Xot4IlaFSR1-GqcUI2oQ