排除应用程序故障是比较复杂的,特别是处理像 Go 这样的高并发语言。它更容易在具体位置使用 print 打印语句来确定程序状态,但是这个方法很难根据条件发展去动态响应你的代码。
调试器提供了一个强大得令人难以置信的故障排除机制。添加排除故障的代码可以巧妙地影响到应用程序该如何运行。调试器可以给正在迷茫的你更精确的看法。
已经有许多 Go 的调试器存在了,其中一些调试器的不好之处是通过在编译时注入代码来提供一个交互终端。gdb 调试器则允许你调试已经编译好的二进制文件,只要他们已经与 debug 信息连接,并不用修改源代码。这是个相当不错的特性,因此你可以从你的部署环境中取一个产品然后灵活地调试它。你可以从Golang 官方文档中阅读更多关于 gdb 的信息,那么这篇指南将简单讲解使用 gdb 调试器来调试 Go 应用程序的基本用法。
这儿会宣布一些 gdb 的最新更新,最特别的是替换 -> 操作为 . 符号来访问对象属性。记住这儿可能在gdb 和 Go 版本中有细微改变。本篇指南基于 gdb 7.7.1和go 1.5beta2。
开始 gdb 调试为了实验 gdb 我使用了一个测试程序,完整的源代码可以在gdb_sandbox_on_Github上查看。让我们从一个非常简单的程序开始吧:
package main
import (
"fmt"
)
func main() {
for i := 0; i < 5; i++ {
fmt.Println("looping")
}
fmt.Println("Done")
}
我们可以运行这段代码并看到它输出内容的和我们想象的一样:
$ go run main.go
looping
looping
looping
looping
looping
Done
我们来调试这个程序吧。首先,使用 go build 编译成二进制文件,接着使用这个二进制文件的路径做为参数运行 gdb。根据你的设定,你也可以使用 source 命令来获取 Go 运行时(Go runtime)的支持。现在我们已经在 gdb 的命令行中了,我们可以在运行我们的二进制文件前为它设置断点。
$ go build -gcflags "-N -l" -o gdb_sandbox main.go
$ ls
gdb_sandbox main.go README.md
$ gdb gdb_sandbox
....
(gdb) source /usr/local/src/go/src/runtime/runtime-gdb.py
Loading Go Runtime support.
第一关,我们在 for 循环里面设置一个断点(b)来查看执行每次循环时我们的代码会各有什么状态。我们可以使用print(p)命令来检查当前内容的一个变量,还有 list(l)和 backtrace(bt)命令查看当前步骤周围的代码。程序运行时可以使用 next(n)执行下一步或者使用 breakpoint(c)执行到下一个断点。
(gdb) b main.go:9
Breakpoint 1 at 0x400d35: file /home/bfosberry/workspace/gdb_sandbox/main.go, line 9.
(gdb) run
Starting program: /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/gdb_sandbox Breakpoint 1, main.main () at
/home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9
9 fmt.Println("looping")
(gdb) l
4 "fmt"
5 )
6
7 func main() {
8 for i := 0; i < 5; i++ {
9 fmt.Println("looping")
10 }`
11 fmt.Println("Done")
12 }
(gdb) p i
$1 = 0
(gdb) n
looping
Breakpoint 1, main.main () at
/home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9
9 fmt.Println("looping")
(gdb) p i
$2 = 1
(gdb) bt
# 0 main.main () at /home/bfosberry/.go/src/github.com/bfosberry/gdb_sandbox/main.go:9
我们的断点可以设置在关联文件的行号中、GOPATH里的文件的行号或一个包里的��数。如下也是一个有效的断点:
(gdb) b github.com/bfosberry/gdb_sandbox/main.go:9
(gdb) b 'main.main'
Structs
我们可以用稍微复杂一点的代码来实例演示如何调试。我们将使用f函数生成一个简单的pair,x和y,当x相等时y=f(x),否则=x。
type pair struct {
x int
y int
}
func handleNumber(i int) *pair {
val := i
if i%2 == 0 {
val = f(i)
}
return &pair{
x: i,
y: val,
}
}
func f(int x) int {
return x*x + x
}
也可以在循环中改变代码来访问这些新函数。
p := handleNumber(i)
fmt.Printf("%+v\n", p)
fmt.Println("looping")