概述

写代码容易,调代码难,这对于写了很多代码的人来说都是很有感触的一个事情。在 Go 中更是如此,因为 Go 不像 Python 那么动态,像一些 Web 框架(例如 Flask),出现异常的时候你甚至可以直接在浏览器或者控制台直接调试,而且还给你保留了上下文信息,简直不要更爽。但是 Go 里面就没有那么舒服了,当然,在平时开发的时候我们可以在本地单步调试,但是,如果程序跑在后台或者常规测试的时候,就需要一些更加方便的工具来支持了,不然我们就只有日志可以用来定位问题了。

在 Go 里面,其实工具没有很多,除了底层一些的 GDB 之外,可能用得比较广的就剩下 delve 了,我之前也介绍过:使用 delve 远程 debug Go 应用,但是比较简单,实操性不那么高,所以这里又稍微扩展了一下,介绍容器以及一个已经在运行的程序可以怎么操作。

容器环境下使用

  1. [root@liqiang.io]# cat Dockerfile
  2. FROM golang:1.17
  3. RUN go get github.com/go-delve/delve/cmd/dlv
  4. ADD . /go/src/github.com/liuliqiang/go-demos
  5. WORKDIR /go/src/github.com/liuliqiang/go-demos
  6. RUN make server
  7. # Final stage
  8. FROM busybox:glibc
  9. COPY --from=0 /lib/x86_64-linux-gnu/libdl.so.2 /lib/libdl.so.2
  10. COPY --from=0 /go/bin/dlv /home/liqiang/
  11. COPY --from=0 /go/src/github.com/liuliqiang/go-demos/.build/server /home/liqiang/
  12. EXPOSE 2345 9000
  13. WORKDIR /home/liqiang
  14. CMD ["/home/liqiang/dlv", "--listen=:2345", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", \
  15. "/home/liqiang/server", "--", "-iden", "grpc-server"]

这样,当你运行起来这个程序之后,你就拥有了一个可以直接调试的服务器了,然后你就可以通过 Goland 远程调试了。

运行时注入

前面容器环境的示例是直接通过 dlv 启动,这个不具有通用性,更常见的场景是一个程序运行,然后发现有一些异常之后,我们再打开调试器注入调试,dlv 当然也是可以如此,但是需要注意的是,你必须地加 gcflags 标记编译你的代码,不然调试器是调试不了的,因为很多信息都被抛弃掉了。

  1. [root@liqiang.io]# go build -gcflags="all=-N -l" -o /home/liqiang/server
  2. [root@liqiang.io]# /home/liqiang/server # 运行程序
  3. [root@liqiang.io]# ps aux | grep server # 获取程序的 pid
  4. root 299664 0.0 0.0 18024 2748 ? Ss 18:41 0:00 bash -c /home/liqiang/server

注入程序 debug:

  1. [root@liqiang.io]# dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient attach 299664

这样调试服务器就算是运行起来了。

Goland 连接远程调试

直接在 Goland 中配置 Host 和 Port 即可,操作步骤为选择项目右上角的设置:

图 1:Config Debug

然后在源代码中选择这个 “Go Remote” 调试器进行调试:

图 2:Add Remote Debugger

在右侧的窗口中输入 Host 和 Port:

图 3:Add Debug Server

参数含义

参数 说明 默认值
--accept-multiclient 可以多个客户端同时接入
--api-version 使用的 API 版本,这个版本一定要匹配 1