首页 > 其他分享 >Go每日一库之174:delve (Go 调试工具)

Go每日一库之174:delve (Go 调试工具)

时间:2023-09-29 21:23:05浏览次数:45  
标签:打印 一库 dlv go Go delve main 断点 调试

简介

Delve 用来调试 Go 语言开发的程序,该工具的目标是为 Go 语言提供一个简单、功能齐全的调试工具。

为什么不推荐 gdb

  • • gdb 对 Go 的调试支持是通过一个 python 脚本文件 src/runtime/runtime-gdb.py 扩展的,功能有限
  • • gdb 只能做到最基本的变量打印,却理解不了 golang 的一些特殊类型,比如 channel,map,slice 等,gdb 原生是无法调适 goroutine 协程的, 因为这是用户态的调度单位,gdb 只能理解线程,所以只能通过 python 脚本的扩展,把协程结构按照链表输出

安装

$ go install github.com/go-delve/delve/cmd/dlv@latest

# 安装完成后查看版本
$ dlv verison

Delve Debugger
Version: 1.20.1
Build: $Id: 96e65b6c615845d42e0e31d903f6475b0e4ece6e 

常用命令

  • dlv attach - 调试进程
  • dlv core - 调试 core
  • dlv debug - 编译并调试当前目录的 main 包,也可以通过参数指定其他包
  • dlv exec - 调试二进制文件
  • dlv test - 编译并调试测试文件

快速开始

我们首先从一个简单的示例程序开始,改程序打印字符串 hello world, 然后结束并退出。

源文件

// main.go

package main

func main() {
    println("hello world")
}

调试源文件

$ dlv debug main.go

# Type 'help' for list of commands.
(dlv) 

# 输入 help 查看参数说明
(dlv) help
The following commands are available:

Running the program:
call ------------------------ Resumes process
...
...
types ---------------------- Print list of types

Type help followed by a command for full documentation.

# 运行程序
(dlv) continue
hello world
Process 3637 has exited with status 0

调试编译后二进制文件

# 编译源文件
$ go build -o main main.go

$ dlv exec ./main

Type 'help' for list of commands.
(dlv) 

# 接下来的步骤和调试源文件的一样,这里不再赘述

调试进程

为了让进程保持在运行状态,我们在程序中加一行休眠代码:

package main

import "time"

func main() {
    time.Sleep(time.Minute)
    println("hello world")
}

调试前先运行程序:

$ go run main.go

# 查看进程 ID
$ ps -ef | grep "go run main.go"

7602 27666  0 21:30 pts/6    00:00:00 go run main.go

# 调试进程
$ dlv attach 7602

Type 'help' for list of commands.
(dlv) 

...

# 1 分钟之后,main.go 并未正常退出,因为当前正在调试
# 输入 continue 继续运行

(dlv) continue 
Process 7602 has exited with status 0

常用调试命令

下列命令是启动 dlv 调试后可用的命令 (也就是当前命令行变为 (dlv) 之后可用)。

运行程序

命令 描述
call 恢复进程,调用函数 (实验阶段)
continue 继续运行程序,直到遇到断点或程序结束
next 单步调试
restart 重新运行
step 单步调试某个函数
step-instruction 单步调试某个 CPU 指令
stepout 从当前函数跳出

操作断点

命令 描述
break 设置断点
breakpoints 打印所有断点
clear 删除断点
clearall 删除所有断点
condition 设置条件断点
on 设置一个断点触发时执行的命令
toggle 打开/关闭 断点

查看变量或内存

命令 描述
args 打印函数参数
display 每次程序停止时打印表达式的值
examinemem 解析给定地址的内存
locals 打印本地变量
print 解析一个表达式
regs 打印寄存器信息
set 设置变量的值
vars 打印包内变量
whatis 打印类型信息

线程 / goroutine 的展示与切换

命令 描述
goroutine 打印或切换 goroutine
goroutines 打印所有 goroutine
thread 切换到指定的线程
threads 打印所有线程信息

调用堆栈

命令 描述
deferred 在 defer 上下文中执行命令
frame 设置当前帧,或在不同的帧上执行命令
stack 打印堆栈信息

其他命令

命令 描述
config 更改配置参数
disassemble 反汇编
dump dump core
exit 结束调试,也可以用 quit

综合示例

最后,我们使用一个的小例子,熟悉下常用的几个命令。

示例程序代码如下:

// main.go

package main

var (
    x = 1024
)

func main() {
    for i := 0; i < 5; i++ {
        println(i)
    }
}

# 开始调试
$ dlv debug main.go

Type 'help' for list of commands.
(dlv)

# 增加断点
(dlv) b main.main
Breakpoint 1 set at 0x45f0c6 for main.main() ./main.go:7

# 查看断点
(dlv) bp
...
Breakpoint 2 (enabled) at 0x45f0c6 for main.main() ./main.go:7 (0)

# 运行程序
(dlv) continue
> main.main() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x45f0c6)
2:
3: var (
4:         x = 1024
5: )
6:
=>   7: func main() {
8:         for i := 0; i < 5; i++ {
9:                 println(i)
10:         }
11: }
12:
# 可以看到,程序停在了设置的断点上

# 打印包变量
(dlv) vars vars main.x
...
main.x = 1024
...

# 单步调试
(dlv) next
> main.main() ./main.go:8 (PC: 0x45f0d4)
3: var (
4:         x = 1024
5: )
6:
7: func main() {
=>   8:         x = 0
9:         for i := 0; i < 5; i++ {
10:                 println(i)
11:         }
12: }
13:

# 再次单步调试
(dlv) next
> main.main() ./main.go:9 (PC: 0x45f0df)
4:         x = 1024
5: )
6:
7: func main() {
8:         x = 0
=>   9:         for i := 0; i < 5; i++ {
10:                 println(i)
11:         }
12: }
13:
14: //timeout := time.After(time.Minute)

# 可以看到,程序停在了循环语句

# 打印包变量
(dlv) vars main.x
main.x = 0

# 再次单步调试
(dlv) next
> main.main() ./main.go:10 (PC: 0x45f0f4)
5: )
6:
7: func main() {
8:         x = 0
9:         for i := 0; i < 5; i++ {
=>  10:                 println(i)
11:         }
12: }
13:
14: //timeout := time.After(time.Minute)
15: //


# 打印本地变量
(dlv) locals
i = 0

# 查看堆栈信息
(dlv) stack
0  0x000000000045f0f4 in main.main
at ./main.go:10
1  0x00000000004358b8 in runtime.main
at /usr/local/go/src/runtime/proc.go:250
2  0x000000000045c0c1 in runtime.goexit
at /usr/local/go/src/runtime/asm_amd64.s:1594


# 打印 goroutine 信息
(dlv) goroutine
Thread 27873 at ./main.go:10
Goroutine 1:
...

# 删除所有断点
(dlv) clearall
Breakpoint 1 cleared at 0x45f0c6 for main.main() ./main.go:7

# 继续执行
(dlv) continue
1
2
3
4
Process 27873 has exited with status 0

# 退出调试
(dlv) exit

与 IDE 集成

Delve 支持以插件的形式集成到主流的 IDE 里面,具体的支持列表请看 这个页面[1]。

常见问题

单点调试总是执行非预期的代码?

一般是被编译器优化了,比如内联会导致 dlv 单步调试无法打印某些变量,解决方法是禁止编译优化。

# 禁用内联和优化 (细节可以阅读引用文章列表)
go run -gcflags "-N -l" main.go

引用链接

[1] 这个页面: https://github.com/go-delve/delve/blob/master/Documentation/EditorIntegration.md
[2] go-delve/delve: https://github.com/go-delve/delve
[3] 如何定位 golang 进程 hang 死的 bug: https://xargin.com/how-to-locate-for-block-in-golang/
[4] Debugging with GDB: https://sourceware.org/gdb/current/onlinedocs/gdb.html/
[5] 100-gdb-tips: https://github.com/hellogcc/100-gdb-tips
[6] 深入 Go 语言 - 11: https://colobu.com/2016/07/04/dive-into-go-11/
[7] WSL2 安装 perf: https://gist.github.com/abel0b/b1881e41b9e1c4b16d84e5e083c38a13

标签:打印,一库,dlv,go,Go,delve,main,断点,调试
From: https://www.cnblogs.com/arena/p/17737365.html

相关文章

  • Go每日一库之173:Pie (高性能、类型安全的slice操作库)
    在Go语言中,对slice和map是我们最常用的数据结构。比如,计算两个切片的交集、差集;判断切片中的元素是否都满足某个条件的等。我推荐大家使用这个包:[elliotchance/pie](https://github.com/elliotchance/pie)。该包封装了对切片和map的常用操作,能满足工作中的大部分需求。比如计算......
  • Go每日一库之172:go-prompt
    简介受python提示工具包的启发,在Go中构建强大的交互式提示一、代码示例packagemainimport( "fmt" "github.com/c-bata/go-prompt")funccompleter(dprompt.Document)[]prompt.Suggest{ s:=[]prompt.Suggest{ {Text:"users",Description:"Store......
  • Go每日一库之133:lo(基于泛型的 Golang lodash 库)
    近日,Go核心开发团队终于宣布了Go1.18正式版本的发布!这是一个大家期待很久的版本!Go1.18包含大量新功能:模糊测试、性能改进、工作区等,以及Go语言开源以来最大的一次语法特性变更——支持泛型!支持泛型后,我们便不再需要写如下冗余的代码:现在只需要简单的一行即可:funcMi......
  • Go每日一库之132:wasm与tinygo
    WASM的概念,这几年还是挺火的,新的语言,比如Rust、Go、Swift等,都对WASM提供支持。相比之下,Go语言的简单性,使得对WASM的支持,使用起来也较简单。本文是目前公开资料中为数不多较完整的教程,希望能对你有帮助。WASM是什么标题说:“Golang中的Wasm太棒了。”,但请用几句话来说......
  • Go每日一库之131:caddy(轻量web服务器)
    一直以来,我都是使用Nginx作为Web服务器,但是配置可以说是非常麻烦了。每次我要新开一个域名,都要先使用acme.sh签发SSL证书,然后再写配置,大概要花上5分钟的时间。曾经想过写个脚本自动完成这些工作,但是苦于对Linux的了解不多,也就作罢了。最近看到了Caddy,一个用Go写的......
  • Go每日一库之130:go-humanize(人性化显示)
    go-humanize是一个「人性化」的Go语言库,人性化的意思不是形容这个Go语言库,而是这个Go语言库实现的功能,它可以把数字、时间、容量等转换为我们人类容易理解的词语,比如硬盘的容量是82854982bytes,我们可不太好理解,但是如果说容量是83M,那就好理解了,go-humanize干的就是这个事情。......
  • Go每日一库之129:promu(Prometheus构建发布工具)
    众所周知,Go语言中打包命令是gobuild。在项目中,你可以单独使用gobuild命令对项目进行编译打包,也可以根据自己的需要,在该命令后加各种参数。prometheus官方为了统一项目(包括prometheus、alertmanager和各种官方的exporter)的编译和打包,开发了promu工具。官方对promu工具......
  • Go每日一库之128:podinfo(k8s微服务模板)
    项目介绍官方Github:PodinfoPodinfo是一个用Go制作的小型web应用程序,它展示了在Kubernetes中运行微服务的最佳实践。它已实现的技术指标(截选自官方README.md):里面每一项技术指标的实现方式,其实都可以拿出来单独讲好久,相关理论也有好多。这里我只是讲针对这个项......
  • Go每日一库之145:MinIO(高性能对象存储)
    1.MinIO简介MinIO是一个基于Go实现的高性能、兼容S3协议的对象存储。它采用GNUAGPLv3开源协议,项目地址是https://github.com/minio/minio,官网是https://min.io。它适合存储海量的非结构化的数据,例如说图片、音频、视频等常见文件,备份数据、容器、虚拟机镜像等等,小......
  • Go每日一库之146:bbs-go(bbs框架)
    概要bbs-go是一款基于Go语言研发的开源、前后端分离、精美小巧、跨平台的社区系统。初期该项目仅用过学习和交流,开源之后越来越多的小伙伴儿开始喜欢和关注他,这也是我长期升级和维护的动力。bbs-go为前后端分离设计,后端接口服务使用简洁的Go语言进行开发,前端页面使用Vue.js进......