

dlv命令行的远程调试 golang 进程步骤(包含容器进程)
source link: https://zhangguanzhang.github.io/2021/07/20/dlv-remote/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

记录下 dlv 的远程调试,建议不要在代码里加 fmt 去调试。不谈 goland 啥的远程调试,本文章目前只写 dlv 的命令行配合远端调试。
一些前提须知
符号链接路径
package main
import (
"fmt"
"os"
)
func main() {
f, _ := os.Open("asdasdasd")
fmt.Println(f.Name())
}
上面代码你编译了后,在其他机器上运行,panic 的堆栈信息会是你机器上的路径信息,路径信息是保留的,例如下面的是我在 windows 上交叉编译仍到 Linux 上执行的:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x497d50]
goroutine 1 [running]:
os.(*File).Name(...)
D:/Install/Go/src/os/file.go:55
main.main()
D:/github_dir/go/dlv-test/main.go:10 +0x50
可以通过下面的编译选项去掉(项目路径也就是$PWD
显示的不要带空格,否则会编译报错):
go build -gcflags="all=-trimpath=$PWD" -asmflags "all=-trimpath=$PWD" main.go
使用上面的参数编译完后的,这里注意下下面的 D:/Install/Go
,后面文章会用到。
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x649857]
goroutine 1 [running]:
os.(*File).Name(...)
D:/Install/Go/src/os/file.go:55
main.main()
main2.go:10 +0x57
dlv 命令行远端调试
很多时候线上机器都是 Linux ,源码在本地,而且机器上不一定会有 golang,也就是说 dlv debug main.go
满足的条件实际上并不多。这里主要讲下 dlv exec
和 dlv attach
。
exec 是用 dlv 运行编译完的二进制文件,golang 关闭 cgo 编译的就是静态编译了,运行机器上有无 golang 都能运行。attach 是调试一个已经运行的进程。
这里我是在linux上运行一个编译好的 gin-demo,然后在目标机器上准备一个 dlv 的二进制文件,用 dlv attach
开一个 server,然后我们在本地有源码的 windows 上 dlv connect
连上去调试。
这里以我本地的 windows 和 远端的 Linux 做测试。windows 上和 linux 上都已经安装了 dlv 了,并把路径加到 PATH 里了,windows 我下了 git bash。
根据实际开发流程来,windows 上项目编辑文件 main.go
,代码随便写的,不要吐槽。
package main
import "github.com/gin-gonic/gin"
func main() {
var count int
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
count++
c.JSON(200, gin.H{
"message": "pong",
"count": count,
})
})
r.Run()
}
go mod init test
然后推送到代码仓库上,手动或者 robot 触发 CI 构建
ci 的编译
Dockerfile 如下,为了避免 dlv 调试出现 Warning: debugging optimized function
,我们需要在 -gcflags=
里加 -N -l
,为了防止变量被 Dockerfile
解析,$PWD
全部换成了pwd
。${LDFLAGS}
是注入一些 version 信息之类的,可以看我文章git工作流下golang项目的version信息该如何处理
FROM golang:1.16.6 as mod
LABEL stage=mod
ARG GOPROXY=https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,https://goproxy.io,direct
WORKDIR /root/myapp/
COPY go.mod ./
COPY go.sum ./
RUN go mod download
FROM mod as builder
LABEL stage=intermediate0
ARG LDFLAGS
ARG GOARCH=amd64
COPY ./ ./
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${GOARCH} \
go build -o gin-demo \
-gcflags="all=-trimpath=`pwd` -N -l" \
-asmflags "all=-trimpath=`pwd`" \
-ldflags "${LDFLAGS}" main.go
FROM alpine:3.13.5
LABEL MAINTAINER="zhangguanzhang [email protected]" \
URL="https://github.com/zhangguanzhang/xxxx"
COPY --from=builder /root/myapp/gin-demo /gin-demo
ENV TZ Asia/Shanghai
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk update && \
apk add --no-cache \
curl \
ca-certificates \
bash \
iproute2 \
tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo Asia/Shanghai > /etc/timezone && \
if [ ! -e /etc/nsswitch.conf ];then echo 'hosts: files dns myhostname' > /etc/nsswitch.conf; fi && \
rm -rf /var/cache/apk/* /tmp/*
ENTRYPOINT ["/gin-demo"]
然后编译完了,模拟下 CD 部署在 Linux 机器上运行
docker run --name t1 --rm -p 8080:8080 test
这里我用容器演示,虽然容器的 pid namespaces 隔离了,但是实际上容器里的所有进程还是会在宿主机上有 pid 对应的。
$ ps aux | grep gin-dem[o]
root 15598 0.0 0.1 708540 5128 ? Ssl 21:10 0:00 /gin-demo
Linux 上的准备
拷贝 Linux 的 dlv 可执行文件放到目标机器的 PATH 下,推荐按照 LFS 的规范放 /usr/local/bin/ 下,然后执行下面的
dlv attach $(pgrep gin-demo) --listen=:2345 --headless=true --log=true \
--log-output=debugger,debuglineerr,gdbwire,lldbout,rpc --accept-multiclient --api-version=2
$(pgrep gin-demo)
也可以换成具体的 pid 。
windows 上开始调试
windows的 dlv 也要放在 PATH 里,或者绝对路径运行 dlv。要在源码目录运行,因为 docker build
里容器编译的时候去掉源码的前缀路径,所以 dlv 调试按照链接找文件都会按照相对路径找,所以我们需要在windows 上的源码路径里执行。
dlv connect 192.168.2.111:2345
连上之后,没有 Linux 的(dlv)
这样的 format,这是 dlv 引用的库的 bug,只要出现了Type 'help' for list of commands.
说明成功连上了。
执行下 sources
看下链接路径:
/usr/local/go/src/...
/usr/local/go/src/...
/usr/local/go/src/..
...
main.go
前面的是 golang 的内部包,最后面的是我们项目路径的,因为我们 build 的时候 trim 掉前缀路径了,所以看着都是相对路径。/usr/local/go
是 golang 的 docker 镜像里的 GOROOT
,我们本地 windows 的 go root 可以通过 cmd 或者 git bash 里使用下面的命令查看:
$ go env GOROOT
D:\Install\Go
源码包已经相对路径存在了,但是 golang 的自带包还没有,我们需要配置映射路径,接着在 dlv 的交互里执行下面的:
config substitute-path /usr/local/go D:\Install\Go
然后我们在 main.go:10
打个断点(),然后 c
执行:
b main.go:10
Breakpoint 1 (enabled) set at 0x891be0 for main.main.func1() main.go:10
c
然后我们触发下请求,例如在 Linux 上直接用 curl 触发请求:
curl localhost:8080/ping
命令会阻塞住,因为 server 端没回复,我们回到我们的 windows 上,界面有打印下面的。
> main.main.func1() main.go:10 (hits goroutine(33):1 total:2) (PC: 0x891be0)
5: func main() {
6: var count int
7: r := gin.Default()
8: r.GET("/ping", func(c *gin.Context) {
9: count++
=> 10: c.JSON(200, gin.H{
11: "message": "pong",
12: "count": count,
13: })
14: })
15: r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
我们打印下 count 的值
p count
1
然后 c,让代码 continue 。然后能看到我们的 Linux 上的 curl 命令收到 server 端的请求了。
dlv 里常用的命令如下:
config max-string-len 1000 # 配置打印变量的输出长度,防止被折叠显示
b file.go:数字 # 文件行数打断点
c # 执行到下一个断点
so # 直接执行完所在的当前函数
s # 单步执行
n 数字 # 执行到后面的n行那里
dlv 运行的时候会读取配置文件路径,像上面的config max-string-len 1000
和 config substitute-path /usr/local/go D:\Install\Go
都可以配置在文件里。
Recommend
-
14
【导语】:Python实现的HTTP请求命令行客户端,我理解为curl的Python版本,但是提供了更友好,更易使用的命令及选项。 简介 HTTPie是使用Python实现的HTTP命令行工具,提供了更人性化、交互性更好的命令和选项,可以用来做测试、调试以及与...
-
6
FFmpeg 命令行工具 - 高级特性、Web 音视频、调试和测试 这篇文章讲解 FFmpeg 命令行工具中使用高级特性、Web 音视频、调试和测试相关的功能。
-
10
loguru - 打印日志(特别是异常的详细信息) loguru本身是一个日志库,跟logging库差不多,但是它还支持更丰富的一些功能,其中一项就是它可以显示抛出异常时的调用栈和各参数值。 使用方法很简单,我们只用使用loguru.logger.catch
-
9
Android 命令行调试 C/C++ 程序 传统方式调试 NDK 开发的程序比较麻烦,先要编译成 JNI,又要导出 java接口,还要再写一个 java 工程,改一个地方又要连续改几处,这样效率是很低的。最频繁使用的关键工作路径(编译/调试环节)如果能极致简...
-
9
SaaS产品的交互设计流程包含哪些步骤?(七)
-
5
10张数据分析知识脑图,包含学习步骤和工具合集,建议收藏
-
13
【tryhackme】Skynet(远程文件包含漏洞,cron任务通配符提权)发布于 25 分钟前┌──(root💀kali)-[~] └─# nmap -sV 10.10.166.193...
-
9
phpmyadmin 4.8.1 远程文件包含漏洞(CVE-2018-12613) 创建时间:2019-11-19 01:33 字数:541 阅读:8...
-
9
命令行 API 调试工具: HTTPie & jq Jan 20, 2015 HTTPie: a CLI, cURL-like tool for humans.
-
3
远程调试 golang 应用 2021-04-20 2022-05-12...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK