如何使用 dlv 结合 Goland 进行程序 debug 调试
如何使用 dlv 结合 Goland 进行程序 debug 调试
相信很多 Golang 的初级玩家不会进行程序的 Debug 定位问题单纯的靠脑子,或者效率很低的不断的添加日志打印,别问我为什么知道的因为我就是这样的,最好最快捷的问题定位方式一定是使用 Debug 打断点调试,这时就引出了本文的主角dlv。
实际上,delve 才是全称,dlv 只是启动命令,如果 VScode,Goland,默认使用的调试器就是基于 delve 的。
安装 dlv
参考官方的安装方法,把 dlv 命令安装在 gopath 的 bin 目录下(需要你把go的bin目录添加到$PATH)
GO
1 2 3 |
git clone https://github.com/go-delve/delve cd delve go install github.com/go-delve/delve/cmd/dlv |
成功安装执行
APPLESCRIPT
1 |
dlv version |
得到:
BASH
1 2 3 |
Delve Debugger Version: 1.7.1 Build: $Id: 3bde2354aafb5a4043fd59838842c4cd4a8b6f0b $ |
进入调试模式有以下几种办法(重要)
- dlv attach pid:对正在运行的进程直接进行调试(pid 为进程id);
- dlv debug:编译源文件并开始调试,这里应和 main 函数位于同一目录,或者指定完整的 main 函数路径
- dlv exec filename:从二进制文件启动调试
这三种模式是调试的重要基础,接下来会通过实际案例来讲解如何使用这三种模式。
直接编译源文件进行本地调试:
使用 dlv debug 命令直接进行源码的编译,以及断点设置,并使用命令查看断点处的参数等信息
比如使用 break 或 b 设置一个断点,使用bp 查看目前打的断点,
dlv 常用的命令总结如下:
命令 | 含义 |
---|---|
b | 设置断点 |
bp | 打印正活动的断点信息 |
clear | 删除断点 |
clearall | 删除所有断点 |
c | 运行直到断点处或程序终止 |
n | 下一步,不会进入函数 |
s | 下一步,会进入函数 |
so | 跳出当前函数 |
args | 查看函数参数 |
locals | 查看所有局部变量 |
list | 打印当前源代码 |
on | 运行到某断点然后执行相应的命令,比如 on 2 list |
bt | 打印当前调用栈 |
exit | 退出 |
goroutines (grs) | 列出所有的 goroutines |
goroutine (gr) | 查看当前的 goroutine |
这样做是十分麻烦的,这个介绍只是让我们理解dlv是如何工作的。一般我们都会结合 idea 进行界面化的调试,那么接下来我们以goland为例子进行 debug 调试。
做一个简单的 demo 代码可见这里:
GO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
const timeoutOpenAPIQuit = 2 * time.Minutefunc main(){ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)defer stop()g, ctx := errgroup.WithContext(ctx)e := gin.Default()e.GET("/api/v1/hello",Hello)g.Go(func() (err error) {defer func() {err = WrapPanicErr(err, recover())}()err = ServerRun(ctx, ":8080", e, timeoutOpenAPIQuit)if err != nil {err = fmt.Errorf("failed to run open api serv: %w", err)}return})if err := g.Wait(); err != nil {logrus.Error(err)}logrus.Info("all services are down")}func Hello(c *gin.Context){name := c.Query("name")str := fmt.Sprintf("hello %s ! \n",name)fmt.Println(str)c.String(http.StatusOK,str)} |
很简单的监听 8080 端口,给出一个 hello 的请求回应方法。
使用gland 进行debug模式编译:
出现如下窗口:
在你需要的地方打上断点:
尝试请求后跳到你的断点处,即可进行操作调试:
使用 Goland 配合 dlv 调试二进制方式进行debug
对你的程序进行编译,-gcflags 为必须添加的。
BASH
1 |
go build -o hello -gcflags "all=-N -l" |
使用dlv启动你的程序
BASH
1 |
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./hello |
配置 Goland 进行调试程序连接 Run -> Debug -> 0 EditConfiguration
添加一个 Go Remode : 命名随意,Host 和 Port 配置你使用dlv 启动的程序监听
点击 Debug 出现以下界面表示连接成功:
尝试去访问直接回跑到断点处:
使用dlv 进行 Docker 镜像远程调试
相信很多小伙伴都遇到过本地环境的数据不够丰富,在本地自测完全没有问题,一到测试环境居然凉了,这时候使用测试环境的debug就很重要了,我们可以以使用 dlv 的二进制文件启动调试的方式进行远程的镜像调试。
方式一:使用 dlv 入侵 docker 中正在执行的进程 ID
准备:
这种方式的好处是不用破坏部署真实环境使用的 dockerfile 调试完成结束掉dlv 不影响线上的部署环境的正常运行,不好的地方就是比较麻烦,
Dockerfile:
DOCKERFILE
1 2 3 4 5 6 |
FROM alpineCOPY hello /usr/local/hello WORKDIR /usr/local/CMD ["./hello"] |
这个文件是 docker 镜像启动后的执行文件,即使用 dlv 侵入docker 中运行的进程id,该文件放入deploy文件夹下。
startdlv.sh:
BASH
1 2 3 |
#!/bin/bashdlv debug --headless --listen=:2345 --api-version=2 --accept-multiclient attach $PID |
这个文件用来在服务器上执行,把docker 镜像压缩包加载出来
install.sh:
BASH
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bashVERSION=%%VERSION%%echo VERSION docker load < dlv_${VERSION}.tar echo "加载完毕。。。"docker run -d -p 8080:8080 --security-opt=seccomp:unconfined dlv:${VERSION} echo "成功跑起。。。" |
Makefile:
MAKEFILE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
DlVVERSION=$(v)IMAGENAME_CERTMANAGER=dlv:$(DlVVERSION)_buildEnv=CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64# 构建程序的二进制文件 注意-gcflags 一定要添加 build:@$(_buildEnv) go build \-o hello -gcflags "all=-N -l"@echo "hello build done"# 构建 docker 镜像 docker:build@docker build -t dlv:$(DlVVERSION) ./@echo " docker build done"# 压缩 docker 镜像,注入docker压缩包, install.sh 到 deploy 文件夹,deploy文件夹下还要有上文的 startdlv.sh 文件 save:docker@sed "s/%%VERSION%%/$(DlVVERSION)/g" install.sh > deploy/install.sh@chmod +x deploy/install.sh@docker save $(IMAGENAME_CERTMANAGER) > deploy/dlv_$(DlVVERSION).tar@scp -r deploy root@你的服务器IP:~/@echo "save done" |
看下几个文件的位置,有助于理解:
搞起
执行 v=1.0.0 make save :
可以看到install.sh , dlv_1.0.0.tar , startdlv.sh 都远程拷贝到了我的服务器(忽略那个dlv_1.0.1.tar):
首先执行install.sh:
此时我们的容器已经成功跑起来了,接下来获取容器中执行程序hello的PID:
可以看到是 19184,改掉我们的 startdlv.sh 中 $PID 为19184,执行该文件:
可以看到dlv 已经入侵到了此进程并监听在2345端口,按照上文的goland添加Go Remode IP 为我的服务器IP,端口同样为2345,连接该dlv 程序(我命名为AliyunHello) :
此时就完成了远程debug的部署工作,我们访问一下我的服务器上的hello程序:
程序hold住并且进入了断点调试状态,很棒成功了!这样就可以愉快的调试了。
方式二:使用 dlv 直接在容器中执行 hello 程序
准备:
这种方式的好处是方便,直接跑起来 docker 即可进行调试,但它一直处在调试状态,是不可与你的测试环境并行的,你需要新建一套环境,而且 docker 中是需要go环境的,导致镜像变得很大。(说在前面,这种方法我没成功。。。找到问题我会更新的)
Dockerfile:
DOCKERFILE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
FROM golang:1.17.1-alpine3.14 as buildRUN set -eux && \apk update && \apk add --no-cache git curl && \go get -u github.com/go-delve/delve/cmd/dlv && \go build -o /usr/local/bin/dlv github.com/go-delve/delve/cmd/dlvCOPY hello /usr/local/hello WORKDIR /usr/local/CMD ["dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./hello"] |
这个文件用来在服务器上执行,把docker 镜像压缩包加载出来
install.sh:
BASH
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bashVERSION=%%VERSION%%echo VERSION docker load < dlv_${VERSION}.tar echo "加载完毕。。。"docker run -d -p 8080:8080 -p 2345:2345 --security-opt=seccomp:unconfined dlv:${VERSION} echo "成功跑起。。。" |
Makefile:
MAKEFILE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
DlVVERSION=$(v)IMAGENAME_CERTMANAGER=dlv:$(DlVVERSION)_buildEnv=CGO_ENABLED=0 GO111MODULE=on GOOS=linux GOARCH=amd64# 构建程序的二进制文件 注意-gcflags 一定要添加 build:@$(_buildEnv) go build \-o hello -gcflags "all=-N -l"@echo "hello build done"# 构建 docker 镜像 docker:build@docker build -t dlv:$(DlVVERSION) ./@echo " docker build done"# 压缩 docker 镜像,注入docker压缩包, install.sh 到 deploy 文件夹,deploy文件夹下还要有上文的 startdlv.sh 文件 save:docker@sed "s/%%VERSION%%/$(DlVVERSION)/g" install.sh > deploy/install.sh@chmod +x deploy/install.sh@docker save $(IMAGENAME_CERTMANAGER) > deploy/dlv_$(DlVVERSION).tar@scp -r deploy root@你的服务器IP:~/@echo "save done" |
这个方式就不需要找到进程ID 然后再进行dlv 入侵了,直接跑起 docker 就好,注意把2345端口映射出来。这种方式我失败了,每次在docker run 的时候都会报 no such file :
不知道为什么,可能是环境问题,我进入容器内部,执行dlv –listen=:2345 –headless=true –api-version=2 –accept-multiclient exec ./hello 就可以,但在dockerfile 里执行这个命令就会报no such file , 没有找到问题的原因,之后找到原因会更新的,推荐使用第一种方式吧,我觉得比较好,虽然要获取进程ID。
文章作者: Mark's Blog
文章链接: 如何使用 dlv 结合 Goland 进行程序 debug 调试 | Mark's Blog
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mark's Blog!
如何使用 dlv 结合 Goland 进行程序 debug 调试相关推荐
- Eclipse Java程序debug调试
1.首先要设置断点:在代码里需要调试的地方,鼠标双击代码行号的左边,再次双击即可取消断点. 2.启动服务开始调试: 方法一,右键单击主函数方法进行选择Debug. 方法二,选择左上角任务栏小瓢虫进行选 ...
- idea调试怎么跳出循环_使用IDEA的Debug调试功能,查看程序的运行过程
Debug追踪,使用IDEA的断点调试功能,查看程序的运行过程 知乎视频www.zhihu.com 1. 在有效代码行,点击行号右边的空白区域,设置断点,程序执行到断点将停止,我们可以手动来运行程序 ...
- 微信小程序如何debug调试
在小程序程序中调用接口地址后加:?XDEBUG_SESSION_START=1 即可进入debug调试模式.前提是接口地址得是本地环境. 效果:小程序调接口时会直接跳转到PhpStorm对应接口打断点 ...
- 为什么我的程序debug版本运行没有问题,而release版本总是报错?
To 楼主 ,我在另外一个类似的贴子作了回答 http://expert.csdn.net/Expert/topic/2955/2955693.xml?temp=.3361933 ...
- 恕我直言,IDEA 的 Debug 调试,你可能只用了 10%
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:bojiangzhou cnblogs.com/chiang ...
- debug调到循环最后_Java入门(7)——循环和debug 调试
循环: while 循环: 格式: int i = 0; ① //初始化条件 while(i < 10) { ② //判断条件 System.out.println(i); ④ //循环 ...
- 【Flutter】Flutter 调试 ( 调试回退功能 | Debug 调试中查看变量的方式 | 控制台信息 )
文章目录 一.调试回退功能 二.Debug 调试中查看变量的方式 三.Debug 控制台信息 四.相关资源 一.调试回退功能 在调试过程中 , 经常错过关键位置的调试 , 如没有进入关键方法进行调试 ...
- 【Intellij IDEA系列】IDEA的Debug调试技巧
以前一直是使用Eclipse进行开发的,突然使用idea进行开发,许多习惯都一时改变不过来,同时对于idea中的许多界面操作和快捷使用起来都特别变扭,这里总结一下debug调试时的一些使用方法. 精简 ...
- 浅析IBM i上C/C++应用程序编译调试方法
软件调试对于编程人员来说有着非常重要的意义,应用程序功能的开发和完善是在不断调试中完成的.本文围绕IBM i上C/C++语言的编译调试问题进行分析和介绍.主要介绍两方面的内容:一是system i上C ...
最新文章
- 阿里云K8S容器服务的使用
- 到这个年纪为什么我还要开始学习理解参与区块链?
- 在RedHat上安装gcc,java 和 eclipse-rcp
- python第一单元笔记_Python 初学笔记 - 第一章-列表
- php-fpm进程数优化方法
- Linux命令行之逗趣无极限
- java void eat_java匿名内部类
- 阿里Java面试题剖析:为什么使用消息队列?消息队列有什么优点和缺点?
- vue中@符号表示什么意思?
- SSIS技巧--优化数据流缓存
- 牛客 - 牛牛的最大兴趣组(思维+数论)
- [java]常用类型转化
- el-table跨页选中
- c语言字符串匹配函数index,C语言(函数)学习之index、rindex
- Selenium Web 自动化 - 项目实战(三)
- Oracle数据库备份和恢复配置详解
- 强悍的 Linux —— grep 与 egrep
- IT书籍汇总下载(python_c++_java_android_网络安全)等-持续更新
- USB协议介绍二 传输
- 【STM32F429开发板用户手册】第2章 STM32F429的开发环境搭建
热门文章
- mysql 1093 you can_mysql中错误:1093-You can’t specify target table for update in FROM clause的解决方法...
- NetworkX 算法列表
- python Pygame的具体使用讲解
- 不容错过的精美的树形表格treegrid在项目里面使用总结
- 本地局域网内添加DNS解析的方法
- 【总结】反欺诈(Fraud Detection)中所用到的机器学习模型
- 1017. Staircases
- Java利用Set集合去重复
- Vgg16 + Unet 介绍
- linux下wifi实现