Go使用dlv除錯程式碼
用 dlv 除錯
那有同學問了,有沒有其他可以除錯 Go、以及和 Go 程式互動的方法呢?其實是有的!這就是我們要介紹的 dlv 除錯工具,目前它對除錯 Go 程式的支援是最好的。
之前沒我怎麼研究它,只會一些非常簡單的命令,這次學會了幾個進階的指令,威力挺大,也進一步加深了對 Go 的理解。
下面我們帶著一個任務來講解 dlv 如何使用。
我們知道,向一個 nil 的 slice append 元素,不會有任何問題。但是向一個 nil 的 map 插入新元素,馬上就會報 panic。這是為什麼呢?又是在哪 panic 呢?
首先寫出讓 map 產生 panic 的示例程式:
package main func main() { var m map[int]int m[1] = 1 }
接著用 go build
命令編譯生成可執行檔案:
go build a.go
然後,使用 dlv 進入除錯狀態:
dlv exec ./a
使用 b
這個命令打斷點,有三種方法:
- b + 地址
- b + 程式碼行數
- b + 函式名
我們要在對 map 賦值的地方加個斷點。先找到程式碼位置:
cat -n main.go
看到:
賦值的地方在第 5 行,加斷點:
(dlv) b a.go:5
Breakpoint 1 set at 0x45e55d for main.main() ./a.go:5
執行 c
命令,直接執行到斷點處:
執行到斷點處
執行 disass
命令,可以看到彙編指令:
disass
這時使用 si
命令,執行單條指令,多次執行 si
,就會執行到 map 賦值函式 mapassign_fast64
:
mapassign_fast64
這時再用單步命令 s
,就會進入判斷 h 的值為 nil 的分支,然後執行 panic
函式:
panic
至此,向 nil 的 map 賦值時,產生 panic 的程式碼就被我們找到了。接著,按圖索驥找到對應 runtime 原始碼的位置,就可以進一步探索了。
除此之外,我們還可以使用 bt
命令看到呼叫棧:
呼叫棧
使用 frame 1
命令可以跳轉到相應位置。這裡 1
對應圖中的 a.go:5
,也就是我們前面打斷點的地方,是不是非常酷炫。
上面這張圖裡我們也能清楚地看到,使用者 goroutine 其實是被 goexit 函式一路呼叫過來的。當用戶 goroutine 執行完畢後,就會回到 goexit 函式做一些收尾工作。當然,這是題外話了。
另外,用 dlv 也能幹第二部分“找到 runtime 原始碼”活。
總結
今天系統地講了幾招通過命令和工具檢視使用者程式碼對應的 runtime 原始碼或者彙編程式碼的方法,非常實用。最後再彙總一下:
- go tool compile
- go tool objdump
- dlv
使用這些命令和工具,可以讓你在看 Go 原始碼的過程中事半功倍。
© 2017-2020 版權屬於 QXQZX &