# 6.3 调试技巧:使用 GDB 调试 Go 程序
做为新手,熟练掌握一个好的调试工具,对于我们学习语言或者排查问题的时候,非常有帮助。
你如果使用 VS Code 或者 Goland ,可以直接上手,我就不再写这方面的文章了。
其实相比有用户界面的 IDE 调试工具,我更喜欢简单直接的命令行调试,原因有三点:
1. 速度快,个人感觉在 Windows 下速度巨慢
2. 依赖少,在 Linux 服务器上 也能轻松调试
3. 指令简单,我习惯只使用快捷键就能操作
如果你有和我一样的感受和习惯,可以看下今天的文章,介绍的是 GDB 调试工具。
## 1. 下载安装 Go
在 Linux 上进行调试,那咱所以得先安装 Go ,由于第一节里只讲了 Windows 的下载安装,并没有讲到在 Linux 上如何安装。所以这里要先讲一下,已经安装过了可以直接跳过。
首先在 go 下载页面上(https://golang.org/dl/),查看并复制源码包的的下载地址

登陆 linux 机器 ,使用 wget 下载
```shell
$ wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz
```

将下载的源码包解压到 `/usr/local` 目录下,并设置环境变量
```SHELL
[root@localhost ~]# tar -C /usr/local -xzf go1.14.2.linux-amd64.tar.gz
[root@localhost ~]#
[root@localhost ~]# export PATH=$PATH:/usr/local/go/bin
[root@localhost ~]# which go
/usr/local/go/bin/go
[root@localhost ~]#
[root@localhost ~]# go version
go version go1.14.2 linux/amd64
[root@localhost ~]#
```
## 2. 开始进行调试
调试使用的是 GDB (好像要求版本 7.1 + ),使用前,请先确保你的机器上已经安装 GDB
```
[root@localhost code]# which gdb
/usr/bin/gdb
```
准备就绪后,先在目录下写一个测试文件
```go
package main
import "fmt"
func main(){
msg := "hello, world"
fmt.Println(msg)
}
```
然后执行 如下命令进行编译,里面有好多个参数,有疑问的可以自行搜索引擎
```shell
# 关闭内联优化,方便调试
$ go build -gcflags "-N -l" demo.go
# 发布版本删除调试符号
go build -ldflags “-s -w”
```
最后使用 GDB 命令进入调试界面
```shell
# 如果你喜欢这种界面的话,用这条命令
$ gdb -tui demo
# 如果你跟我一样不喜欢不习惯用界面,就使用这个命令
$ gdb demo
```
完整操作如下:

进入 GDB 调试界面后,并不是立即可用,你先需要回车,然后再你敲入几行命令,调试窗口就会出现代码。
```shell
(gdb) b main.main # 在 main 包里的 main 函数 加断点
Breakpoint 1 at 0x4915c0: file /home/wangbm/code/demo.go, line 5.
(gdb) run # 执行进程
Starting program: /home/wangbm/code/demo
Breakpoint 1, main.main () at /home/wangbm/code/demo.go:5
(gdb)
```

## 3. 详解调试指令
要熟练使用 GDB ,你得熟悉的掌握它的指令,这里列举一下
- `r`:run,执行程序
- `n`:next,下一步,不进入函数
- `s`:step,下一步,会进入函数
- `b`:breakponit,设置断点
- `l`:list,查看源码
- `c`:continue,继续执行到下一断点
- `bt`:backtrace,查看当前调用栈
- `p`:print,打印查看变量
- `q`:quit,退出 GDB
- `whatis`:查看对象类型
- `info breakpoints`:查看所有的断点
- `info locals`:查看局部变量
- `info args`:查看函数的参数值及要返回的变量值
- `info frame`:堆栈帧信息
- ` info goroutines`:查看 goroutines 信息。在使用前 ,需要注意先执行 source /usr/local/go/src/runtime/runtime-gdb.py
- `goroutine 1 bt`:查看指定序号的 goroutine 调用堆栈
- 回车:重复执行上一次操作
其中有几个指令的使用比较灵活
比如 l - list,查看代码
```
# 查看指定行数上下5行
(gdb) l 8
# 查看指定范围的行数
(gdb) l 5:8
# 查看指定文件的行数上下5行
l demo.go:8
# 可以查看函数,记得加包名
l main.main
```
把上面的 `l` 换成 `b` ,大多数也同样适用
```
# 在指定行打断点
(gdb) b 8
# 在指定指定文件的行打断点
b demo.go:8
# 在指定函数打断点,记得加包名
b main.main
```
还有 p - print,打印变量
```
# 查看变量
(gdb) p var
# 查看对象长度或容量
(gdb) p $len(var)
(gdb) p $cap(var)
# 查看对象的动态类型
(gdb) p $dtype(var)
(gdb) iface var
# 举例如下
(gdb) p i
$4 = {str = "cbb"}
(gdb) whatis i
type = regexp.input
(gdb) p $dtype(i)
$26 = (struct regexp.inputBytes *) 0xf8400b4930
(gdb) iface i
regexp.input: struct regexp.inputBytes *
```
以上就是关于 GDB 的使用方法,非常简单,可以自己手动敲下体验一下。
**参考文章**
- [go 官方对于 gdb 的使用说明](https://golang.org/doc/gdb)
- [Mac 调试 golang 程序]([https://www.do1618.com/archives/771/mac-gdb-%E8%B0%83%E8%AF%95-golang-%E7%A8%8B%E5%BA%8F/](https://www.do1618.com/archives/771/mac-gdb-调试-golang-程序/))
- 第一章:基础知识
- 1.1 一文搞定开发环境的搭建
- 1.2 五种变量创建的方法
- 1.3 数据类型:整型与浮点型
- 1.4 数据类型:byte、rune与字符串
- 1.5 数据类型:数组与切片
- 1.6 数据类型:字典与布尔类型
- 1.7 数据类型:指针
- 1.8 流程控制:if-else
- 1.9 流程控制:switch-case
- 1.10 流程控制:for 循环
- 1.11 流程控制:goto 无条件跳转
- 1.12 流程控制:defer 延迟语句
- 1.13 流程控制:理解 select 用法
- 1.14 异常机制:panic 和 recover
- 1.15 语法规则:理解语句块与作用域
- 第二章:面向对象
- 2.1 面向对象:结构体与继承
- 2.2 面向对象:接口与多态
- 2.3 面向对象:结构体里的 Tag 用法
- 2.4 学习接口:详解类型断言
- 2.5 学习接口:Go 语言中的空接口
- 2.6 学习接口:接口的三个"潜规则"
- 2.7 学习反射:反射三定律
- 2.8 学习反射:全面学习反射的函数
- 2.9 详细图解:静态类型与动态类型
- 2.10 关键字:make 和 new 的区别?
- 第三章:项目管理
- 3.1 依赖管理:包导入很重要的 8 个知识点
- 3.2 依赖管理:超详细解读 Go Modules 应用
- 3.3 开源发布:如何开源自己写的包给别人用?
- 3.4 代码规范:Go语言中编码规范
- 第四章:并发编程
- 4.1 学习 Go 函数:理解 Go 里的函数
- 4.2 学习 Go 协程:goroutine
- 4.3 学习 Go 协程:详解信道/通道
- 4.4 学习 Go 协程:WaitGroup
- 4.5 学习 Go 协程:互斥锁和读写锁
- 4.7 学习 Go 协程: 信道死锁经典错误案例
- 4.7 学习 Go 协程:如何实现一个协程池?
- 4.8 理解 Go 语言中的 Context
- 4.9 学习一些常见的并发模型
- 第五章:学标准库
- 5.1 fmt.Printf 方法详解
- 5.2 os/exec 执行命令的五种姿势
- 第六章:开发技能
- 6.1 Go 命令:go test 工具详解
- 6.2 单元测试:如何进行单元测试?
- 6.3 调试技巧:使用 GDB 调试 Go 程序
- 6.4 Go 命令: Go 命令指南
- 第七章:暂未分类
- 7.1 20 个学习 Go 语言的精品网站
