Debugging Go Apps using Delve

Installation delve on macOS

直接看 文档

有一些要注意的,如果你的项目启用了 go mod,那么不要在项目目录下执行安装,因为这会把 delve 安装到项目目录下。

另外,记得配置好 go 的 bin 目录。

用法

首先进入项目目录,执行 dlv debug,会进入 delve 的交互界面。

  1. break main.go:29 在 29 行打下断点
  2. breakpoints 打印出断点的信息
  3. continue 运行程序,直到断点的位置
  4. print son 打印程序中的变量 son
  5. next 移至下一行代码
  6. list 查看目前行的源码
  7. on 1 print family 遇到断点时执行命令,这里的意思是在遇到第一个断点时,打印 family
  8. restart 重启 debug 进程

用法

我们有如下的代码:

package main

import "fmt"

type Person struct {
  Name string
  Age  int
}

func main() {
  var me Person
  me.Name = "Melvin"
  me.Age = 31

  var wife Person
  wife.Name = "Raye"
  wife.Age = 30

  var daughter Person
  daughter.Name = "Katherina"
  daughter.Age = 11

  var son Person
  son.Name = "Vite"
  son.Age = 10

  var family []Person

  family = append(family, me, wife, daughter, son)

  fmt.Println(family)
  birthdayToday(&daughter)
}

func birthdayToday(person *Person) {
  person.Age = person.Age + 1
}

我们进入 dlv debug 之后依次输入如下命令:

b main.go:29 # 添加第1个断点
b main.go:31 # 添加第2个断点
on 1 print family # 遇到第 1 个断点时,打印 family
on 2 print family # 遇到第 2 个断点时,打印 family
r # 重启程序
c # 执行,直到断点 1
c # 执行,直到断点 2

这时候我们观察到结果就是,第一次打印的是

> main.main() ./main.go:29 (hits goroutine(1):1 total:1) (PC: 0x10c276b)
	family: []main.Person len: 0, cap: 0, nil

第二次打印

> main.main() ./main.go:31 (hits goroutine(1):1 total:1) (PC: 0x10c28ab)
	family: []main.Person len: 4, cap: 4, [
		{Name: "Melvin", Age: 31},
		{Name: "Raye", Age: 30},
		{
			Name: "Katherina",
			Age: 11,},
		{Name: "Vite", Age: 10},
	]

更方便的命令

如果觉得上面的命令手打很麻烦,我们可以在项目目录下新建一个 txt 文件,比如名字叫 debug-config.txt,内容如下:

b main.go:29
b main.go:31
on 1 print family
on 2 print family

进入 dlv debug 执行 source ../debug-config.txt 然后同上面一样,依次执行 c, c 一样可以看到结果。

其他命令

  1. clearall 删除所有断点
  2. funcs main 查看所有函数
  3. locals 打印所有变量
  4. set son.Age = 3 修改变量的值
  5. b main.main 在 main 函数上加断点
  6. whatis son son 的类型

s 和 n 的区别

n 是继续执行下一行代码,而当下一行代码是一个函数的时候,用 s 才能跳入到函数内部。

以上面的例子,我们用 n 已经之下到如下位置

> main.main() ./LearnGo/main.go:33 (PC: 0x10c2931)
    28:
    29:		family = append(family, me, wife, daughter, son)
    30:
    31:		fmt.Println(family)
    32:
=>  33:		birthdayToday(&daughter)
    34:	}
    35:
    36:	func birthdayToday(person *Person) {
    37:		person.Age = person.Age + 1
    38:		fmt.Println(person.Age)

这时候,如果继续按 n 就会跳到下一行,也就是函数的结尾,只有按 s,单步跟踪,才能进入到 birthdayToday 函数的内部继续调试。

如果项目结构复杂怎么办

我这里有一个结构相对复杂的项目,也就是按照 这篇文章 来做的项目结构。

这时候你要进入项目的目录,然后执行 dlv debug ./cmd/server/main.go 就能正常调试整个项目了。

相关阅读

再忘记用法的时候,建议重新读下面的文章,本文都是摘抄的。

如果觉得我的文章对您有用,请在支付宝公益平台找个项目捐点钱。 @Victor Jun 18, 2020

奉献爱心