[译] 使用 VSCode 调试 Golang
原文来自于: vscode-go
1、安装 Delve
有两种安装 Delve 的方式:
- 支持命令
Go: Install/Update Tools
选择dlv
, 安装/更新 delve
(注: 文章中提到的执行命令, 并不是在终端中执行,而是在VS Code 命令面板
中执行, 打开方式:查看
-> 命令面板
)
- 或者手动安装 delve, 参考 安装说明
windows
go get -u github.com/go-delve/delve/cmd/dlv
linux
方式一:go get -u github.com/go-delve/delve/cmd/dlv
方式二
$ git clone https://github.com/go-delve/delve.git $GOPATH/src/github.com/go-delve/delve
$ cd $GOPATH/src/github.com/go-delve/delve
$ make install
注意: 若果你go版本为1.5需要设置
GO15VENDOREXPERIMENT=1
OSX
$ go get -u github.com/go-delve/delve/cmd/dlv
执行上述代码前,确保你电脑上有编译工具
2、调试器配置说明
调试器会使用要以下这些配置, 在通常情况下, 你不需要更改或者修改他们中的任何一项, 但是需要看一看。
go.gopath
:查看GOPATH in VS Codego.inferGopath
: 查看GOPATH in VS Codego.delveConfig
apiVersion
:启动 headless delve 服务, 需要指定的 delve api 版本, 默认为 2。dlvLoadConfig
:当 apiVersion 为 1 时不适用。配置会传递给 delve, 控制 delve 的各种功能,这些功能会影响调试窗格中显示的变量。maxStringLen
: 从字符串读取的最大字节数。maxArrayValues
: 从数组,切片或 map 中读取的最大元素数。maxStructFields
: 从结构读取的最大字段数,-1
将读取所有字段。maxVariableRecurse
: 嵌套类型读取的最大层级。
虽然大多数情况下你不需要调整配置, 但在以下情况需要调整 delve
的配置
- 在调试视图中检查变量时,可能需要更改字符串和数组的长度, 默认上限更改为 64。
- 在调试视图中检查嵌套变量时,请按照实际情况进行配置。
3、设置launch.json
配置文件
当 delve 安装后, 运行命令Debug: Open launch.json
, 如果没有 launch.json
文件, 则会使用默认配置创建一个文件,此文件用于调试当前程序。
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"env": {},
"args": []
}
]
}
以下是launch.json
中的一些属性说明:
属性 | 说明 |
---|---|
name | 定义配置名字 |
type | 指定语言,这里我们填go 即可 |
request | launch ,attach , 当需要对一个已经运行的的程序 debug 时才使用 attach ,其他时候使用launch |
mode | 对于 launch 有 auto , debug , remote , test , exec , 对于 attach 只有local ,remote |
program | 指定包, 文件或者是二进制的绝对路径 |
env | 调试程序时需要注入的环境变量, 例如:{ "ENVNAME": "ENVVALUE" } |
envFile | 绝对路径,env 的值会覆盖envFile 的值 |
args | 需要传给调试程序的命令行参数 |
showLog | 布尔值,是否在调试控制台打印日志, 一般为true |
logOutput | 日志输出目标, 使用逗号分隔达到使用多个组件输出日志的目的 (debugger , gdbwire , lldbout , debuglineerr , rpc ), 当 showLog 为 true 有效 |
buildFlags | 构建程序时需要传递给 Go 编译器的 Flags |
remotePath | 如果mode 为remote 时, 需要指定调试文件所在服务器的绝对路径 |
processId | 进程 id |
host | 目标服务器地址 |
port | 目标端口 |
(1)在调试过程中使用 VS Code 变量
${workspaceFolder}
在工作区的的根目录调试程序${file}
调试当前文件${fileDirname}
调试当前文件所属的程序包
(2)使用 build tags
如果在构建时需要构建标签, (比如:go build -tags=whatever_tag
), 则需要使用标签buildFlags
并添加内容: "-tags=whatever_tag"
,如果你需要使用多个标签时, 则需要使用单引号引起来,像这样:"-tags='first_tag second_tag third_tag'"
4、常用的launch.json
配置示例
在编辑launch.json
时, 你可以将下面这些代码片段用于调试配置。
(1)调试当前文件的配置样本
{
"name": "Launch file",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${file}"
}
(2)调试单个测试用例配置样本
{
"name": "Launch test function",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}",
"args": [
"-test.run",
"MyTestFunction"
]
}
(3)调试包内所有测试用例配置样本
{
"name": "Launch test package",
"type": "go",
"request": "launch",
"mode": "test",
"program": "${workspaceFolder}"
}
(4)调试预构建二进制配置样本
{
"name": "Launch executable",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "absolute-path-to-the-executable"
}
(5)调试本地已运行进程配置样本
{
"name": "Attach to local process",
"type": "go",
"request": "attach",
"mode": "local",
"processId": 0
}
5、远程调试
要使用 VS Code 进行远程调试, 那么需要在远程服务器上运行 headless delve 服务。下面这个示例假定了 你要调试的程序与你在同一目录下, 如果没有, 请参考 dlv debug 命令上的用法文档。
# 在远程服务器上启动 delve 服务
$ dlv debug --headless --listen=:2345 --log --api-version=2
如果需要传递参数到程序, 必须通过Delve
服务传递,例如:
$ dlv debug --headless --listen=:2345 --log -- -myArg=123
然后在 VS Code 中创建一个远程调试的launch.json
{
"name": "Launch remote",
"type": "go",
"request": "launch",
"mode": "remote",
"remotePath": "absolute-path-to-the-file-being-debugged-on-the-remote-machine",
"host": "127.0.0.1", # 目标服务器地址
"port": 2345, # 目标端口
"program": "absolute-path-to-the-file-on-the-local-machine",
"env": {}
}
remotePath
应该指向目标服务器中调试文件的绝对路径 (在源代码中)program
指向本地计算机上文件的绝对路径,此路径与remotePath
对应。
注意: 当选择了这个新的Launch remote
配置启动调试器时,VS Code 会将调试命令发送到 目标服务器的 dlv
, 而不是针对应用程序启动它自己的 dlv 实例。
在 Docker 中调试参考:https://github.com/lukehoban/webapp-go/tree/debugging
6、一步一步调试
我们将使用两个示例来调试我们的Go代码。
- 一个生成JSON的简单go程序。
- 我们将编写一个函数,编写测试并查看如何在VS Code中调试测试
示例1:复仇者 (Example 1: Avengers)
这是第一个示例的源代码。 创建一个文件main.go
package main
import (
"encoding/json"
"fmt"
"log"
)
// Avenger represents a single hero
type Avenger struct {
RealName string `json:"real_name"`
HeroName string `json:"hero_name"`
Planet string `json:"planet"`
Alive bool `json:"alive"`
}
func (a *Avenger) isAlive() {
a.Alive = true
}
func main() {
avengers := []Avenger{
{
RealName: "Dr. Bruce Banner",
HeroName: "Hulk",
Planet: "Midgard",
},
{
RealName: "Tony Stark",
HeroName: "Iron Man",
Planet: "Midgard",
},
{
RealName: "Thor Odinson",
HeroName: "Thor",
Planet: "Midgard",
},
}
avengers[1].isAlive()
jsonBytes, err := json.Marshal(avengers)
if err != nil {
log.Fatalln(err)
}
fmt.Println(string(jsonBytes))
}
在这里,我们只是定义一个struct Avenger
,然后创建一个复仇者数组,将其中一个复仇者的状态更改为还活着,然后将结果转换为JSON,最后将其打印到STDOUT。
您可以使用
go run main.go
// 输出
[{"real_name":"Dr. Bruce Banner","hero_name":"Hulk","planet":"Midgard","alive":false},{"real_name":"Tony Stark","hero_name":"Iron Man","planet":"Midgard","alive":true},{"real_name":"Thor Odinson","hero_name":"Thor","planet":"Midgard","alive":false}]
要开始调试,我们需要创建一个配置。 单击Visual Studio Code左窗格上的“调试”图标。 接下来,单击齿轮图标以创建配置。
![img](vscode-debug-use/SNYhXvw5QpOJquApssPl_Screen Shot 2018-07-30 at 23.09.50.png)
在.vscode/launch.json
下创建一个配置文件,其内容如上所示。 更改配置程序以指向main.go文件。 在这种情况下,由于只有main.go
文件,因此可以将其更改为工作区根目录:
{
// ...
"configuration": [
{
// ...
"program": "${workspaceRoot}",
// ...
}
]
}
接下来,我们需要添加一个断点,因为这就是调试的全部目的。
让我们在第21行func main()
上添加一个断点,方法是单击行号左侧,我们将看到一个红点。
![img](vscode-debug-use/L6KELCfTeyGwSV7OC8qg_Screen Shot 2018-07-30 at 23.15.09.png)
接下来,按F5
或单击左上角“调试”部分上带有绿色播放按钮的“启动”按钮。 它应该打开调试视图。
![img](vscode-debug-use/hFD6Oe7DQsyW4UEb6wPx_Screen Shot 2018-07-30 at 23.16.52.png)
在“调试工具栏”上的“ Step Over
按钮上单击两次。
![img](vscode-debug-use/kXxTp955RTK6AJogAWTa_Screen Shot 2018-07-30 at 23.39.30.png)
一旦调试器移至第40行。
![img](vscode-debug-use/aIx38lKCRcanrbDVxdIg_Screen Shot 2018-07-30 at 23.41.31.png)
左侧的“调试”部分将为我们提供当前断点位置的状态。
![img](vscode-debug-use/P1KDoXbOSsS67fuEj37z_Screen Shot 2018-07-30 at 23.37.12.png)
我们可以在“变量”部分的特定时间查看变量的状态/值。
我们还可以看到调用堆栈,当前运行函数是main
函数,以及line 40
。
您可以继续Stepping Over
,一旦我们越过线,您将看到avengers
的价值发生变化。 "Tony Stark"
is Alive
.
![img](vscode-debug-use/sJT7KlTS4eQiqO94gk0w_Screen Shot 2018-07-30 at 23.45.11.png)
条件断点 (Conditional Breakpoints)
VSCode断点为您提供了一个选项,可以通过给它们一个表达式来编辑断点,大多数情况下,这些时间通常是布尔表达式。
例如,在第40行avengers[1].isAlive()
,我们可以在此处添加一个条件,即仅当我们要计算的表达式为true时才会引发断点。 例如avenger[1].Planet == "Earth"
为此,请在断点上单击鼠标右键,然后选择“编辑断点”。
![img](vscode-debug-use/uocIXzzGSb8HQLxbpzAQ_Screen Shot 2018-07-30 at 23.48.27.png)
如果没有断点,仍然可以右键单击,然后会提示您添加Conditional Breakpoint
![img](vscode-debug-use/3wMg8wvSqSnhSGguK3pv_Screen Shot 2018-07-30 at 23.53.50.png)
选择以上任意一项后,让我们添加条件。 avenger[1].Planet == "Earth"
![img](vscode-debug-use/CiLhwAwiQWiTv1O9MXB5_Screen Shot 2018-07-30 at 23.54.16.png)
现在,如果您使用F5
启动调试器,它将不会在断点处停止。 该应用程序将正常运行,我们将在调试控制台中看到结果。
![img](vscode-debug-use/LxU8aMbQNeQwV3msFYaK_Screen Shot 2018-07-30 at 23.57.01.png)
但是,当我们编辑代码以符合我们的期望时。 将托尼·斯塔克的星球改变为Earth
// ...
{
RealName: "Tony Stark",
HeroName: "Iron Man",
Planet: "Earth",
},
// ...
当我们再次使用F5
启动调试器时,现在将打开“调试视图”,并且执行在断点处停止。 我们可以看到JSON没有显示在调试控制台中
![img](vscode-debug-use/dqddqnWYRGqWhCHHpM6z_Screen Shot 2018-07-30 at 23.59.50.png)
调试测试 (Debugging Tests)
让我们向文件中添加一个新函数,以实现简单的加法运算。
func add(a, b int) int{
return a+b
}
然后,我们在同一目录中创建测试文件main_test.go
,其中包含以下内容。
package main
import "testing"
func Test_add(t *testing.T) {
a, b, c := 1, 2, 3
res := add(a, b)
if res != c {
t.Fail()
}
}
该代码仅将两个数字相加,而测试仅调用该函数。
但是,如果您安装了VSCode-Go插件,您将在测试功能的顶部看到其他选项。 run test
和debug test
![img](vscode-debug-use/DrcNNILbQrqHKotioGj2_Screen Shot 2018-07-31 at 00.07.35.png)
您可以单击run test
以运行测试,并在“输出”窗口中查看结果。
但是,要调试测试,也许是因为我们无法弄清楚,我们需要做的就是像以前一样添加一个断点,然后单击debug test
。
if res != c
,让我们在第10行添加一个断点,然后单击debug test。
![img](vscode-debug-use/vMqbDfYIRWKEyMMyRz8t_Screen Shot 2018-07-31 at 00.08.08.png)
“调试”视图再次打开,我们可以使用“调试工具”跳过并检查左侧变量部分的状态。
总结
调试是开发软件的关键部分,借助Visual Studio Code之类的工具,可以使我们的生活变得更加轻松。
我们在这里使用了一个简单的示例来解释这些概念,但是可以随时将调试器添加到任何现有项目中,并进行尝试。 您最终将减少用于记录执行期间给定点代码的值/状态的fmt.Println
语句。
7、Troubleshooting
如果在调试 Go 代码遇到问题时, 首先应该更新 delve 版本, 确保使用的是最新的 delve, 并且已使用当前的 Go 版本对其进行了编译。为确保最新版本,请执行命令 Go: Install/Update Tools
选择 dlv
, 点击OK
(1)启动调试日志
将调试配置中的
showLog
属性设置为true
。在 delve 的调试控制台可以看到调试日志。将调试配置中的
trace
属性设置为log
。在调试控制台看到日志的同时也会将日志保存在文件中,日志文件会在调试控制台开头打印出来,注意查看。将调试配置中的
logOutput
属性设置为
rpc
。您看到与 VS Code 和 delve 之间往返的 RPC 消息相对应的日志。需要将 showLog 设置为 true。
logOutput
属性与delve
的--log-output
标志对应,使用逗号分隔可以输出到多个组价列表。