<2018-07-27 Fri>
How to do go debug.
Background
Go has no its own debugger so it turned to gdb. See Debugging Go Code with GDB for details.
But GDB does not understand Go programs well. As a better alternative, Delve has its place.
Installation
go get github.com/derekparker/delve/cmd/dlv
Code to Debug
main.go
:
package main
import (
"fmt"
"golang.org/x/sync/errgroup"
)
func main() {
hello()
}
var Printf = fmt.Printf
func hello() {
g := &errgroup.Group{}
for i := 0; i < 10; i++ {
g.Go(func() error {
Printf("goroutine %02d\n", i)
return nil
})
}
g.Wait()
}
main_test.go
:
package main
import (
"fmt"
"reflect"
"testing"
)
func TestHello(t *testing.T) {
lines := make([]string, 10)
Printf = func(format string, a ...interface{}) (n int, err error) {
i := a[0].(int)
lines[i] = fmt.Sprintf(format, a...)
return len(lines[i]), nil
}
hello()
want := []string{
"goroutine 00\n",
"goroutine 01\n",
"goroutine 02\n",
"goroutine 03\n",
"goroutine 04\n",
"goroutine 05\n",
"goroutine 06\n",
"goroutine 07\n",
"goroutine 08\n",
"goroutine 09\n",
}
if !reflect.DeepEqual(want, lines) {
t.Fatal(want, lines)
}
}
Debugging Practices
We will learn:
- How to set breakpoints
break main.main
,break TestHello
,break db.go:27
,
- How to switch goroutines
goroutines
to list goroutines,goroutine 18
to switch goroutine
- How to debug a service
Debug a Program
$ dlv debug
Type 'help' for list of commands.
(dlv) break main.main
Breakpoint 1 set at 0x10b1faf for main.main() ./main.go:9
(dlv) continue
> main.main() ./main.go:9 (hits goroutine(1):1 total:1) (PC: 0x10b1faf)
4: "fmt"
5:
6: "golang.org/x/sync/errgroup"
7: )
8:
=> 9: func main() {
10: hello()
11: }
12:
13: var Printf = fmt.Printf
14:
Debug Test Case
dlv test
Type 'help' for list of commands.
(dlv) break TestHello
Breakpoint 1 set at 0x1137b4b for _/...TestHello() ./main_test.go:9
(dlv) continue
> _.../hello.TestHello() ./main_test.go:9 (hits goroutine(5):1 total:1) (PC: 0x1137b4b)
4: "fmt"
5: "reflect"
6: "testing"
7: )
8:
=> 9: func TestHello(t *testing.T) {
10: lines := make([]string, 10)
11: Printf = func(format string, a ...interface{}) (n int, err error) {
12: i := a[0].(int)
13: lines[i] = fmt.Sprintf(format, a...)
14: return len(lines[i]), nil
(dlv) break 12
Breakpoint 2 set at 0x1138101 for _.../hello.TestHello.func1() ./main_test.go:12
(dlv) continue
> _...hello.TestHello.func1() ./main_test.go:12
(hits goroutine(15):1 total:1) (PC: 0x1138101)
7: )
8:
9: func TestHello(t *testing.T) {
10: lines := make([]string, 10)
11: Printf = func(format string, a ...interface{}) (n int, err error) {
=> 12: i := a[0].(int)
13: lines[i] = fmt.Sprintf(format, a...)
14: return len(lines[i]), nil
15: }
16: hello()
17: want := []string{
(dlv) print a
[]interface {} len: 1, cap: 1, [
10,
]
Debug a Service
$ icp-cland&
$ ps -ef|grep icp-cland
501 89630 3744 0 2:44PM ttys002 0:00.34 icp-cland
$ dlv attach 89630
Type 'help' for list of commands.
(dlv) break github..../cland/icp-cland/icp/service/admin/db.go:27
Breakpoint 1 set at 0x47c771e for github...
(dlv) continue
> github..../cland/icp-cland/icp/...
Warning: debugging optimized function
22: *logging.Tracer
23: }
24:
25: func (dba *dbAdmin) Purge(ctx context.Context,
26: req *dbs.PurgeRequest) (rep *dbs.PurgeReply, err error) {
=> 27: sp, ctx, logger := dba.StartSpanFromContext(ctx, "DBAdminPurge")
28: defer sp.Finish()
29: rep = &dbs.PurgeReply{}
30: filter := req.GetFilter()
31: var purged int64
32: purged, err = model.Purge(filter)