现在北京时间:2021-07-25 02:18
为了迎接“烟花”(台风)的到来,笔者决定痛下杀手,呕心沥血,说一说delve这玩意
首先介绍Debug工具
- 调试器: pprof、delve
- IDE:goland、vscode
- 等等
什么是delve?
相对pprof,goland而言,delve可能相对“知名度”就没这么大了
那,delve到底是什么呢?
是一个golang的调试器,可以理解为golang的专属gdb
如果不知道gdb是什么,没有关系
那我会跟你说goland调试的背后用的也是delve
这样,想必就不会这么陌生了
所以,delve是一款golang的调试器
这里我们引入官方的解释
Delve is a debugger for the Go programming language. The goal of the project is to provide a simple, full featured debugging tool for Go. Delve should be easy to invoke and easy to use. Chances are if you're using a debugger, things aren't going your way. With that in mind, Delve should stay out of your way as much as possible.
delve跟pprof的区别?
有一个 http server,利用pprof,你可以轻松的通过 /debug/pprof/ 请求来获取目前程序的调用情况
但是,如果这个时候,程序hang住了呢?你的 /debug 也就打不开了
那么这个时候,就无法通过pprof来进行debug了,瞬间是不是感觉两眼一摸黑
这个时候,delve就该出场了,解决pprof无法解决的问题
delve 的原理
可以大致理解为:通过系统调用的方式,来控制进程
所以,当程序hang住,pprof也被hang住了,而delve依然可以有效debug的关键所在
delve 的使用
可以通过如下3种方式来进行
- debug
go debug {main.go} - attach
go attach {pid} - api
./dlv --listen=127.0.0.1:26953 --headless=true --api-version=2 --check-go-version=false --only-same-user=false attach {pid}
我们来dlv -h 看看dlv的介绍
这里我们主要来介绍dlv attach的方式,api的模式利用的也是attach模式
dlv attach {pid} 则会直接进入到调试模式
我们先来看看attach提供哪些能力
从标题可以看的出来,大致分为6类
- 运行程序
- 打断点
- 看参数和内存
- 监听goroutine
- 查看stack和frames
- 其他
举个栗子:
这个时候程序hang住了,你肯定需要看看运行情况,跑了哪些东西
那么,我们就可以输入goroutines 来查看所有goroutine的运行列表,当然,我们熟练之后可以直接输入grs(缩写)来获取
这里,我们可以看到一共有5个goroutine在运行,实践的产线中肯定不会是这么简单,为了表述,这里笔者跑了个简单的demo,循环time.Sleep 2s;
可以很清晰的看到 goid =1 是笔者的用户goroutine,而其他4个为gc goroutine
这里普及一个概念,所有的goroutine都有一个id,runtime要进行管理的,这个id就称之为goid。
runtime的管理,也就是goroutine的调度,就是我们常说的GMP(并发调度模型),这个后续笔者会出一篇来专门说明
那么我们可以输入 gr {goid}来进入到指定的goroutine中去
通过stack 能够看到堆栈信息,如果要看detail,可以附带参数 -full
这里我们可以通过frame {frame id} 来进入
实际,在生产的环境中,你需要借助更多的条件,才能定位问题
例如需要通过“next” 查看下一步代码
例如需要通过“args” 来查看参数情况
例如需要通过“dump” 来dump出程序的情况
等等
你只有熟练掌握和了解dlv和相关的基本原理,才能更好的做到debug
dlv 源码解析
dlv 架构
dlv 大致上可以划分为3层
1. UI Layer : 接受用户输入的commands 。
通过 github.com/spf13/cobra 来实现CLI,包括k8s用的也是cobra来实现的CLI
2. Target Layer : 实际控制调试过程,读取目标进程的CPU 寄存器。 从操作系统接收调试事件(例如新进程)Target Layer 不记录源码相关信息。
在delve 中提供了对target layer 的三种实现方式:
- **pkg/proc/native:** 使用os API call 控制目标进程, Linux 和Windows 的默认实现方式。
- **pkg/proc/core:** 读取linuxs 的core dump 文件。
- **pkg/proc/gdbserial:** 通过TCP/IP 连接服务器。采用的协议叫做 GDB Remote Serial Protocol——GDB的标准远程通信协议
3. Symbol Layer:将源码转译成内存地址。
大致的流程如下:
dlv remote通讯
dlv 在remote模式下,采取rpc的通讯协议
有一个Client的抽象,通过 RPCClient来实现,包含了dlv -h中看到的相关方法
文章的末尾,插播一个打印堆栈来debug的小技巧
日常,如果我们在遇到error的时候,想要获取stack信息,但是又不能让程序panic
那么可以试试runtime包
import runtime/debug
debug.Stack() 能够获取你想要的堆栈信息 但是,现在已经有不错的error 包来处理了
你学废了嚒?
“烟花”(台风)要来了,明天还会远么?
相关文档
dlv 源码:github.com/go-delve/de…
dlv 使用说明:github.com/go-delve/de…
dlv 插件和GUI:github.com/go-delve/de…
dlv api:github.com/go-delve/de…