GO delve(dlv)调试工具笔记及实操
这个文档总结了,我自己在linux通过delve上调试go代码的一些操作,比较常用的。无论是在调试应用,还是自己trace源码都挺好用。
GO version: 1.16.8
dlv versoin:1.7.2
dlv安装
$ go install github.com/go-delve/delve/cmd/dlv@latest
go版本小于1.16的用下面方式安装
$ git clone https://github.com/go-delve/delve
$ cd delve
$ go install github.com/go-delve/delve/cmd/dlv
dlv指令
仅列出常用或者会用到的
| 指令 | 用处 | 实操 |
|---|---|---|
| attach | 这个命令将使Delve控制一个已经运行的进程,并开始一个新的调试会话。 当退出调试会话时,你可以选择让该进程继续运行或杀死它。 | case1 |
| exec | 这个命令将使Delve执行二进制文件,并立即附加到它,开始一个新的调试会话。请注意,如果二进制文件在编译时没有关闭优化功能,可能很难正确地调试它。请考虑在Go 1.10或更高版本上用-gcflags="all=-N -l "编译调试二进制文件,在Go的早期版本上用-gcflags="-N -l"。 | case2 |
| help | 使用手册 | case3 |
| debug | 默认情况下,没有参数,Delve将编译当前目录下的 "main "包,并开始调试。或者,你可以指定一个包的名字,Delve将编译该包,并开始一个新的调试会话。 | case4 |
| test | test命令允许你在单元测试的背景下开始一个新的调试会话。默认情况下,Delve将调试当前目录下的测试。另外,你可以指定一个包的名称,Delve将在该包中调试测试。双破折号`--`可以用来传递参数给测试程序。 | case5 |
| version | 查看dlv版本 | case6 |
dlv调试指令
仅记录个人觉得会用到的指令
断点管理
| 指令 | 缩写 | 用法 | 案例 |
|---|---|---|---|
| break | b | 设置断点 | case7 |
| breakpoints | bp | 查看当前所有断点 | case8 |
| clear | / | 删除断点 | case9 |
| clearall | / | 删除多个断点 | case10 |
| toggle | / | 启用或关闭断点 | case11 |
程序执行中的调试指令
| 指令 | 缩写 | 用法 | 案例 |
|---|---|---|---|
| continue | c | 继续执行到一个断点或者程序结束吗 | case12 |
| next | n | 执行下一行代码 | case13 |
| restart | r | 重新执行程序 | case14 |
| step | s | 执行代码的下一步 | case15 |
| step-instruction | si | 执行下一行机器码 | case16 |
| stepout | so | 跳出当前执行函数 | case17 |
参数管理
| 指令 | 缩写 | 用法 | 案例 |
|---|---|---|---|
| args | / | 打印函数input | case18 |
| display | / | 打印加入到display的变量的值,每次执行下一行代码或下一个断点时 | case19 |
| locals | / | 打印局部变量 | case20 |
| p | 打印表达式的结果 | case21 | |
| set | / | 设置某个变量的值 | case22 |
| vars | / | 查看全局变量 | case23 |
| whatis | / | 查看变量类型 | case24 |
其他
| 指令 | 缩写 | 用法 | 案例 |
|---|---|---|---|
| disassemble | disass | 查看反编译后的代码,机器码 | case25 |
| exit | quit / q | 退出 | case26 |
| funcs | / | 打印程序用到的所有函数 | case27 |
| help | h | 帮助信息 | case28 |
| list | ls / l | 打印代码 | case29 |
dlv实操案例
测试代码
package main
import (
"fmt"
"log"
"math/rand"
"net/http"
"time"
func count(i, j int) int {
yz := 5
result := (i + j) * yz
return result
func randHandler(w http.ResponseWriter, r *http.Request) {
rand.Seed(time.Now().Unix())
var (
i = rand.Intn(20)
j = rand.Intn(20)
result := count(i, j)
_, _ = w.Write([]byte(fmt.Sprintf("%d", result)))
func main() {
http.HandleFunc("/rand", randHandler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("start server fail: %v", err)
case1 attach
我理解的就是对一个阻塞的程序进行debug,比如http server。
- 背景启动http server
-
dlv attach 176,attach刚才背景执行的程序的pid。同时,给请求的方法打一个断点。 - 发送一个请求给这个http server,应该是会阻塞住
- c,继续执行,会执行到打断点的地方,这样就可以对http server的方法进行debug
case2 exec
对一个可执行文件进行调试,是针对编译后的。
注意,如果直接
go build -v
,那有些代码会被优化掉,比如内联函数,在调试的时候会被忽略掉。比如如下代码:
package main
import "fmt"
func main(){
a := count(1,2)
fmt.Println(a)