使用 dlv 可以方便的对 go 生成的二进制文件进行 debug,以下面的代码 main.go 为例讲解 dlv 的使用
package main
import (
"fmt"
"net/http"
"time"
)
func Hello(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query().Get("q")
if q != "" {
fmt.Printf("search query: %s\n", q)
}
fmt.Fprintf(w, "Hello World! %s", time.Now())
}
func main() {
http.HandleFunc("/", Hello)
http.ListenAndServe(":8080", nil)
}
直接使用 go install 命令即可将 dlv 安装到 $GOPATH/bin 目录下面
# 可以下载最新的
go install github.com/go-delve/delve/cmd/dlv@latest
# 也可以下载指定的版本
go install github.com/go-delve/delve/cmd/dlv@v1.7.3
直接使用 go build 编译二进制文件的时候,会对代码进行优化和内联,导致使用 dlv 的时候部分命令不能使用,比如函数的调用。
# 存在优化的编译
go build -o http_server main.go
# 去掉编译优化
go build -gcflags="all=-N -l" -o http_server main.go
使用 exec 子命令,直接调试可运行的二进制文件
➜ dlv exec ./http_server
Type 'help' for list of commands.
(dlv)
比如我想在 main.go 的 Hello 函数进行调试,定位这个函数的行数为第 9 行,路径为 main.go,然后可以执行 break main.go:9
(dlv) break main.go:9
Breakpoint 1 set at 0x6e1bef for main.Hello() ./main.go:9
如果提示 location xxx ambiguous,需要对路径进行明确,使用输出的长路径即可,比如 break github.com/xxx/xxx/main.go。
设置好断点之后,可以通过 breakpoints命令查询所有的断点,可以发现 dlv 默认也会将 panic 加入断点。
(dlv) breakpoints
Breakpoint runtime-fatal-throw (enabled) at 0x438720 for runtime.throw() /usr/local/go/src/runtime/panic.go:1188 (0)
Breakpoint unrecovered-panic (enabled) at 0x438a80 for runtime.fatalpanic() /usr/local/go/src/runtime/panic.go:1271 (0)
print runtime.curg._panic.arg
Breakpoint 1 (enabled) at 0x6e1bef for main.Hello() ./main.go:9 (1)
这一步不能省略!!! 省略之后无法调试断点,执行 continue 命令
(dlv) continue
# or
(dlv) c
如果请求 ok 了,continue 命令不再阻塞
(dlv) continue
> main.Hello() ./main.go:9 (hits goroutine(18):1 total:2) (PC: 0x6e1bef)
4: "fmt"
5: "net/http"
6: "time"
7: )
8:
=> 9: func Hello(w http.ResponseWriter, r *http.Request) {
10: q := r.URL.Query().Get("q")
11: if q != "" {
12: fmt.Printf("search query: %s\n", q)
13: }
14: fmt.Fprintf(w, "Hello World! %s", time.Now()
如果出现 warning 是因为二进制文件是经过优化的,编译的时候可以添加 -gcflags="all=-N -l" 参数去掉该提示。
使用 dlv 提供的命令,进行调试
| 命令 | 说明 |
|---|---|
| args | 当前函数的参数 |
| step | 进入函数/方法中 |
| next | 跳转到下一行代码 |
| continue/c | 运行代码直到下一个断点或者结束 |
| break/b | 设置断点 |
| breakpoints/bp | 所有的断点 |
| clear xx | 清理掉第 xx 个断点 |
| clearall | 清理所有的断点 |
| locals | 输出所有局部变量 |
| 输出指定的变量 |
print 如果需要输出长字符串,可以先执行 config max-string-len 1000 扩大输出的字符串长度
编译时需要禁止优化才能使用
go build xxxx -gcflags="all=-N -l"
| 命令 | 说明 |
|---|---|
| list | 输出源代码 |
| call | 调用函数 |
dlv 不仅使用 attach 对正在运行的程序进行调试,也可以直接对二进制文件进行调试 (dlv exec),甚至是没有编译的代码包 (dlv debug),更多更详细的文档,可以参考下面的两个链接:
多动手试试,很容易就可以熟悉 dlv 使用~