R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。

在 VSCode 中配置 Go 调试环境

前期准备

作为一个 Go 开发者,首先需要把系统中的 Go 的环境配好(包括环境变量和包管理器),具体怎么配这儿就不说了。不过 Go 的项目似乎都需要跑在 $GOPATH/src 下面,感觉有点反人类。Go 最推荐的调试器就是 Delve,安装和使用方法这儿也不说了,正确安装后应该可以在命令行中直接运行 dlv 命令。

想看结果的同学,可以直接跳到文章末尾,那儿有可以运行的配置。


VSCode 作为微软爸爸的东西,我一直是很推崇的;刚好最近也在搞 Go 的项目,于是在想如何不用 JB 系软件(太臃肿)的情况下轻松上手 Go 开发。VSCode 的插件还是很多的,按照网上的文章,下了一个 Go 插件(居然也是微软爸爸官方支持),我随便写了个 Go 文件,按下了 F5,弹出了一个叫 launch.json 的文件,心想 VSCode 那么智能,应该可以直接运行的吧?

我随便写了个 Go 文件,打了个断点,按 F5 之后发现可以正常调试了,但公司的项目用了 Gin,入口文件在某个目录下。F5 只能调试当前打开的文件,而不是启动整个 Gin 项目,因此我开始折腾 VSCode 的调试系统。

由于需要对项目配置 VSCode 的东西,并且 Delve 的原理是先复制一个叫 debug 的程序到项目目录下,调试结束后再删掉,但如果程序被强行结束则不会删,因此需要在项目的 .gitignore 中加入两行:

.vscode/
debug

插件能做的事情

插件可以做到高亮、代码补全、查找引用、自动构建、代码格式化等各种各样的事情,用的都是 Go 自带的东西,例如 gocodegodefgolint 等,这些包在插件初始化的时候就会自动安装,如果遇到什么问题,后期也可以对照着插件的文档或错误提示手动 go get

在文档中提到了“部分支持调试”,这应该是让 VSCode 的调试系统支持 Go 的前提。

VSCode 的调试系统

对于支持多种调试工具的 VSCode 来说,它的原理是这样的:

  1. 调试工具(例如 Delve)启动,加载被调试的代码,并监听本地的一个端口
  2. VSCode 通过访问这个端口来跟 Delve 交互
  3. 接收到的数据可以被 Go 插件解析,并通过 VSCode 的 API 反映在界面上

这也是大多数通过第三方调试工具来调试的原理。

VSCode 通过读取 launch.json 来判断需要加载的是哪个调试器,对于 Go 来说,文件大概的格式如下:

{
    "version": "该配置文件的版本,由 VSCode 自动生成",
    "configurations": [
        {
            "name": "名字,好听就行",
            "type": "语言类型,装了插件后填 go 就会调用 dlv 命令",
            "request": "launch 是启动新进程,append 是附加到现有的进程",
            "mode": "dlv 的一些选项,支持 debug、exec、test、remote",
            "host": "dlv 监听的地址",
            "port": "dlv 监听的端口(此处应该是数字)",
            "program": "本地的程序路径",
            "cwd": "当前工作路径",
            "env": {
                "环境变量名": "环境变量值"
            },
            "args": ["程序启动参数"],
            "showLog": "是否输出 dlv 的日志(此处应该是 true/false)"
        }
    ]
}

其实 VSCode 编辑这类文件的时候会有一个代码提示,提示已经足够详细了。

通过思考和多次尝试,我得出了结论:Delve 的命令行执行方式是 dlv debug --headless --listen 2345 path/to/main.go
,并且需要保证当前工作路径是项目根目录而不是 main.go 所在目录,因此应该做如下配置(workspaceFolder 是当前打开的文件所属的工作区的根目录,如果你是将项目文件夹一个个加入工作区的,那就是加入的文件夹目录):

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug with dlv",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "host": "127.0.0.1",
            "port": 2345,
            "program": "${workspaceFolder}/path/to/main.go",
            "cwd": "${workspaceFolder}",
            "env": {},
            "args": [],
            "showLog": true
        }
    ]
}

搞定!打一个断点,按下 F5,会发现项目已经启动起来了,访问一个 API,发现触发了断点:

1

不过还有个缺点,就是断点是在 Delve 启动的时候就设置好了的,因此如果修改了断点,也需要重新启动调试,但这毕竟是后端项目,跟前端相比还是没有那么方便的。


Update 2018.11.18

添加或删除断点可以不用重新启动调试,只需要等一两秒,让插件把命令发给 Delve 之后就可以了。

版权声明:除文章开头有特殊声明的情况外,所有文章均可在遵从 CC BY 4.0 协议的情况下转载。

这是我们共同度过的

第 1114 天