背景
使用 vscode 调试 golang 程序相信大家并不陌生,但当我们要调试的程序有以下特点的话,是不是会变得很棘手?
- 要调试的程序并不由我们直接触发
- 要调试的程序是短暂进程(调试中难以捕获进程 id)
本文将以 git-hooks(proc-receive)的调试为例介绍一种针对这种场景的调试方案
程序编译
当然我们在编译代码前要记得关闭编译器优化:
go build -gcflags='all=-N -l' -o hooks/proc-receive.bin cmd/proc-receive/*.go
使用 dlv 托管进程的启动
一般来说我们直接使用的是编译出的二进制文件,但在调试场景下,如果能通过某些手段让程序在真正执行前先“等待”我们的调试器去连接,并且在调试器下发一个“开始执行”的指令后才执行代码逻辑,我们就能实现对这类进程的调试。
幸运的是 dlv 工具就为我们提供了这样的能力。如下,我们事先在调试器中设置好断点,然后将 proc-receive 的启动以 dlv headless server 的形式进行包装,那么在该程序被真正唤起之前就会插入一个 dlv 等待调试客户端的连接的启动暂停!
#!/bin/bash
dlv --log --log-dest hooks/dlv.log --listen=:2345 --headless=true --api-version=2 exec hooks/proc-receive.bin
注意,使用 dlv 启动进程时推荐配置 --log 和 --log-dest 参数,否则会干扰到原程序的 stdout 和 stderr,进而影响程序的正常交互
配置 launch.json
如上,我们通过 dlv headless server 的方式启动的程序,vscode 自然需要进行配套的一些设置:
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "attach proc-receive",
"type": "go",
"request": "attach",
"mode": "remote",
"remotePath": "${workspaceFolder}",
"port": 2345,
"host": "127.0.0.1"
}
]
}
开始调试
初始化仓库和 proc-receive 配置
# 初始化仓库
git init --bare /tmp/demo.git
# 配置被 proc-receive 截获的引用
cd /tmp/demo.git
# 开始 push-options 支持
git config receive.advertisepushoptions true
# refs/ok will mark command status `ok`
git config --add receive.procreceiverefs refs/ok
# refs/ng will mark command status `ng`
git config --add receive.procreceiverefs refs/ng
# refs/re will mark command status `ok` with a renamed refname
git config --add receive.procreceiverefs refs/re
# refs/ig will ignore the command totally
git config --add receive.procreceiverefs refs/ig
# 配置 git hooks 指向我们工程里的 hooks 目录
mv hooks hooks.backup
ln -sf $PROJECTDIR/hooks
触发 proc-receive
# repo clone
git clone /tmp/demo.git /tmp/demo
cd /tmp/demo
# add some commit
git commit --allow-empty -m "empty commit on `date`"
# push to trigger proc-receive
GIT_TRACE_PACKET=1 git push origin HEAD:refs/ok/01 HEAD:refs/ng/01 HEAD:refs/re/01 HEAD:master
使用 vscode 进行调试
执行 git push 后,push 的流程在需要唤起 proc-receive 进程的时候就会 hang 住,等待 dlv 客户端的连接
此时,使用 vscode 打开代码工程,在程序中添加断点(因为是短暂进程,不提前设断点的话程序一下子就执行完成了),然后切换到调试 tab,选择 `attach proc-receive`,调试就能够正常开始了!