调试挂起的 Go 程序的提示

主要内容总结:

  • 场景与问题:在帮助设置gokrazy/rsync实现同步RPKI数据时,发现特定调用下rsync接收程序会无限挂起。通过构建特定版本并尝试同步仓库可重现该问题。
  • 调试技巧 1:按Ctrl+\SIGQUIT)打印堆栈跟踪:这是查看Go程序挂起位置的简单方法,Go运行时收到SIGQUIT会打印堆栈跟踪然后退出进程,默认启用,可通过GOTRACEBACK环境变量自定义,还可使用https://github.com/maruel/panicparse程序将堆栈跟踪以更美观的形式呈现,此例中问题在于rsync接收实现错误预期服务器发送uid/gid列表,相关提交已修复。
  • 调试技巧 2:附加delve调试器到进程:若仅打印堆栈跟踪不足以诊断问题,可使用交互式调试器,常用的Linux调试器是GDB,但调试Go程序推荐使用delve调试器,安装delve后,由于默认安全设置,需使用sudo sysctl -w kernel.yama.ptrace_scope=0允许在Linux中附加现有进程到调试器,然后使用dlv attach附加到挂起的gokr-rsync进程,开始时仅看到runtime包的函数,需切换到感兴趣的goroutine才能看到期望的堆栈跟踪。
  • 调试技巧 3:保存核心转储以备后用:若没时间即时使用调试器,可保存核心转储,通过运行程序时设置环境变量GOTRACEBACK=crash,可使Go运行时使程序崩溃并让Linux内核生成核心转储,现代Linux系统通常使用systemd-coredump收集核心转储,可使用coredumpctl管理,若系统未使用systemd-coredump,可使用ulimit -c unlimited和设置kernel.core_patternsysctl设置,然后可使用coredumpctl debug --debugger=dlv --debugger-arguments=core启动delve调试核心转储,此例中通过核心转储可看到与之前相同的堆栈跟踪信息。
  • 结论:长期来看,设置方便调试程序的环境是值得的,Go自带堆栈打印功能,通过GOTRACEBACK=crash可获取核心转储,结合delve调试器可有效诊断Go程序问题,作者运营博客多年,欢迎支持其工作(如购买咖啡)。
阅读 8
0 条评论