Debug go programs with plugins using VSCode Go extension
/使用VSCode的golang插件debug go程序是非常方便的,可以比较容易地实现查看变量名、断点调试、单步跟踪等功能。但是笔者最近在使用这个插件debug 需要用到go plugin的程序 (MIT 6.824 mapreduce lab)时,遇到了问题:无论怎么办,从plugin中加载函数都会失败,这让我百思不得其解。在翻阅无数stackoverflow帖子和github issues之后,终于找到了解决办法,便有了这篇博文。
1. 问题复现
此处需要进行debug的go程序的文件名为mrworker.go
,go插件在launch.json
中给出的默认配置如下:
1 | { |
对其稍加修改,得到的配置文件如下:
1 | { |
程序mrworker.go
需要接受一个命令行参数指定plugin,这里我选用wc.so
。
在加载wc.so
时,会发现程序调用plugin.Open
打开该插件时失败了:
使用调试器打印错误信息如下:
1 | plugin was built with a different version of package runtime/internal/atomic |
2. 错误原因
逛了很长时间的stackoverflow和github之后,才知道这个错误出现的原因:编译可执行文件和plugin的build flags不同。plugin的编译是用的MIT 6.824中给出的命令:
1 | go build -race -buildmode=plugin ../mrapps/wc.go |
但是dlv 编译可执行文件时的build flags无从知晓,在网上翻阅很多资料都没有找到。
build flags的不一致导致上述错误的发生。
3. 解决办法
我们无法获知dlv编译可执行文件时的build flags,那么我们可以自行编译,然后调试编译后的可执行文件。这里需要将dlv的模式从debug
转成exec
,表示debug可执行文件,同时在launch.json
文件中添加preLaunchTask
属性,表示在debug之前首先执行指定的任务,在这里就是用我们自己的flags编译插件和可执行文件了。修改之后的配置如下:
launch.json:
1 | { |
tasks.json:
1 | { |
tasks里面便是编译可执行文件和plugin的命令:
build_mrworker_plugin.sh
1
2
3
4#!/usr/bin/zsh
cd ./src/main
go build -race -buildmode=plugin ../mrapps/wc.go
go build -race mrworker.go
这样可以看到我们的程序可以正常加载plugin了:
尽管我们现在可以正常调试了,但是还是会遇到下面的问题:
在调试的过程中会看到左边侧栏会提示optimized function,这样的话有时候查看一些重要变量的值或者属性时会失败,解决这个问题只需要在编译可执行文件和plugin时加上编译选项-gcflags="all=-N -l"
即可。