准备工作

  1. 安装 delve
  2. 项目代码
1
2
git clone git@git.baijiashilian.com:web/demo/go-debug-demo.git
git clone git@git.baijiashilian.com:web/gotools/gitlab-tools.git

调试工具 delve

delve 安装

1
go install github.com/go-delve/delve/cmd/dlv@latest

dlv 配置

1
~/.dlv/config.yml

调试方式

本地调试

从源码开始编译并开始调试

1
dlv debug [package] [flags]

调试已经编译好的二进制文件

1
dlv exec ./hello -- args1 args2
  1. 编译的时候需要禁用编译器优化:go build -gcflags="-N -l"
  2. 编译选项有哪些:go tool compile -h

调试正在运行的进程

1
dlv attach pid [executable] [flags]

调试单元测试

1
dlv test [package] [flags]

远程调试

JSON-RPC & DAP

1
dlv --headless --listen 0.0.0.0:2345 <command> <target> <args>

兼容dlv connectVS Code GoGoLand

使用 DAP(Debugger Adapter Protocol)

开启一个 DAP Server,只支持 DAP 协议

1
dlv dap --listen 0.0.0.0:2345 [flags]

只支持VS Code,不支持GoLanddlv connect

客户端 dlv connect

1
dlv connect <addr>

实践操作

本地调试 - dlv 命令行

准备源码

1
git clone git@git.baijiashilian.com:web/gotools/gitlab-tools.git

启动 dlv

1
dlv debug -- project ls

加断点

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
  gitlab-tools git:(master) dlv debug -- project ls  //编译并开始运行程序,命令行参数 project ls
Type 'help' for list of commands.
(dlv) c /*****运行程序到下一个断点处,因为没加过断点,所以程序直接运行结束了*****/
Warning: load config failed! error:config file:/Users/caiyili/.gitlab/config not exists!

254   android/maven   https://git.baijiashilian.com/android/maven
Process 81002 has exited with status 0
(dlv) b root.go:53  /*****尝试给room.go第53行加断点,失败了,提示进程已经结束*****/
Process 81002 has exited with status 0
(dlv) r  /*****重启程序*****/
Process restarted with PID 81007
(dlv) b root.go:53  /*****再次浓度给room.go第53行加断点,提示这个room.go文件有歧义*****/
Command failed: Location "root.go:53" ambiguous: /Users/caiyili/Code/gitlab-tools/cmd/root.go, /usr/local/go/src/crypto/x509/root.go
(dlv) b cmd/root.go:53 /*****cmd/root.go:53加断点*****/
Breakpoint 1 set at 0x104899fb0 for gitlab-tools/cmd.onInit() ./cmd/root.go:53
(dlv) bp  /****查看所有断点*****/
Breakpoint runtime-fatal-throw (enabled) at 0x10448e2b0 for runtime.throw() /usr/local/go/src/runtime/panic.go:982 (0)
Breakpoint unrecovered-panic (enabled) at 0x10448e5c0 for runtime.fatalpanic() /usr/local/go/src/runtime/panic.go:1065 (0)
	print runtime.curg._panic.arg
Breakpoint 1 (enabled) at 0x104899fb0 for gitlab-tools/cmd.onInit() ./cmd/root.go:53 (0)
(dlv) c  /*****运行程序到下一个断点处******/
> gitlab-tools/cmd.onInit() ./cmd/root.go:53 (hits goroutine(1):1 total:1) (PC: 0x104899fb0)
    48:
    49:	func onInit() {
    50:		if cfgPath == "" {
    51:			cfgPath = config.GetDefaultCfgPath()
    52:		}
=>  53:		if err := config.LoadConfig(cfgPath); err != nil {
    54:			fmt.Fprintf(os.Stderr, "Warning: load config failed! error:%v \n\n", err)
    55:		}
    56:		cfg = config.Cfg()
    57:		if verbose {
    58:			printJson(cfg)

(dlv) locals
(no locals)
(dlv) p cfgPath
"/Users/caiyili/.gitlab/config"
(dlv) vars cmd.cfg*
gitlab-tools/cmd.cfgCmd = ("*github.com/spf13/cobra.Command")(0x104bc79a0)
gitlab-tools/cmd.cfgShowCmd = ("*github.com/spf13/cobra.Command")(0x104bc7c20)
gitlab-tools/cmd.cfgSetCmd = ("*github.com/spf13/cobra.Command")(0x104bcb0a0)
gitlab-tools/cmd.cfgPath = "/Users/caiyili/.gitlab/config"
gitlab-tools/cmd.cfg = *gitlab-tools/config.Config nil
(dlv) l
> gitlab-tools/cmd.onInit() ./cmd/root.go:53 (hits goroutine(1):1 total:1) (PC: 0x104899fb0)
    48:
    49:	func onInit() {
    50:		if cfgPath == "" {
    51:			cfgPath = config.GetDefaultCfgPath()
    52:		}
=>  53:		if err := config.LoadConfig(cfgPath); err != nil {
    54:			fmt.Fprintf(os.Stderr, "Warning: load config failed! error:%v \n\n", err)
    55:		}
    56:		cfg = config.Cfg()
    57:		if verbose {
    58:			printJson(cfg)
(dlv) s /*****step 下一步,会进到调用函数里****/
> gitlab-tools/config.LoadConfig() ./config/config.go:28 (PC: 0x1045d5f30)
    23:
    24:	func (c *Config) GetApiHost() string {
    25:		return c.Host
    26:	}
    27:
=>  28:	func LoadConfig(cfgPath string) (err error) {
    29:		var c = cfg
    30:		c.path = cfgPath
    31:		if !fileExist(cfgPath) {
    32:			return fmt.Errorf("config file:%s not exists!", cfgPath)
    33:		}
(dlv) n /*****下一行源码*****/
> gitlab-tools/config.LoadConfig() ./config/config.go:32 (PC: 0x1045d61a4)
    27:
    28:	func LoadConfig(cfgPath string) (err error) {
    29:		var c = cfg
    30:		c.path = cfgPath
    31:		if !fileExist(cfgPath) {
=>  32:			return fmt.Errorf("config file:%s not exists!", cfgPath)
    33:		}
    34:		file, err := os.OpenFile(cfgPath, os.O_RDONLY, 0644)
    35:		if err != nil {
    36:			return fmt.Errorf("read config file '%s' error: %w", cfgPath, err)
    37:		}
(dlv) stepout /*****把当前函数执行完,回到上一层函数*****/
> gitlab-tools/cmd.onInit() ./cmd/root.go:53 (PC: 0x104899fc4)
Values returned:
	err: error(*errors.errorString) *{
		s: "config file:/Users/caiyili/.gitlab/config not exists!",}

    48:
    49:	func onInit() {
    50:		if cfgPath == "" {
    51:			cfgPath = config.GetDefaultCfgPath()
    52:		}
=>  53:		if err := config.LoadConfig(cfgPath); err != nil {
    54:			fmt.Fprintf(os.Stderr, "Warning: load config failed! error:%v \n\n", err)
    55:		}
    56:		cfg = config.Cfg()
    57:		if verbose {
    58:			printJson(cfg)
(dlv)

远程调试 - VSCode

1
git@git.baijiashilian.com:web/demo/go-debug-demo.git

远程机器启动 dlv

1
2
3
cd /apps/home/caiyili/baijiayun/go-debug-demo

dlv debug -l 0.0.0.0:2345  --headless --api-version=2 --log --log-output=dap,rpc

VSCode 配置 launch.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "remote-debug-demo",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "remotePath": "/apps/home/caiyili/baijiayun/go-debug-demo",
            "port": 2345,
            "substitutePath": [{
                "from": "/apps/home/caiyili/baijiayun/go-debug-demo",
                "to": "/Users/caiyili/Code/go-debug-demo"
        	}],
            "host": "test-www.baijiayun.com"
        },
        {
            "name": "launch-debug-demo",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}"
        }
    ]
}

image.png

VSCode Launch 配置参考https://code.visualstudio.com/docs/editor/debugging#_launch-configurations

dlv connect

1
dlv connect test-www.baijiayun.com:2345![image.png]

image.png

点击开始调试

image.png

服务端日志

image.png

添加断点

image.png

访问页面

1
curl http://test-www.baijiayun.com:8088/view

常用命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
(dlv) help
The following commands are available:

Running the program:
    continue (alias: c) --------- Run until breakpoint or program termination.
    next (alias: n) ------------- Step over to next source line.
    restart (alias: r) ---------- Restart process.
    step (alias: s) ------------- Single step through program.
    step-instruction (alias: si)  Single step a single cpu instruction.
    stepout (alias: so) --------- Step out of the current function.

Manipulating breakpoints:
    break (alias: b) ------- Sets a breakpoint.
    breakpoints (alias: bp)  Print out info for active breakpoints.
    clear ------------------ Deletes breakpoint.


Viewing program variables and memory:
    args ----------------- Print function arguments.
    locals --------------- Print local variables.
    print (alias: p) ----- Evaluate an expression.
    vars ----------------- Print package variables.
    whatis --------------- Prints type of an expression.

Listing and switching between threads and goroutines:
    goroutine (alias: gr) -- Shows or changes current goroutine
    goroutines (alias: grs)  List program goroutines.

Other commands:
    list (alias: ls | l) ------- Show source code.

FAQ

远程调试需要做代码路径映射

dlv 命令中映射

1
(dlv) config substitute-path /apps/home/caiyili/baijiayun/go-debug-demo /Users/caiyili/Code/go-debug-demo

vscode launch 配置

1
2
3
4
5
6
"substitutePath": [
  {
    "from": "/apps/home/caiyili/baijiayun/go-debug-demo",
    "to": "/Users/caiyili/Code/go-debug-demo"
  }
]

dlv 配置文件中配置映射 ~/.dlv/config

1
2
3
4
# commands.
substitute-path:
  - {from: /apps/home/caiyili/baijiayun/prometheus-demo , to: /Users/caiyili/Code/prometheus-demo }
  # - {from: path, to: path}

DAP

image.png

image.png

相关资料