解bug的幾種思路

ahuang發表於2019-03-13

不完美

人無完人,人寫的程式自然免不了會產生各種各樣的bug。
它就像空氣一樣,包圍在我們周圍,解bug可以說是每個程式設計師的家常便飯。

解bug的過程

一般可以分為3步:

  1. 復現問題。如果問題能穩定重現,比較好說。對於不好復現的問題,筆者一般做法是在可疑程式碼裡新增一些輔助日誌,然後將這些輔助日誌上線,等問題下次再次出現時,就可以通過這些新增的日誌推測原因。(如果日誌太多,刷的太快,日誌裡面可以加上一些統一的關鍵字,這樣可以通過grep檢視自己關心的日誌)
  2. 定位問題。定位bug問題過程中,會像偵探一樣,仔細尋找各種蛛絲馬跡。這個過程很關鍵,如果能很快的找到原因,那麼解決問題就能得心應手了。
  3. 解決問題。方法多種多樣,可以自己尋找方案,可以網上查閱,或者找他人討論等。

一般到這裡,就算是解決了這個問題。不妨在這個時候總結一下,在定位和解決這個問題的過程是如何的?筆者最近開始留意所經歷大大小小的bug,從中總結了一些通用的解決模式,這樣以後如果一條路走不通,可以換下思路,以便能夠更快的定位和解決問題。

下面主要通過一些真實有趣的例子進行闡述,有些例子是開發過程中遇到的問題或者需求,不一定是bug,但筆者認為可以作為一種解bug的思路作為參考。

思路1-斷點除錯

以前端為例,可以在瀏覽器裡設定斷點除錯,
node.js的專案可以在vscode裡進行除錯,
其他語言應該都有各自的除錯方案。

斷點除錯或者新增輔助日誌是最本能的一種方式了。

思路2-縮小範圍

刪除法縮小範圍

好幾次遇到css樣式有問題,但是不知道到底是哪裡的css產生的問題。

舉個例子:
我們的頁面使用了第三方ui庫iview的table元件,這個元件給table自帶了border樣式,它的樣子是這樣的:

解bug的幾種思路
使用這個table元件有個問題是,border自帶的顏色和視覺稿的顏色不一樣,比如視覺稿的樣子可能是這樣的:
解bug的幾種思路
於是希望通過覆蓋的方式改成視覺稿的樣子,一般做法是先找到這個自帶的border在哪設定的,然後修改舊的css樣式去覆蓋它。

一開始的做法是每層元素找啊找,是哪個元素做了設定呢?由於border可能設定在很多元素上,比如div,table,thead,tbody,th,tr,td等等元素上,找了很久沒找到設定的地方....

後來換了種方法找,在元素上右鍵"Delete Element",依次對可疑元素進行刪除。如果哪個元素被刪除之後,border沒有了,那麼就說明border設定肯定是在這個元素上設定的。 通過這樣排查後,最後發現這個border被設定在了2個地方。

border-top和border-left設定在table最外層的div上;
border-bottom和border-right設定在table某個外層的before和after偽類上。

找到了這些border設定的地方,就很好對其進行樣式覆蓋啦。

解bug的幾種思路
解bug的幾種思路
解bug的幾種思路

二分查詢縮小範圍

如果某個問題是最近有次程式碼提交引入的,但不知是哪次提交引入。比如,團隊有人說,"這個問題,之前都沒有的,最近才出現...",可以考慮這個辦法。

  1. 假設有問題的最新提交是commit1,
  2. 找到最近一次沒有問題的提交點,假設為commit2,
  3. 那麼導致問題的應該是commit1和commit2之間的某次提交,可以用二分查詢辦法,查詢這2個點之間的有問題的提交。
  4. 最後根據檢視這次提交的內容,推測問題原因

曾經嘗試過一次,使用這個辦法的前提是,需要確定哪次commit是正常的,這個是關鍵。

思路3-對比法

前端有個特點,同一個功能,經常可以有多種方式去實現,這個特點就為我們提供了一個很好解決問題的思路。
對比法是指,如果方法1有問題,那麼使用方法2,如果方法2可以實現和方法1相同的功能,這樣通過對比一下這2個方法的區別,從而找到方法1的問題所在。

舉個例子:

前後端分離的專案,前端開發環境通常會通過webpack-dev-server,設定proxy和後端介面進行聯通。

有次遇到個問題是,前端程式碼通過axios庫發請求到proxy對應的後端介面,可是介面請求返回顯示500,前端也沒有報錯資訊。但是筆者通過postman直接調後端介面,返回正常的。
這說明介面是OK的,這就很詭異了,直接的辦法當然找後端開發看下後臺日誌,查一下是什麼導致的500,可當時後端開發不在,於是自己嘗試換了js原生的fetch介面發請求,更詭異的是fetch發請求成功了!

解bug的幾種思路
解bug的幾種思路
發現這個結果後,就開始對比通過這2個方式傳送的請求有什麼不同。在對比請求header時,發現2者的cookie不一樣。然後手動清除cookie之後,重新用axios請求就正常了。

思路4-搜尋關鍵字

搜尋工具是個我們的好夥伴,如何搜尋卻是一門學問。如果搜尋關鍵字選的好,有些問題都能快速找到解決方案,也有些問題,需要反反覆覆修改關鍵字,才能找到解決方法。

我在使用hightcharts繪曲線圖時,需要在series曲線點上hover時展示一個豐富資料的table和截圖,如下圖所示:

解bug的幾種思路
為了實現這個功能,我在series上除了曲線xy軸需要的資料,還額外加了hover需要展示的資料,這樣hover時就可以通過fomatter方法拿到這個點points上額外的資料,從而繪製tooltip。 但是好景不長,在我擴大x軸觀察範圍時,hover展示的各項資料都是為undefined!!!

觀察到的情況如下:

  • 當選擇時間區間較短時,小於8分鐘左右,大概400多個點,能獲取到point資料所有資料,包括x,y以及這個點上其他自定義的資料
  • 當選擇時間區間較長時,大於8分鐘左右,能獲取到point資料,但只能獲取到x,y的值,無法獲取到這個點上其他自定義的資料。

大於8分鐘的效果如下:

解bug的幾種思路

於是開始搜尋尋找問題所在和解決方案,嘗試過以下各種關鍵字:

  • highcharts width limit tooltip formatter points
  • highcharts 寬度 資料拿不到
  • highcharts pointer undefined
  • highcharts large data pointer undefined
  • highcharts large number of points(data)

這些都沒有辦法,在這個過程中,又觀察到1個現象,如果把highcharts容器寬度width設大一些,就可以獲取大於8分鐘時的hover資料,於是曾一度冒出偷懶的想法,希望能改下互動來解決這個問題,但是本著使用者體驗至上的想法,不能因為技術原因讓使用者妥協,還是繼續查查吧。

皇天不負有心人,終於在官方issues裡,通過下面這個搜尋關鍵字下,總算找到了問題的答案!

  • highcharts points count less than data length

這個是highcharts官方issue裡的一個問題 檢視地址

解bug的幾種思路
解bug的幾種思路

根據highcharts開發人員的回答,Highstock(這是highcharts下的產品之一,筆者繪圖也確實是使用的highstock,但如果用hightchats搜尋應該更好)中有dataGrouping特性,預設是啟用的,這個特性將指定數量的資料合併展現為一個點,顯示的值根據不同的圖表型別有所不同。所以可能是因為不同的點的資料被合併了,才導致hover時,展示的是undefined。

因此我們對應的解決方案就很簡單了,直接禁止這個特性就可以了,設定dataGrouping.enable = false,即可關閉這個特性,只需要一個簡單的配置即可解決啦

解bug的幾種思路

在找到答案之前,也考慮過其他解決方案,比如hover時想要的points資料不直接從曲線的points,而是將時間軸的x值作為key,其它需要的資料作為value存入1個變數,hover需要資料時根據拿到x值,從變數中獲取。(由於後面找到最直接的方案,這個方案就未驗證了)

在這個例子中,筆者獲得幾個想法:

  • 去issues裡搜尋。這個適用於工程中使用了某些github上開源專案,如果遇到問題不好解決,優先考慮去github的issues裡搜尋答案,因為這裡有來自開發者最直接的答案。這點在後好幾次我使用其它開源工具時遇到問題時,都是通過搜尋issues快速的成功解決了問題。
  • 如何優化關鍵字,應該有很多方法。筆者未做深入研究,一點淺顯理解是,英語要學好,多嘗試修改問題描述,或者根據上次搜尋結果,參考新的關鍵字,總能找到一些蛛絲馬跡的。

思路5-檢視原始碼

對於有些網上也不好搜到的問題,有的時候,順著報錯資訊去檢視原始碼,也不失為一種好的思路。

比如我在使用element-ui樹元件時,使用了懶載入模式,其中有個問題就是,需要在最外層插入一個節點,天真的認為呼叫它的一些已有的insert/append類似API應該可以做到。比如在1個已有節點下加入了子節點,沒啥毛病,然後期望用同樣的想法實現最外層新增節點,嘗試如下:

解bug的幾種思路
解bug的幾種思路
解bug的幾種思路

issues上有人提過這個問題,開發者的回答是無計劃支援這個需求 地址

看著新增根節點時控制檯的報錯資訊,要不就進去看看原始碼如何寫的吧。於是找到報錯入口,進入原始碼,大概研讀原始碼,希望能找到一些希望。

解bug的幾種思路
解bug的幾種思路
解bug的幾種思路

從原始碼分析來看,報錯的直接原因是root節點的data為undefined導致。為了驗證這個,再次在斷點除錯時,檢視了節點data,和判斷一致。

解bug的幾種思路
解bug的幾種思路

檢視root資訊,發現root下有2個childnodes,但是data卻為空

解bug的幾種思路

於是筆者想到的解決辦法是給root節點新增一個data,然後將需要新增的節點呼叫insertBefore加入,這樣介面就不會報data undefined的錯誤了。

解bug的幾種思路

解決了這個問題,API就能順利的在樹的最外層新增了新節點。

解bug的幾種思路

想必很多人也經歷過這些,如果原始碼比較多,可以看下關鍵的地方,另外邊除錯邊看,效率會更高。

總結

以上便是我總結的幾個思路,相信在今後的工作中,可能還有更多的更廣闊的思路。 筆者水平有限,歡迎多多交流和指正,謝謝。

相關文章