新聞中心
本文轉(zhuǎn)載自微信公眾號(hào)「洋芋編程」,作者蠻荊 。轉(zhuǎn)載本文請(qǐng)聯(lián)系洋芋編程公眾號(hào)。

簡(jiǎn)介
Delve? 用來(lái)調(diào)試 Go? 語(yǔ)言開(kāi)發(fā)的程序,該工具的目標(biāo)是為 Go 語(yǔ)言提供一個(gè)簡(jiǎn)單、功能齊全的調(diào)試工具。
為什么不推薦 gdb
- gdb 對(duì) Go 的調(diào)試支持是通過(guò)一個(gè) python 腳本文件 src/runtime/runtime-gdb.py 擴(kuò)展的,功能有限
- gdb 只能做到最基本的變量打印,卻理解不了 golang 的一些特殊類型,比如 channel,map,slice 等,gdb 原生是無(wú)法調(diào)適 goroutine 協(xié)程的, 因?yàn)檫@是用戶態(tài)的調(diào)度單位,gdb 只能理解線程,所以只能通過(guò) python 腳本的擴(kuò)展,把協(xié)程結(jié)構(gòu)按照鏈表輸出
安裝
$ go install github.com/go-delve/delve/cmd/dlv@latest
# 安裝完成后查看版本
$ dlv verison
Delve Debugger
Version: 1.20.1
Build: $Id: 96e65b6c615845d42e0e31d903f6475b0e4ece6e
常用命令
- dlv attach - 調(diào)試進(jìn)程
- dlv core - 調(diào)試 core
- dlv debug? - 編譯并調(diào)試當(dāng)前目錄的 main 包,也可以通過(guò)參數(shù)指定其他包
- dlv exec - 調(diào)試二進(jìn)制文件
- dlv test - 編譯并調(diào)試測(cè)試文件
快速開(kāi)始
我們首先從一個(gè)簡(jiǎn)單的示例程序開(kāi)始,改程序打印字符串 hello world, 然后結(jié)束并退出。
源文件
// main.go
package main
func main() {
println("hello world")
}
調(diào)試源文件
$ dlv debug main.go
# Type 'help' for list of commands.
(dlv)
# 輸入 help 查看參數(shù)說(shuō)明
(dlv) help
The following commands are available:
Running the program:
call ------------------------ Resumes process
...
...
types ---------------------- Print list of types
Type help followed by a command for full documentation.
# 運(yùn)行程序
(dlv) continue
hello world
Process 3637 has exited with status 0
調(diào)試編譯后二進(jìn)制文件
# 編譯源文件
$ go build -o main main.go
$ dlv exec ./main
Type 'help' for list of commands.
(dlv)
# 接下來(lái)的步驟和調(diào)試源文件的一樣,這里不再贅述
調(diào)試進(jìn)程
為了讓進(jìn)程保持在運(yùn)行狀態(tài),我們?cè)诔绦蛑屑右恍行菝叽a:
package main
import "time"
func main() {
time.Sleep(time.Minute)
println("hello world")
}
調(diào)試前先運(yùn)行程序:
$ go run main.go
# 查看進(jìn)程 ID
$ ps -ef | grep "go run main.go"
7602 27666 0 21:30 pts/6 00:00:00 go run main.go
# 調(diào)試進(jìn)程
$ dlv attach 7602
Type 'help' for list of commands.
(dlv)
...
# 1 分鐘之后,main.go 并未正常退出,因?yàn)楫?dāng)前正在調(diào)試
# 輸入 continue 繼續(xù)運(yùn)行
(dlv) continue
Process 7602 has exited with status 0
常用調(diào)試命令
下列命令是啟動(dòng) dlv? 調(diào)試后可用的命令 (也就是當(dāng)前命令行變?yōu)?nbsp;(dlv) 之后可用)。
運(yùn)行程序
|
命令 |
描述 |
|
call |
恢復(fù)進(jìn)程,調(diào)用函數(shù) (實(shí)驗(yàn)階段) |
|
continue |
繼續(xù)運(yùn)行程序,直到遇到斷點(diǎn)或程序結(jié)束 |
|
next |
單步調(diào)試 |
|
restart |
重新運(yùn)行 |
|
step |
單步調(diào)試某個(gè)函數(shù) |
|
step-instruction |
單步調(diào)試某個(gè) CPU 指令 |
|
stepout |
從當(dāng)前函數(shù)跳出 |
操作斷點(diǎn)
|
命令 |
描述 |
|
break |
設(shè)置斷點(diǎn) |
|
breakpoints |
打印所有斷點(diǎn) |
|
clear |
刪除斷點(diǎn) |
|
clearall |
刪除所有斷點(diǎn) |
|
condition |
設(shè)置條件斷點(diǎn) |
|
on |
設(shè)置一個(gè)斷點(diǎn)觸發(fā)時(shí)執(zhí)行的命令 |
|
toggle |
打開(kāi)/關(guān)閉 斷點(diǎn) |
查看變量或內(nèi)存
|
命令 |
描述 |
|
args |
打印函數(shù)參數(shù) |
|
display |
每次程序停止時(shí)打印表達(dá)式的值 |
|
examinemem |
解析給定地址的內(nèi)存 |
|
locals |
打印本地變量 |
|
|
解析一個(gè)表達(dá)式 |
|
regs |
打印寄存器信息 |
|
set |
設(shè)置變量的值 |
|
vars |
打印包內(nèi)變量 |
|
whatis |
打印類型信息 |
線程 / goroutine 的展示與切換
|
命令 |
描述 |
|
goroutine |
打印或切換 goroutine |
|
goroutines |
打印所有 goroutine |
|
thread |
切換到指定的線程 |
|
threads |
打印所有線程信息 |
調(diào)用堆棧
|
命令 |
描述 |
|
deferred |
在 defer 上下文中執(zhí)行命令 |
|
frame |
設(shè)置當(dāng)前幀,或在不同的幀上執(zhí)行命令 |
|
stack |
打印堆棧信息 |
其他命令
|
命令 |
描述 |
|
config |
更改配置參數(shù) |
|
disassemble |
反匯編 |
|
dump |
dump core |
|
exit |
結(jié)束調(diào)試,也可以用 ? |
綜合示例
最后,我們使用一個(gè)的小例子,熟悉下常用的幾個(gè)命令。
示例程序代碼如下:
// main.go
package main
var (
x = 1024
)
func main() {
for i := 0; i < 5; i++ {
println(i)
}
}
# 開(kāi)始調(diào)試
$ dlv debug main.go
Type 'help' for list of commands.
(dlv)
# 增加斷點(diǎn)
(dlv) b main.main
Breakpoint 1 set at 0x45f0c6 for main.main() ./main.go:7
# 查看斷點(diǎn)
(dlv) bp
...
Breakpoint 2 (enabled) at 0x45f0c6 for main.main() ./main.go:7 (0)
# 運(yùn)行程序
(dlv) continue
> main.main() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x45f0c6)
2:
3: var (
4: x = 1024
5: )
6:
=> 7: func main() {
8: for i := 0; i < 5; i++ {
9: println(i)
10: }
11: }
12:
# 可以看到,程序停在了設(shè)置的斷點(diǎn)上
# 打印包變量
(dlv) vars vars main.x
...
main.x = 1024
...
# 單步調(diào)試
(dlv) next
> main.main() ./main.go:8 (PC: 0x45f0d4)
3: var (
4: x = 1024
5: )
6:
7: func main() {
=> 8: x = 0
9: for i := 0; i < 5; i++ {
10: println(i)
11: }
12: }
13:
# 再次單步調(diào)試
(dlv) next
> main.main() ./main.go:9 (PC: 0x45f0df)
4: x = 1024
5: )
6:
7: func main() {
8: x = 0
=> 9: for i := 0; i < 5; i++ {
10: println(i)
11: }
12: }
13:
14: //timeout := time.After(time.Minute)
# 可以看到,程序停在了循環(huán)語(yǔ)句
# 打印包變量
(dlv) vars main.x
main.x = 0
# 再次單步調(diào)試
(dlv) next
> main.main() ./main.go:10 (PC: 0x45f0f4)
5: )
6:
7: func main() {
8: x = 0
9: for i := 0; i < 5; i++ {
=> 10: println(i)
11: }
12: }
13:
14: //timeout := time.After(time.Minute)
15: //
# 打印本地變量
(dlv) locals
i = 0
# 查看堆棧信息
(dlv) stack
0 0x000000000045f0f4 in main.main
at ./main.go:10
1 0x00000000004358b8 in runtime.main
at /usr/local/go/src/runtime/proc.go:250
2 0x000000000045c0c1 in runtime.goexit
at /usr/local/go/src/runtime/asm_amd64.s:1594
# 打印 goroutine 信息
(dlv) goroutine
Thread 27873 at ./main.go:10
Goroutine 1:
...
# 刪除所有斷點(diǎn)
(dlv) clearall
Breakpoint 1 cleared at 0x45f0c6 for main.main() ./main.go:7
# 繼續(xù)執(zhí)行
(dlv) continue
1
2
3
4
Process 27873 has exited with status 0
# 退出調(diào)試
(dlv) exit
與 IDE 集成
Delve 支持以插件的形式集成到主流的 IDE 里面,具體的支持列表請(qǐng)看 這個(gè)頁(yè)面[1]。
常見(jiàn)問(wèn)題
單點(diǎn)調(diào)試總是執(zhí)行非預(yù)期的代碼?
一般是被編譯器優(yōu)化了,比如內(nèi)聯(lián)會(huì)導(dǎo)致 dlv 單步調(diào)試無(wú)法打印某些變量,解決方法是禁止編譯優(yōu)化。
# 禁用內(nèi)聯(lián)和優(yōu)化 (細(xì)節(jié)可以閱讀引用文章列表)
go run -gcflags "-N -l" main.go
Reference
- go-delve/delve[2]
- 高效獲取堆棧調(diào)用信息
- 內(nèi)聯(lián)優(yōu)化
- 逃逸分析
- golang 調(diào)試分析的高階技巧
- 如何定位 golang 進(jìn)程 hang 死的 bug[3]
- Debugging with GDB[4]
- 100-gdb-tips[5]
- 深入 Go 語(yǔ)言 - 11[6]
- WSL2 安裝 perf[7]
引用鏈接
[1]? 這個(gè)頁(yè)面: ??https://github.com/go-delve/delve/blob/master/Documentation/EditorIntegration.md??
[2]? go-delve/delve: ??https://github.com/go-delve/delve??
[3]? 如何定位 golang 進(jìn)程 hang 死的 bug: ??https://xargin.com/how-to-locate-for-block-in-golang/??
[4]? Debugging with GDB: ??https://sourceware.org/gdb/current/onlinedocs/gdb.html/??
[5]? 100-gdb-tips: ??https://github.com/hellogcc/100-gdb-tips??
[6]? 深入 Go 語(yǔ)言 - 11: ??https://colobu.com/2016/07/04/dive-into-go-11/??
[7]? WSL2 安裝 perf: https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13
當(dāng)前名稱:Go調(diào)試工具-Delve快速入門(mén)
網(wǎng)站網(wǎng)址:http://fisionsoft.com.cn/article/dhdihdo.html


咨詢
建站咨詢
