英文原文:Using the gdb debugger with Go
排除应用程序故障是比较复杂的,特别是处理像 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上查看。让我们从一个非常简单的程序开始吧:
我们可以运行这段代码并看到它输出内容的和我们想象的一样:
我们来调试这个程序吧。首先,使用 go build 编译成二进制文件,接着使用这个二进制文件的路径做为参数运行 gdb。根据你的设定,你也可以使用 source 命令来获取 Go 运行时(Go runtime)的支持。现在我们已经在 gdb 的命令行中了,我们可以在运行我们的二进制文件前为它设置断点。
|
一曲
|
第一关,我们在 for 循环里面设置一个断点(b)来查看执行每次循环时我们的代码会各有什么状态。我们可以使用print(p)命令来检查当前内容的一个变量,还有 list(l)和 backtrace(bt)命令查看当前步骤周围的代码。程序运行时可以使用 next(n)执行下一步或者使用 breakpoint(c)执行到下一个断点。
我们的断点可以设置在关联文件的行号中、GOPATH里的文件的行号或一个包里的函数。如下也是一个有效的断点:
Structs我们可以用稍微复杂一点的代码来实例演示如何调试。我们将使用f函数生成一个简单的pair,x和y,当x相等时y=f(x),否则=x。
也可以在循环中改变代码来访问这些新函数。
因为我们需要调试的是变量 y。我们可以在y被设置的地方放置断点然后单步执行。可以使用 info args 查看函数的参数,在 bt 之前可以返回当前回溯。
|
一曲
|
因为我们在变量 y 是在函数 f 中被设定的这样一个条件下,我们可以跳到这个函数的上下文并检查堆区的代码。应用运行时我们可以在一个更高的层次上设置断点并检查其状态。
如果我们在这个断点处继续住下走我们将越过在这个函数中的断点1,而且将立即触发在 HandleNumer 函数中的断点,因为函数 f 只是对变量 i 每隔一次才执行。我们可以通过暂时使断点 2不工作来避免这种情况的发生。
我们也可以分别使用 clear 和 delete breakpoint NUMBER 来清除和删除断点。动态产生和系住断点,我们可以有效地在应用流中来回移动。 |
木兰宿莽
|
Slices and Pointers
|
1 2 3 4 5 6 7 | var pairs []*pair for i := 0; i < 10; i++ { p := handleNumber(i) fmt.Printf( "%+v\n" , p) pairs = append(pairs, p) fmt.Println( "looping" ) } |
现在我们来检查生成出来的 slice 或 pairs,首先我们用转换成数组来看一下这个 slice。因为 handleNumber 返回的是一个 *pair 类型,我们需要引用这个指针来访问 struct(结构)的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ( gdb ) b main.go:18 Breakpoint 1 at 0x400e14: file /home/bfosberry/ .go /src/github .com /bfosberry/gdb_sandbox/main .go, line 18. ( gdb ) run Starting program: /home/bfosberry/ .go /src/github .com /bfosberry/gdb_sandbox/gdb_sandbox &{x:0 y:0} Breakpoint 1, main.main () at /home/bfosberry/ .go /src/github .com /bfosberry/gdb_sandbox/main .go:18 18 fmt .Println( "looping" ) ( gdb ) p pairs $1 = []*main.pair = {0xc82000a3a0} ( gdb ) p pairs[0] Structure has no component named operator[]. ( gdb ) p pairs.array $2 = (struct main.pair **) 0xc820030028 ( gdb ) p pairs.array[0] $3 = (struct main.pair *) 0xc82000a3a0 ( gdb ) p *pairs.array[0] $4 = {x = 0, y = 0} ( gdb ) p (*pairs.array[0]).x $5 = 0 ( gdb ) p (*pairs.array[0]).y $6 = 0 ( gdb ) continue Continuing. looping &{x:1 y:1} Breakpoint 1, main.main () at /home/bfosberry/ .go /src/github .com /bfosberry/gdb_sandbox/main .go:18 18 fmt .Println( "looping" ) ( gdb ) p (pairs.array[1][5]).y $7 = 1 ( gdb ) continue Continuing. looping &{x:2 y:6} Breakpoint 1, main.main () at /home/bfosberry/ .go /src/github .com /bfosberry/gdb_sandbox/main .go:18 18 fmt .Println( "looping" ) ( gdb ) p (pairs.array[2][6]).y $8 = 6 ( gdb ) |
你会发现这里 gdb 并不确定 pairs 是一个 slice 类型,我们不能直接访问它的属性,为了访问它的成员我们需要使用 pairs.array 来转换成数组,然后我们就可以检查 slice 的 length(长度)和 capacity(容量):
1 2 3 4 | ( gdb ) p $len(pairs) $12 = 3 ( gdb ) p $cap(pairs) $13 = 4 |
这时我们可以让它循环几次,并透过这个 slice 不用的成员方法监听增加的 x 和 y 的值,要注意的是,这里的 struct 属性可以通过指针访问,所以 p pairs.array[2].y 一样可行。
一曲
翻译于 3天前
0人顶
顶 翻译的不错哦!
Goroutines
现在我们已经可以访问 struct 和 slice 了,下面再来更加复杂一点的程序吧。让我们添加一些goroutines 到 mian 函数,并行处理每一个数字,返回的结果存入信道(chan)中:
如果我等待 WaitGroup 执行完毕再检查 pairs slice 的结果,我们可以预期到内容是完全相同的,虽然它的排序可能有些出入。gdb 真正的威力来自于它可以在 goroutines 正在运行时进行检查:
你会发现我们在 goroutine 要执行的代码段中放置了一个断点,从这里我们可以检查到局部变量,和进程中的其它 goroutines:
在这里我们做的第一件事就是列出所有正在运行的 goroutine,并确定我们正在处理的那一个。然后我们可以看到一些回溯,并发送任何调试命令到 goroutine。这个回溯和列表清单并不太准确,如何让回溯更准确,goroutine 上的 info args 显示了我们的局部变量,以及主函数中的可用变量,goroutine 函数之外的使用前缀&。 |
一曲
|
结论当调试应用时,gdb 的强大令人难以置信。但它仍然是一个相当新的事物,并不是所有的地方工作地都很完美。使用最新的稳定版 gdb,go 1.5 beta2,有不少地方有突破: Interfaces根据 go 博客上的文章, go 的 interfaces 应该已经支持了,这允许在 gdb 中动态的投影其基类型。这应该算一个突破。 Interface{} 类型目前没有办法转换 interface{} 为它的类型。 列出 goroutine 的不同点在其他 goroutine 中列出周边代码会导致一些行数的漂移,最终导致 gdb 认为当前的行数超出文件范围并抛出一个错误:
Goroutine 调试还不稳定处理 goroutines 往往不稳定;我遇到过执行简单命令产生错误的情况。现阶段你应该做好处理类似问题的准备。 |
Garfielt
|
gdb 支持 Go 的配置非常麻烦
|
叶秀兰
|
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
有疑问加站长微信联系(非本文作者)