Go 語(yǔ)言源碼級(jí)調(diào)試器 Delve
01? 介紹
Delve 是一個(gè)簡(jiǎn)單、強(qiáng)大和易用的 Go 語(yǔ)言源代碼層級(jí)的調(diào)試器,也是 Go 官方推薦使用的調(diào)試器。
02? 安裝
Delve 安裝非常簡(jiǎn)單,如果讀者朋友使用的是 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
?可執(zhí)行文件的詳細(xì)位置。我建議讀者朋友們將?dlv
可執(zhí)行文件,配置到 PATH 環(huán)境變量。
需要注意的是,如果讀者朋友們使用的是 macOS,還需要安裝命令行開(kāi)發(fā)工具:
xcode-select?--install
為了避免每次使用 dlv 都需要授權(quán)允許使用 debugger,建議讀者朋友們開(kāi)啟開(kāi)發(fā)者模式:
sudo?/usr/sbin/DevToolsSecurity?-enable
03? 實(shí)踐
在完成 Part 02 中的所有操作之后,我們使用?dlv version
?檢查?dlv
?可執(zhí)行程序是否已可以使用。
我們可以使用?dlv
?的任意可用命令啟動(dòng)一個(gè)調(diào)式會(huì)話,比較常用的命令是?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
}
閱讀上面這段我們將用于調(diào)試會(huì)話的代碼示例,它包含一個(gè) main 函數(shù)和一個(gè) sum 函數(shù),main 函數(shù)中定義變量 a 和變量 b,調(diào)用 sub 函數(shù),并將返回結(jié)果賦值給變量 c,最后打印變量 c 的值。
啟動(dòng)一個(gè)調(diào)試會(huì)話:
[root@VM-8-14-centos?work]#?dlv?debug
Type?'help'?for?list?of?commands.
(dlv)
閱讀上面這段代碼,我們使用?dlv debug
?啟動(dòng)一個(gè)調(diào)試會(huì)話,在沒(méi)有任何參數(shù)的情況下,Delve 編譯并開(kāi)始調(diào)試當(dāng)前目錄中的?main
?包。
我們也可以指定一個(gè)文件名,Delve 將會(huì)編譯該指定文件的?main
?包,并啟動(dòng)一個(gè)調(diào)試會(huì)話。
[root@VM-8-14-centos?work]#?dlv?debug?main.go
Type?'help'?for?list?of?commands.
(dlv)
調(diào)試會(huì)話啟動(dòng)后,我們可以使用調(diào)試命令進(jìn)行調(diào)試程序。
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)
調(diào)試會(huì)話啟動(dòng)后,我們可以使用 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 命令添加斷點(diǎn),和 list 命令一樣,添加斷點(diǎn)的位置,也可以使用上述兩種方式。
我們可以使用 breakpoints 命令,列出所有斷點(diǎn),可以使用 clear 命令刪除指定斷點(diǎn),可以使用 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 添加斷點(diǎn)后,執(zhí)行 continue 命令,程序?qū)?zhí)行到斷點(diǎn)位置;執(zhí)行 next 命令,程序繼續(xù)執(zhí)行下一行代碼;執(zhí)行 step 命令,程序步入到調(diào)用函數(shù)內(nèi)部;執(zhí)行 stepout 命令,程序步出到調(diào)用函數(shù)的調(diào)用位置;執(zhí)行 print 命令,打印指定參數(shù)的值。
讀者朋友們使用以上命令,可以滿足大部分調(diào)試場(chǎng)景。為了方便理解,以上示例中使用的命令都沒(méi)有使用簡(jiǎn)寫(xiě)形式,在實(shí)際使用時(shí),使用簡(jiǎn)寫(xiě)形式會(huì)更加便捷。
簡(jiǎn)寫(xiě)形式:
-
break(b) -
continue(c) -
next(n) -
step(s) -
stepout(so) -
print(p)
04? 總結(jié)
本文我們簡(jiǎn)單介紹 Go 語(yǔ)言調(diào)試器 Delve 的基本使用方式,讀者朋友們可以在程序調(diào)試時(shí)將 Delve 使用起來(lái),替換使用 print 打印的形式調(diào)試代碼。
關(guān)于 Delve 的高級(jí)功能,例如調(diào)試 goroutines、將調(diào)試器附加到現(xiàn)有進(jìn)程、遠(yuǎn)程調(diào)試以及從 VSCode 編輯器或 Goland IDE 使用 Delve。感興趣的讀者朋友們可以參考 Delve 的幫助文檔。
原文鏈接:https://mp.weixin.qq.com/s/bEl-z-xiIOf966MR9HdzQA