go调试工具-delve
简介
go debug工具,专门为go开发的调试工具,并且采用go语言开发,支持多平台。
官网:https://github.com/go-delve/delve
官网有详细的手册,学习起来很方便
快速开始
安装
我本地的go版本
官方手册
go install github.com/go-delve/delve/cmd/dlv@latest
测试代码
package main
import (
"context"
"errors"
"fmt"
)
func main() {
fmt.Println("begin")
var (
a = []int{1,2,3,4,5}
b = []int{1,2,3,4,5}
ctx = context.Background()
)
res, err := doOperation(ctx, a, b)
if err != nil {
return
}
fmt.Printf("%v\n",res)
fmt.Println("end")
}
func doOperation(ctx context.Context,a,b []int) ([]int,error) {
if len(a) == 0 && len(b) == 0{
return nil,errors.New("slice is empty")
}
if len(a) != len(b){
return nil,errors.New("slice length not equals")
}
res := make([]int, len(a))
for index := range a {
res[index] = a[index] + b[index]
}
return res,nil
}
文件结构
dlv开始调试
进入项目的根目录下,执行
dlv debug
输入help看帮助信息
断点
在main.go的第十行打个断点
输入 b
或者break
继续运行直到下一个断点
输入 c
或者continue
逐步运行
输入n
或者next
进入到调用函数(step in)
输入s
或step
现在在doOperation
打个断点,先运行到这里
b main.go:17
在输入c
s进入,结果如下
查看当前执行的代码
list
退出当前函数(step out)
输入so
或者step out
现在运行到了doOperation
,想要返回到main 调用它的地方,输入so
打印变量
p
或者print
查看所有的本地变量
locals
重新debug
修改代码之后重新的debug
rebuild
,会保留之前打的断点
使用介绍
上面的快速开始介绍了一些常用的使用方式,它的help写的很好,很清晰,关于单个命令就不再这里介绍了。
下面主要介绍几个命令
- debug
- exec
- test
除此之外还有一个子命令(开始dlv session之后的输入的命令)
disassemble-官方文档
debug
官方文档
dlv debug [package] [flags]
在没有参数的情况下,会自动编译当前文件夹下面的main包,也可以指定包的名字来debug
此外,还支持远程debug。
–headless :会开启一个服务端,用connect命令来连接及可进行debug,只要命令里面支持这个flag就可以用。
例子如下
-
启动服务端
–headless:启动服务端
-l:指定端口
–log:开启日志
dlv debug --headless -l :2345 --log
- 客户端连接
dlv connect localhost:2345
连接好之后就可以正常操作了。
exec
官方文档
对编译好的二进制进行debug
建议在build 二进制文件的时候要关闭掉编译器的优化,build的增加下面参数
-gcflags="all=-N -l"
同样的,它也支持服务器的方式,远程调试。
test
用来做单元测试的debug
官方文档
例子如下:
现在对上面的doOperation
做单元测试,测试代码如下
package main
import (
"context"
"reflect"
"testing"
)
func Test_doOperation(t *testing.T) {
var ctx = context.Background()
t.Run("test1", func(t *testing.T) {
type args struct {
ctx context.Context
a []int
b []int
}
tests := []struct {
name string
args args
want []int
wantErr bool
}{
{
name: "test1",
args: args{
ctx: ctx,
a: []int{1,2,3,4},
b: []int{1,2,3,4},
},
want: []int{2,4,6,8},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := doOperation(tt.args.ctx, tt.args.a, tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("doOperation() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("doOperation() got = %v, want %v", got, tt.want)
}
})
}
})
t.Run("test2", func(t *testing.T) {
type args struct {
ctx context.Context
a []int
b []int
}
tests := []struct {
name string
args args
want []int
wantErr bool
}{
{
name: "test1",
args: args{
ctx: ctx,
a: []int{1,2,3,4},
b: []int{1,2,3,4},
},
want: []int{2,4,6,8},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := doOperation(tt.args.ctx, tt.args.a, tt.args.b)
if (err != nil) != tt.wantErr {
t.Errorf("doOperation() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("doOperation() got = %v, want %v", got, tt.want)
}
})
}
})
}
如上所示,一个方法里面有两个子测试。
dlv test [package] [flags]
-
运行当前package下所有的单测
dlv test -- --test.v
-
选择的运行
dlv test -- --test.run Test_doOperation --test.v // 如果想要运行Test_doOperation中的test1,需要用/来拼接 dlv test -- --test.run Test_doOperation/test1 --test.v
可以正常的做操作,比如打断点
查看它支持的test相关的flag
随便输入一个错误的flag,会输出所有的flag
dlv test -- --test.cd
goLand 远程debug
这就是把上面说的远程debug和goLand结合了起来。先说出核心操作是
- 服务端开启用dlv 启动debug,headlress模式,go在build源码的时候需要禁用优化。
- 客户端连接,正常debug
先用命令行操作,在结合goLand使用
命令行操作
go build
go build -o debug_main -gcflags "all=-N -l" base_go
dlv启动服务端
dlv exec ./debug_main --listen=:9987 --headless --api-version=2 --accept-multiclient --log
// 命令解释
// listen 指定host和port
// headless 启动无头浏览器
// api-version
// accept-multiclient
dlv启动客户端
dlv connect hadoop105:9987
正常操作
现在的背景是:服务端在Linux服务器中,客户端在windows中。
打断点
list查看代码
因为两个执行路径不一样,所以,打不开文件。但别的功能是可以用的,
goLang操作
搜索Go Remote,添加Host和Port,其实这里以及告诉我们服务端要怎么做了。
启动服务端
先在config里面选择刚才的remote,打断点,debug运行(先打断点,在启动debug)
到这里就结束了