Delve是托管在Github上的一个开源项目。
项目地址:https://github.com/go-delve/delve
概要
Delve是Go程序的源代码级调试器。
Delve使您可以通过控制进程的执行,评估变量并提供线程/ goroutine状态,CPU寄存器状态等信息来与程序进行交互。
该工具的目的是为调试Go程序提供一个简单而强大的界面。
使用
Usage:
dlv [command]
Available Commands:
attach Attach to running process and begin debugging.
connect Connect to a headless debug server.
core Examine a core dump.
dap [EXPERIMENTAL] Starts a TCP server communicating via Debug Adaptor Protocol (DAP).
debug Compile and begin debugging main package in current directory, or the package specified.
exec Execute a precompiled binary, and begin a debug session.
help Help about any command
run Deprecated command. Use 'debug' instead.
test Compile test binary and begin debugging program.
trace Compile and begin tracing program.
version Prints version.
————————————————————————————————————————————————————————————————————————————————————
attach 连接到正在运行的流程并开始调试.
connect 连接到无头调试服务器.
core 检查核心转储.
dap [EXPERIMENTAL]启动通过调试适配器协议(DAP)进行通信的TCP服务器。
debug 编译并开始调试当前目录下的主包或指定的包.
exec 执行预编译的二进制文件,并开始调试会话.
help 帮助信息
run 弃用的命令。使用“debug”替代它.
test 编译测试二进制文件并开始调试程序.
trace 编译并开始跟踪程序.
version 打印版本.
使用"dlv [command] --help"获取有关命令的详细信息.
上面列举了dlv的一些命令,其中常用的有如 help、attach、core、debug、trace、version 等。
dlv help:上面的信息只是列出了命令列表,具体使用方法没有给出,这里可以运行 dlv help 查看具体命令的使用方法。也可以运行 dlv help help 查看 help 命令的使用说明。这里以查看 core 命令为例。
Examine a core dump (only supports linux and windows core dumps).
The core command will open the specified core file and the associated
executable and let you examine the state of the process when the
core dump was taken.
Currently supports linux/amd64 and linux/arm64 core files and windows/amd64 minidumps.
Usage:
dlv core <executable> <core> [flags]
GOTRACEBACK
GOTRACEBACK
GOTRACEBACK 控制程序崩溃时输出的详细程度。 它可以采用不同的值:
none 不显示任何goroutine栈trace。
single, 默认选项,显示当前goroutine栈trace。
all 显示所有用户创建的goroutine栈trace。
system 显示所有goroutine栈trace,甚至运行时的trace。
crash 类似 system, 而且还会生成 core dump。
最后一个选项使我们能够在发生崩溃的情况下调试程序。
以以下程序为例:
package main
import "math/rand"
var sum int
func main() {
for {
n := rand.Intn(1e6)
sum += n
if sum % 42 == 0 {
panic(":)")
}
}
}
运行它,程序很快就发生崩溃:
panic: :)
goroutine 1 [running]:
main.main()
/home/liuxin/gopath/src/test/panic.go:12 +0x8a
exit status 2
GOTRACEBACK=crash
go build -gcflags=all='-N -l' panic.go # 编译,关闭优化方便调试。编译后生成了二进制文件 panic
ulimit -c unlimited # 解开转储文件大小限制,否则不会生成转储文件
GOTRACEBACK=crash ./panic # 运行,会提示核心已转储。生成了转储文件 core
dlv core panic core # 调试
core dump
Type 'help' for list of commands.
(dlv) bt
0 0x000000000045d371 in runtime.raise
at /home/liuxin/go/src/runtime/sys_linux_amd64.s:165
1 0x0000000000442a8b in runtime.dieFromSignal
at /home/liuxin/go/src/runtime/signal_unix.go:729
2 0x0000000000442f4e in runtime.sigfwdgo
at /home/liuxin/go/src/runtime/signal_unix.go:943
3 0x0000000000441bd4 in runtime.sigtrampgo
at /home/liuxin/go/src/runtime/signal_unix.go:412
4 0x000000000045d6d3 in runtime.sigtramp
at /home/liuxin/go/src/runtime/sys_linux_amd64.s:389
5 0x000000000045d7c0 in runtime.sigreturn
at /home/liuxin/go/src/runtime/sys_linux_amd64.s:481
6 0x0000000000442c3a in runtime.crash
at /home/liuxin/go/src/runtime/signal_unix.go:821
7 0x000000000042e6a4 in runtime.fatalpanic
at /home/liuxin/go/src/runtime/panic.go:1216
8 0x000000000042e01e in runtime.gopanic
at /home/liuxin/go/src/runtime/panic.go:1064
9 0x0000000000461ddd in main.main
at ./panic.go:15
10 0x0000000000430ac8 in runtime.main
at /home/liuxin/go/src/runtime/proc.go:203
11 0x000000000045b7f1 in runtime.goexit
at /home/liuxin/go/src/runtime/asm_amd64.s:1373
dlvbtpanicframe 9
(dlv) frame 9
> runtime.raise() /home/liuxin/go/src/runtime/sys_linux_amd64.s:165 (PC: 0x45d371)
Warning: debugging optimized function
Frame 9: ./panic.go:15 (PC: 461ddd)
Warning: listing may not match stale executable
10: sum += n
11: if sum % 42 == 0 {
12: panic(":)")
13: }
14: }
=> 15: }
locals
(dlv) locals
n = 203300
sumvars
(dlv) vars main
runtime.mainStarted = true
runtime.main_init_done = chan bool 0/0
main.sum = 5705994
main..inittask = runtime.initTask {state: 2, ndeps: 1, nfns: 0}
main.main()main()main()packagepackagemainmaingo/src/runtime/proc.go
调试
进入调试模式的几种方法:
dlv attach pidgdb attach pid
dlv debugdlv debug test.goattachdebug
dlv exec executable_file
dlv core executable_file core_file
dlv trace
dlv trace
package main
import (
"fmt"
"time"
)
func Afunc(){
fmt.Println("Called")
}
func main(){
ticker := time.NewTicker(time.Second)
for{
select{
case <-ticker.C:
Afunc()
}
}
} //每隔一秒执行调用一次Afunc这个函数
dlv trace trace.go Afunc
$ dlv trace trace.go Afunc
> goroutine(1): main.Afunc()
Called
=> ()
> goroutine(1): main.Afunc()
Called
=> ()
> goroutine(1): main.Afunc()
Called
=> ()
> goroutine(1): main.Afunc()
Called
=> ()
Afunc
下面完整的来一遍调试过程:
以写的代码为例:
goroutinegoroutineDelvegoroutine
package main
import (
"fmt"
"sync"
"time"
)
func dostuff(wg *sync.WaitGroup, i int) {
fmt.Printf("goroutine id %d\n", i)
time.Sleep(300 * time.Second)
fmt.Printf("goroutine id %d\n", i)
wg.Done()
}
func main() {
var wg sync.WaitGroup
workers := 10
wg.Add(workers)
for i := 0; i < workers; i++ {
go dostuff(&wg, i)
}
wg.Wait()
}
dlv debug
$ dlv debug test.go
Type 'help' for list of commands.
(dlv)
dlv help
break [name]:
(dlv) b test.go:16
Breakpoint 2 set at 0x4affc3 for main.main() ./test.go:16
c
(dlv) c
> main.main() ./test.go:16 (hits goroutine(1):1 total:1) (PC: 0x4affc3)
workers: (unreadable eval error: could not find symbol value for workers)
11: time.Sleep(300 * time.Second)
12: fmt.Printf("goroutine id %d\n", i)
13: wg.Done()
14: }
15:
=> 16: func main() {
17: var wg sync.WaitGroup
18: workers := 10
19:
20: wg.Add(workers)
21: for i := 0; i < workers; i++ {
bp
(dlv) bp
Breakpoint runtime-fatal-throw at 0x434360 for runtime.fatalthrow() /home/liuxin/go/src/runtime/panic.go:1162 (0)
Breakpoint unrecovered-panic at 0x4343d0 for runtime.fatalpanic() /home/liuxin/go/src/runtime/panic.go:1189 (0)
print runtime.curg._panic.arg
Breakpoint 1 at 0x4b0398 for main.main() ./test.go:16 (0)
上面也有提到
bt
frame
print
同时你也可以输出一个表达式
(dlv) print workers < 100
true
dostuff
(dlv) break dostuff
Breakpoint 2 set at 0x4b01e8 for main.dostuff() ./test.go:9
cn
(dlv) c
> main.dostuff() ./test.go:9 (hits goroutine(7):1 total:2) (PC: 0x4afe18)
> main.dostuff() ./test.go:9 (hits goroutine(6):1 total:2) (PC: 0x4afe18)
4: "fmt"
5: "sync"
6: "time"
7: )
8:
=> 9: func dostuff(wg *sync.WaitGroup, i int) {
10: fmt.Printf("goroutine id %d\n", i)
11: time.Sleep(300 * time.Second)
12: fmt.Printf("goroutine id %d\n", i)
13: wg.Done()
14: }
(dlv) n
> main.dostuff() ./test.go:9 (hits goroutine(15):1 total:4) (PC: 0x4afe18)
> main.dostuff() ./test.go:9 (hits goroutine(11):1 total:4) (PC: 0x4afe18)
> main.dostuff() ./test.go:10 (PC: 0x4afe2f)
5: "sync"
6: "time"
7: )
8:
9: func dostuff(wg *sync.WaitGroup, i int) {
=> 10: fmt.Printf("goroutine id %d\n", i)
11: time.Sleep(300 * time.Second)
12: fmt.Printf("goroutine id %d\n", i)
13: wg.Done()
14: }
15:
(dlv) print i
0
(dlv) n
goroutine id 5
可以看到Delve会告诉你目前的goroutine id.
大体就这样,讲的还不够全面,更多的功能,感兴趣的话可以查查参考文档摸索吧。