Go 語言源碼級調試器 Delve
01? 介紹
Delve 是一個簡單、強大和易用的 Go 語言源代碼層級的調試器,也是 Go 官方推薦使用的調試器。
02? 安裝
Delve 安裝非常簡單,如果讀者朋友使用的是 Go 1.16 或更高版本,可以直接使用?go install
?安裝:
go?install?github.com/go-delve/delve/cmd/dlv@latest
如果讀者朋友們使用的是低于 Go 1.16 的版本,可是先下載 Delve 源碼,然后使用?go install
?安裝:
git?clone?https://github.com/go-delve/delve
cd?delve
go?install?github.com/go-delve/delve/cmd/dlv
安裝完成之后,可以使用?go help install
?查看?dlv
?可執行文件的詳細位置。我建議讀者朋友們將?dlv
可執行文件,配置到 PATH 環境變量。
需要注意的是,如果讀者朋友們使用的是 macOS,還需要安裝命令行開發工具:
xcode-select?--install
為了避免每次使用 dlv 都需要授權允許使用 debugger,建議讀者朋友們開啟開發者模式:
sudo?/usr/sbin/DevToolsSecurity?-enable
03? 實踐
在完成 Part 02 中的所有操作之后,我們使用?dlv version
?檢查?dlv
?可執行程序是否已可以使用。
我們可以使用?dlv
?的任意可用命令啟動一個調式會話,比較常用的命令是?dlv debug
,?dlv exec
?和?dlv test
。限于篇幅,本文我們介紹?dlv debug
?的使用方法。
示例代碼:
package?main
import?(
????"fmt"
)
func?main()?{
????a?:=?1
????b?:=?2
????c?:=?sum(a,?b)
????fmt.Println(c)
}
func?sum(a,?b?int)?int?{
????res?:=??a?+?b
????return?res
}
閱讀上面這段我們將用于調試會話的代碼示例,它包含一個 main 函數和一個 sum 函數,main 函數中定義變量 a 和變量 b,調用 sub 函數,并將返回結果賦值給變量 c,最后打印變量 c 的值。
啟動一個調試會話:
[root@VM-8-14-centos?work]#?dlv?debug
Type?'help'?for?list?of?commands.
(dlv)
閱讀上面這段代碼,我們使用?dlv debug
?啟動一個調試會話,在沒有任何參數的情況下,Delve 編譯并開始調試當前目錄中的?main
?包。
我們也可以指定一個文件名,Delve 將會編譯該指定文件的?main
?包,并啟動一個調試會話。
[root@VM-8-14-centos?work]#?dlv?debug?main.go
Type?'help'?for?list?of?commands.
(dlv)
調試會話啟動后,我們可以使用調試命令進行調試程序。
list 命令:
dlv?debug
Type?'help'?for?list?of?commands.
(dlv)?list?main.main
Showing?/work/main.go:7?(PC:?0x49670a)
?????2:
?????3:?import?(
?????4:??"fmt"
?????5:?)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
(dlv)?list?./main.go:7
Showing?/work/main.go:7?(PC:?0x49670a)
?????2:
?????3:?import?(
?????4:??"fmt"
?????5:?)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
(dlv)
調試會話啟動后,我們可以使用 list 命令列出指定位置的源碼,包含兩種方式,第一種方式是?<package name>.<func name>
,第二種方式是?<file name>:<line number>
。
break 命令:
dlv?debug
Type?'help'?for?list?of?commands.
(dlv)?break?main.main
Breakpoint?1?set?at?0x49670a?for?main.main()?./main.go:7
(dlv)
我們可以使用 break 命令添加斷點,和 list 命令一樣,添加斷點的位置,也可以使用上述兩種方式。
我們可以使用 breakpoints 命令,列出所有斷點,可以使用 clear 命令刪除指定斷點,可以使用 clearall 刪除所有斷定。
continue、next、step、stepout 和 print 命令:
?dlv?debug
Type?'help'?for?list?of?commands.
(dlv)?break?main.main
Breakpoint?1?set?at?0x49670a?for?main.main()?./main.go:7
(dlv)?continue
>?main.main()?./main.go:7?(hits?goroutine(1):1?total:1)?(PC:?0x49670a)
?????2:
?????3:?import?(
?????4:??"fmt"
?????5:?)
?????6:
=>???7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
(dlv)?next
>?main.main()?./main.go:8?(PC:?0x496718)
?????3:?import?(
?????4:??"fmt"
?????5:?)
?????6:
?????7:?func?main()?{
=>???8:??a?:=?1
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
(dlv)?next
>?main.main()?./main.go:9?(PC:?0x496721)
?????4:??"fmt"
?????5:?)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
=>???9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
(dlv)?next
>?main.main()?./main.go:10?(PC:?0x49672a)
?????5:?)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
=>??10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
????15:??res?:=??a?+?b
(dlv)?print?a
1
(dlv)?print?b
2
(dlv)?step
>?main.sum()?./main.go:14?(PC:?0x4967e0)
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
=>??14:?func?sum(a,?b?int)?int?{
????15:??res?:=??a?+?b
????16:??return?res
????17:?}
(dlv)?next
>?main.sum()?./main.go:15?(PC:?0x496800)
????10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
=>??15:??res?:=??a?+?b
????16:??return?res
????17:?}
(dlv)?next
>?main.sum()?./main.go:16?(PC:?0x49680f)
????11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
????15:??res?:=??a?+?b
=>??16:??return?res
????17:?}
(dlv)?next
>?main.main()?./main.go:10?(PC:?0x496739)
Values?returned:
?~r0:?3
?????5:?)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
=>??10:??c?:=?sum(a,?b)
????11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
????15:??res?:=??a?+?b
(dlv)?next
>?main.main()?./main.go:11?(PC:?0x49673e)
?????6:
?????7:?func?main()?{
?????8:??a?:=?1
?????9:??b?:=?2
????10:??c?:=?sum(a,?b)
=>??11:??fmt.Println(c)
????12:?}
????13:
????14:?func?sum(a,?b?int)?int?{
????15:??res?:=??a?+?b
????16:??return?res
(dlv)?print?c
3
(dlv)
閱讀上面這段代碼,我們使用 Delve 添加斷點后,執行 continue 命令,程序將執行到斷點位置;執行 next 命令,程序繼續執行下一行代碼;執行 step 命令,程序步入到調用函數內部;執行 stepout 命令,程序步出到調用函數的調用位置;執行 print 命令,打印指定參數的值。
讀者朋友們使用以上命令,可以滿足大部分調試場景。為了方便理解,以上示例中使用的命令都沒有使用簡寫形式,在實際使用時,使用簡寫形式會更加便捷。
簡寫形式:
-
break(b) -
continue(c) -
next(n) -
step(s) -
stepout(so) -
print(p)
04? 總結
本文我們簡單介紹 Go 語言調試器 Delve 的基本使用方式,讀者朋友們可以在程序調試時將 Delve 使用起來,替換使用 print 打印的形式調試代碼。
關于 Delve 的高級功能,例如調試 goroutines、將調試器附加到現有進程、遠程調試以及從 VSCode 編輯器或 Goland IDE 使用 Delve。感興趣的讀者朋友們可以參考 Delve 的幫助文檔。
原文鏈接:https://mp.weixin.qq.com/s/bEl-z-xiIOf966MR9HdzQA