自底向上程式碼除錯技巧
原創:打碼日記(微信公眾號ID:codelogs),歡迎分享,轉載請保留出處。
簡介
我們常使用IDE的除錯功能解決程式問題,但很多同學用的是自上而下除錯法,即找到一個程式碼入口,打上斷點然後單步除錯。
但一些特殊的除錯場景,比如除錯框架程式碼,在不太熟悉框架程式碼的情況,會因為不知道從哪個入口開始除錯而感到迷茫,這時可以試試本文介紹的方法。
例子
比如,我們寫了一個介面,在獲取引數的時候亂碼瞭如下:
如何快速定位這個問題呢?
眾所周知,亂碼基本都是因為請求方與服務方字符集配置不一致產生的,比如請求方使用UTF-8,服務方使用GBK。
另外,既然上述介面中引數定義的是String型別,那麼spring或tomcat框架中一定有個地方將網路請求中的位元組流資料轉換成這個String物件。
那麼就可以這樣,我們在String類的所有構造方法中加入條件斷點,加斷點後在斷點處點滑鼠右鍵即可新增條件,比如new String().contains("abcdefg")
abcdefg
,這樣當框架中new出包含abcdefg
的String物件時,我們的條件斷點就會命中,如下:在String中新增條件斷點後,我們重啟應用,發如下請求來觸發斷點:
curl http://192.168.0.103:8080/test?data=abcdefg
命中了條件斷點,如下:
我們往呼叫棧上面看,發現是handleQueryParameters
中建立字串時使用的GBK來new出String,而GBK來自queryStringCharset
屬性,如下:
那是哪裡配置成了GBK
呢?我們再在設定queryStringCharset
屬性的地方,即setQueryStringCharset
我們再往呼叫棧上面看,發現setQueryStringCharset
的引數來自connector.getURICharset()
,如下:
同理,我們在賦值connector.setURICharset
的地方打上斷點,重啟應用,發現在重啟的過程中就命中了斷點,如下:
我們再往呼叫棧上面看,發現connector.setURICharset
的引數來自this.getUriEncoding()
方法,而this是TomcatServletWebServerFactory
型別
同理,我們在TomcatServletWebServerFactory.setUriEncoding
我們再往呼叫棧上面看,在to(Consumer<T> consumer)
方法中發現uriEncoding來自this.supplier.get()
,而this.supplier.get()
取的應該就是ServerProperties$Tomcat
物件中的urlEncoding屬性
同理,我們在它的setter方法ServerProperties$Tomcat.setUriEncoding
裡面加上斷點,重啟應用,如下:
我們再往呼叫棧上面看,發現uriEncoding是從JavaBeanBinder.bind
方法設定進來,從呼叫方法命名上不難看出,這個方法應該就是將配置檔案中的值set到配置類屬性中去的,當前被配置的類是ServerProperties$Tomcat
,而正在配置的屬性是uri-encoding
,如下:
我們在此處瞄下各變數與引數的組成,很快就在BeanPropertyBinder
引數中找到,uriEncoding來自application-web.yml
檔案的第4行第19列,如下:
果真,在application-web.yml
中第4行,配置了uri-encoding: GBK
,如下:
在將其改成UTF-8
之後,發現亂碼就不存在了,如下:
ok,通過這個例子,相信你已經體會到自底向上除錯方法的訣竅了。
總結
上面這個例子其實可以有更快處理方法,比如在當前工程全文搜尋GBK
,又或者google一下springboot
,亂碼
之類的關鍵詞,也能很快解決問題,但思路是很重要的,比如類似下面的場景,也能運用這裡的方法:
- 系統執行時,不知道什麼地方的程式碼老是執行了一條刪除的sql,我們可以和上面一樣,在String裡面打上條件斷點,條件是包含刪除sql的部分片段。
- 系統啟動時,不知道什麼地方的程式碼監聽了12345埠,我們可以在
ServerSocket
裡面加上條件斷點,條件是port==12345
。 - 系統執行時,不知道什麼地方的程式碼老是寫
/tmp/app.log
檔案,我們可以在FileOutputStream.write
方法加上條件斷點,條件是File=="/tmp/app.log"
。
往期內容
真正理解可重複讀事務隔離級別
Linux文字命令技巧(下)
Linux文字命令技巧(上)
原來awk真是神器啊
常用網路命令總結