背景

使用 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`,调试就能够正常开始了!