1
- # 11.2 使用GDB调试
2
- 开发程序过程中调试代码是开发者经常要做的一件事情,Go语言不像PHP、Python等动态语言,只要修改不需要编译就可以直接输出,而且可以动态的在运行环境下打印数据。当然Go语言也可以通过Println之类的打印数据来调试,但是每次都需要重新编译,这是一件相当麻烦的事情。我们知道在Python中有pdb/ipdb之类的工具调试,Javascript也有类似工具,这些工具都能够动态的显示变量信息,单步调试等。不过庆幸的是Go也有类似的工具支持:GDB。Go内部已经内置支持了GDB,所以,我们可以通过GDB来进行调试,那么本小节就来介绍一下如何通过GDB来调试Go程序。
3
-
4
- ## GDB调试简介
5
- GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具 。使用GDB可以做如下事情:
6
-
7
- 1 . 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序 。
8
- 2 . 可让被调试的程序在你所指定的调置的断点处停住 。(断点可以是条件表达式)
9
- 3 . 当程序被停住时,可以检查此时你的程序中所发生的事 。
10
- 4 . 动态的改变你程序的执行环境 。
11
-
12
- 目前支持调试Go程序的GDB版本必须大于7.1。
13
-
14
- 编译Go程序的时候需要注意以下几点
15
-
16
- 1 . 传递参数-ldflags "-s",忽略debug的打印信息
17
- 2 . 传递-gcflags "-N -l" 参数,这样可以忽略Go内部做的一些优化,聚合变量和函数等优化,这样对于GDB调试来说非常困难,所以在编译的时候加入者两个参数避免这些优化。
18
-
19
- ## 常用命令
20
- GDB的一些常用命令如下所示
21
-
22
- - list
23
-
24
- 简写命令`l`,用来显示源代码,默认显示十行代码,后面可以带上参数显示的具体行,例如:`list 15`,显示十行代码,其中15行在显示的十行里面的中间,如下所示。
25
-
26
- 10 time.Sleep(2 * time.Second)
27
- 11 c <- i
28
- 12 }
29
- 13 close(c)
30
- 14 }
31
- 15
32
- 16 func main() {
33
- 17 msg := "Starting main"
34
- 18 fmt.Println(msg)
35
- 19 bus := make(chan int)
36
-
37
-
38
- - break
39
-
40
- 简写命令`b`,用来设置断点,后面跟上参数设置断点的行数,例如`b 10`在第十行设置断点。
41
- - delete
42
-
43
- 简写命令` d ` ,用来删除断点,后面跟上断点设置的序号,这个序号可以通过` info breakpoints ` 获取相应的设置的断点序号,如下是显示的设置断点序号。
44
-
45
- Num Type Disp Enb Address What
46
- 2 breakpoint keep y 0x0000000000400dc3 in main.main at /home/xiemengjun/gdb.go:23
47
- breakpoint already hit 1 time
48
-
49
- - backtrace
50
-
51
- 简写命令`bt`,用来打印执行的代码过程,如下所示:
52
-
53
- #0 main.main () at /home/xiemengjun/gdb.go:23
54
- #1 0x000000000040d61e in runtime.main () at /home/xiemengjun/go/src/pkg/runtime/proc.c:244
55
- #2 0x000000000040d6c1 in schedunlock () at /home/xiemengjun/go/src/pkg/runtime/proc.c:267
56
- #3 0x0000000000000000 in ?? ()
57
- - info
58
-
59
- info命令用来显示信息,后面有几种参数,我们常用的有如下几种:
60
-
61
- - `info locals`
62
-
63
- 显示当前执行的程序中的变量值
64
- - `info breakpoints`
65
-
66
- 显示当前设置的断点列表
67
- - `info goroutines`
68
-
69
- 显示当前执行的goroutine列表,如下代码所示,带*的表示当前执行的
70
-
71
- * 1 running runtime.gosched
72
- * 2 syscall runtime.entersyscall
73
- 3 waiting runtime.gosched
74
- 4 runnable runtime.gosched
75
- - print
76
-
77
- 简写命令`p`,用来打印变量或者其他信息,后面跟上需要打印的变量名,当然还有一些很有用的函数$len()和$cap(),用来返回当前string、slices或者maps的长度和容量。
78
-
79
- - whatis
80
-
81
- 用来显示当前变量的类型,后面跟上变量名,例如`whatis msg`,显示如下:
82
-
83
- type = struct string
84
- - next
85
-
86
- 简写命令`n`,用来单步调试,跳到下一步,当有断点之后,可以输入`n`跳转到下一步继续执行
87
- - countinue
88
-
89
- 简称命令`c`,用来跳出当前断点处,后面可以跟参数N,跳过多少次断点
90
-
91
- - set variable
92
-
93
- 该命令用来改变运行过程中的变量值,格式如:`set variable <var>=<value>`
94
-
95
- ## 调试过程
1
+ # 11.2 使用GDB调试
2
+ 开发程序过程中调试代码是开发者经常要做的一件事情,Go语言不像PHP、Python等动态语言,只要修改不需要编译就可以直接输出,而且可以动态的在运行环境下打印数据。当然Go语言也可以通过Println之类的打印数据来调试,但是每次都需要重新编译,这是一件相当麻烦的事情。我们知道在Python中有pdb/ipdb之类的工具调试,Javascript也有类似工具,这些工具都能够动态的显示变量信息,单步调试等。不过庆幸的是Go也有类似的工具支持:GDB。Go内部已经内置支持了GDB,所以,我们可以通过GDB来进行调试,那么本小节就来介绍一下如何通过GDB来调试Go程序。
3
+
4
+ ## GDB调试简介
5
+ GDB是GNU开源组织发布的一个强大的类UNIX系统下的程序调试工具 。使用GDB可以做如下事情:
6
+
7
+ 1 . 启动程序,可以按照开发者的自定义要求运行程序 。
8
+ 2 . 可让被调试的程序在开发者设定的调置的断点处停住 。(断点可以是条件表达式)
9
+ 3 . 当程序被停住时,可以检查此时程序中所发生的事 。
10
+ 4 . 动态的改变当前程序的执行环境 。
11
+
12
+ 目前支持调试Go程序的GDB版本必须大于7.1。
13
+
14
+ 编译Go程序的时候需要注意以下几点
15
+
16
+ 1 . 传递参数-ldflags "-s",忽略debug的打印信息
17
+ 2 . 传递-gcflags "-N -l" 参数,这样可以忽略Go内部做的一些优化,聚合变量和函数等优化,这样对于GDB调试来说非常困难,所以在编译的时候加入者两个参数避免这些优化。
18
+
19
+ ## 常用命令
20
+ GDB的一些常用命令如下所示
21
+
22
+ - list
23
+
24
+ 简写命令`l`,用来显示源代码,默认显示十行代码,后面可以带上参数显示的具体行,例如:`list 15`,显示十行代码,其中15行在显示的十行里面的中间,如下所示。
25
+
26
+ 10 time.Sleep(2 * time.Second)
27
+ 11 c <- i
28
+ 12 }
29
+ 13 close(c)
30
+ 14 }
31
+ 15
32
+ 16 func main() {
33
+ 17 msg := "Starting main"
34
+ 18 fmt.Println(msg)
35
+ 19 bus := make(chan int)
36
+
37
+
38
+ - break
39
+
40
+ 简写命令 `b`,用来设置断点,后面跟上参数设置断点的行数,例如`b 10`在第十行设置断点。
41
+
42
+ - delete
43
+ 简写命令 `d`,用来删除断点,后面跟上断点设置的序号,这个序号可以通过`info breakpoints`获取相应的设置的断点序号,如下是显示的设置断点序号。
44
+
45
+ Num Type Disp Enb Address What
46
+ 2 breakpoint keep y 0x0000000000400dc3 in main.main at /home/xiemengjun/gdb.go:23
47
+ breakpoint already hit 1 time
48
+
49
+ - backtrace
50
+
51
+ 简写命令 `bt`,用来打印执行的代码过程,如下所示:
52
+
53
+ #0 main.main () at /home/xiemengjun/gdb.go:23
54
+ #1 0x000000000040d61e in runtime.main () at /home/xiemengjun/go/src/pkg/runtime/proc.c:244
55
+ #2 0x000000000040d6c1 in schedunlock () at /home/xiemengjun/go/src/pkg/runtime/proc.c:267
56
+ #3 0x0000000000000000 in ?? ()
57
+ - info
58
+
59
+ info命令用来显示信息,后面有几种参数,我们常用的有如下几种:
60
+
61
+ - `info locals`
62
+
63
+ 显示当前执行的程序中的变量值
64
+ - `info breakpoints`
65
+
66
+ 显示当前设置的断点列表
67
+ - `info goroutines`
68
+
69
+ 显示当前执行的goroutine列表,如下代码所示,带*的表示当前执行的
70
+
71
+ * 1 running runtime.gosched
72
+ * 2 syscall runtime.entersyscall
73
+ 3 waiting runtime.gosched
74
+ 4 runnable runtime.gosched
75
+ - print
76
+
77
+ 简写命令`p`,用来打印变量或者其他信息,后面跟上需要打印的变量名,当然还有一些很有用的函数$len()和$cap(),用来返回当前string、slices或者maps的长度和容量。
78
+
79
+ - whatis
80
+
81
+ 用来显示当前变量的类型,后面跟上变量名,例如`whatis msg`,显示如下:
82
+
83
+ type = struct string
84
+ - next
85
+
86
+ 简写命令 `n`,用来单步调试,跳到下一步,当有断点之后,可以输入`n`跳转到下一步继续执行
87
+ - coutinue
88
+
89
+ 简称命令 `c`,用来跳出当前断点处,后面可以跟参数N,跳过多少次断点
90
+
91
+ - set variable
92
+
93
+ 该命令用来改变运行过程中的变量值,格式如:`set variable <var>=<value>`
94
+
95
+ ## 调试过程
96
96
我们通过下面这个代码来演示如何通过GDB来调试Go程序,下面是将要演示的代码:
97
97
98
98
package main
@@ -119,17 +119,17 @@ GDB的一些常用命令如下所示
119
119
for count := range bus {
120
120
fmt.Println("count:", count)
121
121
}
122
- }
123
-
124
- 编译文件,生成可执行文件gdbfile:
125
-
126
- go build -gcflags "-N -l" -ldflags "-s" gdbfile.go
127
-
128
- 通过gdb命令启动调试:
129
-
122
+ }
123
+
124
+ 编译文件,生成可执行文件gdbfile:
125
+
126
+ go build -gcflags "-N -l" -ldflags "-s" gdbfile.go
127
+
128
+ 通过gdb命令启动调试:
129
+
130
130
gdb gdbfile
131
131
132
- 启动之后我们首先看看是不是可以运行起来,输入 ` run ` 命令之后程序就开始运行,我们可以看到程序输出如下 ,和我们在命令行直接执行程序输出是一样的:
132
+ 启动之后首先看看这个程序是不是可以运行起来,只要输入 ` run ` 命令回车后程序就开始运行,程序正常的话可以看到程序输出如下 ,和我们在命令行直接执行程序输出是一样的:
133
133
134
134
(gdb) run
135
135
Starting program: /home/xiemengjun/gdbfile
@@ -145,8 +145,8 @@ GDB的一些常用命令如下所示
145
145
count: 8
146
146
count: 9
147
147
[LWP 2771 exited]
148
- [Inferior 1 (process 2771) exited normally]
149
- 好了,现在我们知道怎么让程序跑起来了,接下来我们开始设置断点 :
148
+ [Inferior 1 (process 2771) exited normally]
149
+ 好了,现在我们已经知道怎么让程序跑起来了,接下来开始给代码设置断点 :
150
150
151
151
(gdb) b 23
152
152
Breakpoint 1 at 0x400d8d: file /home/xiemengjun/gdbfile.go, line 23.
@@ -157,9 +157,9 @@ GDB的一些常用命令如下所示
157
157
[Switching to LWP 3284]
158
158
159
159
Breakpoint 1, main.main () at /home/xiemengjun/gdbfile.go:23
160
- 23 fmt.Println("count:", count)
161
-
162
- 上面我们可以看到我们在第23行设置了断点,然后运行,我们可以看到在我们设置断点的地方程序停住了,我们可以查看相应的源码,我们可以看到显示的原始是当前停止行之前的五行开始显示 :
160
+ 23 fmt.Println("count:", count)
161
+
162
+ 上面例子 ` b 23 ` 表示在第23行设置了断点,之后输入 ` run ` 开始运行程序。现在程序在前面设置断点的地方停住了,我们需要查看断点相应上下文的源码,输入 ` list ` 就可以看到源码显示从当前停止行的前五行开始 :
163
163
164
164
(gdb) list
165
165
18 fmt.Println(msg)
@@ -171,7 +171,7 @@ GDB的一些常用命令如下所示
171
171
24 }
172
172
25 }
173
173
174
- 现在我们可以查看所有的变量信息,然后打印相应的变量,查看相应变量的类型 :
174
+ 现在GDB在运行当前的程序的环境中已经保留了一些有用的调试信息,我们只需打印出相应的变量,查看相应变量的类型及值 :
175
175
176
176
(gdb) info locals
177
177
count = 0
@@ -183,7 +183,7 @@ GDB的一些常用命令如下所示
183
183
(gdb) whatis bus
184
184
type = chan int
185
185
186
- 接下来我们如何让程序继续往下执行呢?请看下面的命令
186
+ 接下来该让程序继续往下执行,请继续看下面的命令
187
187
188
188
(gdb) c
189
189
Continuing.
@@ -201,7 +201,9 @@ GDB的一些常用命令如下所示
201
201
Breakpoint 1, main.main () at /home/xiemengjun/gdbfile.go:23
202
202
23 fmt.Println("count:", count)
203
203
204
- 我们可以看到每次输入c之后都会执行一次,执行到下一次for循环,打印出来相应的信息,接下来我们来改变变量的信息,然后继续执行到下一步,我们可以看到我们可以修改相应的变量值:
204
+ 每次输入` c ` 之后都会执行一次代码,又跳到下一次for循环,继续打印出来相应的信息。
205
+
206
+ 设想目前需要改变上下文相关变量的信息,跳过一些过程,并继续执行下一步,得出修改后想要的结果:
205
207
206
208
(gdb) info locals
207
209
count = 2
@@ -218,7 +220,7 @@ GDB的一些常用命令如下所示
218
220
Breakpoint 1, main.main () at /home/xiemengjun/gdbfile.go:23
219
221
23 fmt.Println("count:", count)
220
222
221
- 最后我们来看一下,这整个的过程中起了多少个goroutine,每个goroutine都是在做什么 :
223
+ 最后稍微思考一下,前面整个程序运行的过程中到底创建了多少个goroutine,每个goroutine都在做什么 :
222
224
223
225
(gdb) info goroutines
224
226
* 1 running runtime.gosched
@@ -236,12 +238,12 @@ GDB的一些常用命令如下所示
236
238
#5 0x000000000040d16a in schedunlock () at /home/xiemengjun/go/src/pkg/runtime/proc.c:267
237
239
#6 0x0000000000000000 in ?? ()
238
240
239
- 通过gotoutine的命令我们清楚的知道go内部是怎么执行的,每个函数的调用顺序都非常清楚的显示出来了 。
241
+ 通过查看goroutines的命令我们可以清楚地了解goruntine内部是怎么执行的,每个函数的调用顺序已经明明白白地显示出来了 。
240
242
241
243
## 小结
242
- 本小节我们介绍了GDB调试Go程序的一些基本命令,包括` run ` 、` print ` 、` info ` 、` set variable ` 、` coutinue ` 、` list ` 、` break ` 等一些经常用到的调试命令 ,通过上面的例子演示,我相信读者已经对于通过GDB调试Go程序有了基本的理解,如果你想获取更多的调试技巧请参考官方网站的GDB调试手册,还有GDB官方网站的手册。
243
-
244
- ## links
245
- * [ 目录] ( < preface.md > )
246
- * 上一节: [ 错误处理] ( < 11.1.md > )
244
+ 本小节我们介绍了GDB调试Go程序的一些基本命令,包括` run ` 、` print ` 、` info ` 、` set variable ` 、` coutinue ` 、` list ` 、` break ` 等经常用到的调试命令 ,通过上面的例子演示,我相信读者已经对于通过GDB调试Go程序有了基本的理解,如果你想获取更多的调试技巧请参考官方网站的GDB调试手册,还有GDB官方网站的手册。
245
+
246
+ ## links
247
+ * [ 目录] ( < preface.md > )
248
+ * 上一节: [ 错误处理] ( < 11.1.md > )
247
249
* 下一节: [ Go怎么写测试用例] ( < 11.3.md > )
0 commit comments