Go 調試工具 - delve 快速入門

簡介

Delve 用來調試 Go 語言開發的程序,該工具的目標是爲 Go 語言提供一個簡單、功能齊全的調試工具。

爲什麼不推薦 gdb

安裝

$ go install github.com/go-delve/delve/cmd/dlv@latest

# 安裝完成後查看版本
$ dlv verison

Delve Debugger
Version: 1.20.1
Build: $Id: 96e65b6c615845d42e0e31d903f6475b0e4ece6e

常用命令

快速開始

我們首先從一個簡單的示例程序開始,改程序打印字符串 hello world, 然後結束並退出。

源文件

// main.go

package main

func main() {
    println("hello world")
}

調試源文件

$ dlv debug main.go

# Type 'help' for list of commands.
(dlv) 

# 輸入 help 查看參數說明
(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.

# 運行程序
(dlv) continue
hello world
Process 3637 has exited with status 0

調試編譯後二進制文件

# 編譯源文件
$ go build -o main main.go

$ dlv exec ./main

Type 'help' for list of commands.
(dlv) 

# 接下來的步驟和調試源文件的一樣,這裏不再贅述

調試進程

爲了讓進程保持在運行狀態,我們在程序中加一行休眠代碼:

package main

import "time"

func main() {
    time.Sleep(time.Minute)
    println("hello world")
}

調試前先運行程序:

$ go run main.go

# 查看進程 ID
$ ps -ef | grep "go run main.go"

7602 27666  0 21:30 pts/6    00:00:00 go run main.go

# 調試進程
$ dlv attach 7602

Type 'help' for list of commands.
(dlv) 

...

# 1 分鐘之後,main.go 並未正常退出,因爲當前正在調試
# 輸入 continue 繼續運行

(dlv) continue 
Process 7602 has exited with status 0

常用調試命令

下列命令是啓動 dlv 調試後可用的命令 (也就是當前命令行變爲 (dlv) 之後可用)。

運行程序

xe940m

操作斷點

vUH3xi

查看變量或內存

vjTkjm

線程 / goroutine 的展示與切換

153XOg

調用堆棧

d5FMub

其他命令

dV0kfF

綜合示例

最後,我們使用一個的小例子,熟悉下常用的幾個命令。

示例程序代碼如下:

// main.go

package main

var (
    x = 1024
)

func main() {
    for i := 0; i < 5; i++ {
        println(i)
    }
}
# 開始調試
$ dlv debug main.go

Type 'help' for list of commands.
(dlv)
 
# 增加斷點
(dlv) b main.main
Breakpoint 1 set at 0x45f0c6 for main.main() ./main.go:7

# 查看斷點
(dlv) bp
...
Breakpoint 2 (enabled) at 0x45f0c6 for main.main() ./main.go:7 (0)

# 運行程序
(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:
# 可以看到,程序停在了設置的斷點上

# 打印包變量
(dlv) vars vars main.x
...
main.x = 1024
...

# 單步調試
(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:

# 再次單步調試
(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)

# 可以看到,程序停在了循環語句

# 打印包變量
(dlv) vars main.x
main.x = 0

# 再次單步調試
(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:
...

# 刪除所有斷點
(dlv) clearall
Breakpoint 1 cleared at 0x45f0c6 for main.main() ./main.go:7

# 繼續執行
(dlv) continue
1
2
3
4
Process 27873 has exited with status 0

# 退出調試
(dlv) exit

與 IDE 集成

Delve 支持以插件的形式集成到主流的 IDE 裏面,具體的支持列表請看 這個頁面 [1]。

常見問題

單點調試總是執行非預期的代碼?

一般是被編譯器優化了,比如內聯會導致 dlv 單步調試無法打印某些變量,解決方法是禁止編譯優化。

# 禁用內聯和優化 (細節可以閱讀引用文章列表)
go run -gcflags "-N -l" main.go

Reference

引用鏈接

[1] 這個頁面: https://github.com/go-delve/delve/blob/master/Documentation/EditorIntegration.md
[2] go-delve/delve: https://github.com/go-delve/delve
[3] 如何定位 golang 進程 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 語言 - 11: https://colobu.com/2016/07/04/dive-into-go-11/
[7] WSL2 安裝 perf: https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/57jcuLwsJbtxnkkM7_R2_A