程序開發,很大一部分的時間是用於調試,調試也是一門值得了解的學科,我這總結一般調試手段:
- 加日誌
- 加斷言
- 代碼調試
- 調試core文件
加日誌
這個就無需贅言了,你懷疑哪個地方,或者在出現問題的時候,需要知道哪個地方有問題,那麼日誌肯定是少不了的,而且這個也是最基本的,適用於所有語言。
但是經驗來看,加日誌屬於事後,或者是預判,不是很靈活,因爲總有你沒想到的地方,所以我們發現,做程序開發,在排查線上問題的時候,總會抱怨其他程序員沒有加日誌。關鍵還是在於計劃趕不上變化。
日誌是必須的,但是隻靠日誌,在某些場景還是沒法解決問題的:
- 你不知道哪個地方出了問題,你只能在懷疑的地方又加日誌
- 每次加日誌,你肯定得改代碼,如果是編譯型語言你還得重新打包,慢
- 加了日誌之後,發現打印出的信息還是太少,返回去又搞一遍,返工
加斷言
這個在 c 語言開發裏經常看見,通常是你認爲絕對的一段代碼邏輯,直接斷言,或者在排查問題的時候,懷疑某一個地方,那麼加一個斷言,這個本質邏輯就是:如果代碼走到我懷疑的地方,那麼就證明了我某些推論,斷言之後,還可以留下內存 core 文件,以便調試。
調試core文件
這個是c/c++程序比較常見的一種調試方式,c 程序一般線上都會開 core 開關,到跑到異常的時候,整個程序內存鏡像 dump 出來,相當於保存了現場。這個有時候也是排查 c 程序運行唯一手段。
golang程序經常用這種方式,排查一些問題。golang 有兩個調試工具:
- gdb
- dlv
gdb 能調試 golang 程序的,但是不好調試,有幾個原因:
- 關鍵在於golang的協程是用戶態的執行單元,gdb 調試的是系統的執行題,進程,線程,所以根本很難調試到指定的協程。
- 協程數太多,這個也是最尷尬的地方,生產環境的 golang 程序動不動就成千上萬個 goroutine,你列都列不完。
- gdb 對 golang 程序的數據結構理解不完善。打印變量經常打印不出來。
但是 gdb 有自己的獨一無二的特點:
- 可以 gcore 文件,這個 dlv 就做不到,所以我們經常用 gdb 來 core 文件,用 dlv 來分析 core
- x 命令分析內存更透測
代碼調試
代碼調試無論那種語言,都是很重要的一個環節。要能夠離線的對二進制或者在線的對進程進行調試,設置斷點嘍,打印一些程序變量嘍,這些都是無侵入式的,不需要改代碼即可。
- 比如 c/c++ 可以使用 gdb 進行單步調試,或者 attach 到已經在運行的進程裏進行調試。
- python 可以使用 pdb 進行單步調試
- golang 可以 gdb 和 dlv 進行離線和在線調試
golang 調試
golang 調試有兩個工具:
- gdb
- dlv
都支持的三種調試場景:
- 二進制文件
- 在線進程
- core 文件
gdb
# 調試二進制文件
gdb --args {二進制文件} {參數}
# 調試在線進程
gdb {二進制文件} -p {pid}
# 調試 core 文件
gdb {二進制文件} {core 文件}
dlv
# 調試二進制文件
dlv exec {二進制文件} -- {參數}
# 調試在線進程
dlv attach {二進制文件} {pid}
# 調試 core 文件
dlv core {二進制文件} {core 文件}
python 調試
python的調試有兩個手段:
- 侵入式
- 非侵入式
侵入式:
#!/usr/bin/env python
def test_print():
# 斷點位置
import pdb; pdb.set_trace()
print("test print")
if __name__ == "__main__":
test_print()
非侵入式:
python -m pdb test.py
後面抽個時間分享下 gdb,dlv怎麼調試golang程序的,敬請期待。
堅持思考,方向比努力更重要。關注我:奇伢雲存儲