开发一看就会,一学就废的Debug:delve大杀器

1,710 阅读4分钟

image.png

现在北京时间: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的区别?

image.png
有一个 http server,利用pprof,你可以轻松的通过 /debug/pprof/ 请求来获取目前程序的调用情况
但是,如果这个时候,程序hang住了呢?你的 /debug 也就打不开了
那么这个时候,就无法通过pprof来进行debug了,瞬间是不是感觉两眼一摸黑
这个时候,delve就该出场了,解决pprof无法解决的问题

delve 的原理

可以大致理解为:通过系统调用的方式,来控制进程
所以,当程序hang住,pprof也被hang住了,而delve依然可以有效debug的关键所在

delve 的使用

可以通过如下3种方式来进行

  1. debug
    go debug {main.go}
  2. attach
    go attach {pid}
  3. 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的介绍 image.png

这里我们主要来介绍dlv attach的方式,api的模式利用的也是attach模式
dlv attach {pid} 则会直接进入到调试模式

我们先来看看attach提供哪些能力

image.png 从标题可以看的出来,大致分为6类

  • 运行程序
  • 打断点
  • 看参数和内存
  • 监听goroutine
  • 查看stack和frames
  • 其他

举个栗子:
这个时候程序hang住了,你肯定需要看看运行情况,跑了哪些东西
那么,我们就可以输入goroutines 来查看所有goroutine的运行列表,当然,我们熟练之后可以直接输入grs(缩写)来获取

image.png 这里,我们可以看到一共有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

image.png

这里我们可以通过frame {frame id} 来进入

image.png

实际,在生产的环境中,你需要借助更多的条件,才能定位问题
例如需要通过“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:将源码转译成内存地址。

大致的流程如下: image.png

dlv remote通讯

dlv 在remote模式下,采取rpc的通讯协议
有一个Client的抽象,通过 RPCClient来实现,包含了dlv -h中看到的相关方法

image.png

文章的末尾,插播一个打印堆栈来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…