Delve目的就是为了解决开发者在使用 GDB 调试中遇到的各种各样的问题。我们开始详细的介绍一些使用Delve 调试代码的例子。

安装

首先默认你已经安装了 Go 环境,安装命令很简单,一句话。

go install github.com/go-delve/delve/cmd/dlv
GO15VENDOREXPERIMENT=1
调试代码
dlv
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()
}
goroutinegoroutineDelvegoroutine
dlv debug test-debug.go
attachdebug session

首先我们在main函数上设置一个断点。

(dlv) break main.main
Breakpoint 1 set at 0x22d3 for main.main() ./test-debug.go:16
continue
(dlv) continue
> main.main() ./test-debug.go:16 (hits goroutine(1):1 total:1) (PC: 0x22d3)
    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++ {
(dlv)
next
(dlv) next
> main.main() ./test-debug.go:17 (PC: 0x22d7)
    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++ {
    22:            go dostuff(&wg, i)
(dlv)
> main.main() ./test-debug.go:18 (PC: 0x22f1)
    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++ {
    22:            go dostuff(&wg, i)
    23:        }
(dlv)
> main.main() ./test-debug.go:20 (PC: 0x22fa)
    15:
    16:    func main() {
    17:        var wg sync.WaitGroup
    18:        workers := 10
    19:
=>  20:        wg.Add(workers)
    21:        for i := 0; i < workers; i++ {
    22:            go dostuff(&wg, i)
    23:        }
    24:        wg.Wait()
    25:    }
(dlv)    
print
(dlv) print wg
sync.WaitGroup {
    state1: [12]uint8 [0,0,0,0,0,0,0,0,0,0,0,0],
    sema: 0,}
(dlv) print workers
10
(dlv)

同时你也可以输出一个表达式

(dlv) print workers < 100
true
dostuff 
(dlv) break dostuff
Breakpoint 2 set at 0x2058 for main.dostuff() ./test-debug.go:9
continuenext
(dlv) next
goroutine id 3
> main.dostuff() ./test-debug.go:10 (PC: 0x205f)
     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)

可以看到Delve会告诉你目前的goroutine id,我们试试输出一下i和wg.

(dlv) print i
4
(dlv) print wg
*sync.WaitGroup {
    state1: [12]uint8 [1,0,0,0,10,0,0,0,0,0,0,0],
    sema: 0,}
    

我们创建了10个goroutine,如果你继续使用next,你会发现你还是在同一个goroutine下。这样就避免了被调试器跳转到了另外的goroutine下导致不必要的调试错误。可见还是为 Go 而生的调试器才是真爱啊。

进阶调试
daemonattachattach
go build test-debug.go
./test-debug

然后使用ps查看正在运行的程序pid

  501 40994   549   0 12:08AM ttys003    0:00.00 ./test-debug
attach
dlv attach 40994
debug seesion
(dlv) break dostuff
Breakpoint 1 set at 0x2058 for main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:9
(dlv) break dostuff:3
Breakpoint 2 set at 0x2144 for main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12
continue
(dlv) continue
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(18):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(19):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(26):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(23):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(24):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(20):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(21):1 total:8) (PC: 0x2144)
> main.dostuff() /Users/xianlu/WorkSpace/golang/src/test-debug.go:12 (hits goroutine(25):1 total:8) (PC: 0x2144)
     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:
    16:    func main() {
    17:        var wg sync.WaitGroup
(dlv)
print
(dlv) print i
7
(dlv) print wg
*sync.WaitGroup {
    state1: [12]uint8 [1,0,0,0,10,0,0,0,0,0,0,0],
    sema: 0,}
attach