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+ ## 调试过程
9696我们通过下面这个代码来演示如何通过GDB来调试Go程序,下面是将要演示的代码:
9797
9898package main
@@ -119,17 +119,17 @@ GDB的一些常用命令如下所示
119119for count := range bus {
120120fmt.Println("count:", count)
121121}
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+
130130gdb gdbfile
131131
132- 启动之后我们首先看看是不是可以运行起来,输入 ` run ` 命令之后程序就开始运行,我们可以看到程序输出如下 ,和我们在命令行直接执行程序输出是一样的:
132+ 启动之后首先看看这个程序是不是可以运行起来,只要输入 ` run ` 命令回车后程序就开始运行,程序正常的话可以看到程序输出如下 ,和我们在命令行直接执行程序输出是一样的:
133133
134134(gdb) run
135135Starting program: /home/xiemengjun/gdbfile
@@ -145,8 +145,8 @@ GDB的一些常用命令如下所示
145145count: 8
146146count: 9
147147[LWP 2771 exited]
148- [Inferior 1 (process 2771) exited normally]
149- 好了,现在我们知道怎么让程序跑起来了,接下来我们开始设置断点 :
148+ [Inferior 1 (process 2771) exited normally]
149+ 好了,现在我们已经知道怎么让程序跑起来了,接下来开始给代码设置断点 :
150150
151151(gdb) b 23
152152Breakpoint 1 at 0x400d8d: file /home/xiemengjun/gdbfile.go, line 23.
@@ -157,9 +157,9 @@ GDB的一些常用命令如下所示
157157[Switching to LWP 3284]
158158
159159Breakpoint 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 ` 就可以看到源码显示从当前停止行的前五行开始 :
163163
164164(gdb) list
16516518 fmt.Println(msg)
@@ -171,7 +171,7 @@ GDB的一些常用命令如下所示
17117124 }
17217225 }
173173
174- 现在我们可以查看所有的变量信息,然后打印相应的变量,查看相应变量的类型 :
174+ 现在GDB在运行当前的程序的环境中已经保留了一些有用的调试信息,我们只需打印出相应的变量,查看相应变量的类型及值 :
175175
176176(gdb) info locals
177177count = 0
@@ -183,7 +183,7 @@ GDB的一些常用命令如下所示
183183(gdb) whatis bus
184184type = chan int
185185
186- 接下来我们如何让程序继续往下执行呢?请看下面的命令
186+ 接下来该让程序继续往下执行,请继续看下面的命令
187187
188188(gdb) c
189189Continuing.
@@ -201,7 +201,9 @@ GDB的一些常用命令如下所示
201201Breakpoint 1, main.main () at /home/xiemengjun/gdbfile.go:23
20220223 fmt.Println("count:", count)
203203
204- 我们可以看到每次输入c之后都会执行一次,执行到下一次for循环,打印出来相应的信息,接下来我们来改变变量的信息,然后继续执行到下一步,我们可以看到我们可以修改相应的变量值:
204+ 每次输入` c ` 之后都会执行一次代码,又跳到下一次for循环,继续打印出来相应的信息。
205+
206+ 设想目前需要改变上下文相关变量的信息,跳过一些过程,并继续执行下一步,得出修改后想要的结果:
205207
206208(gdb) info locals
207209count = 2
@@ -218,7 +220,7 @@ GDB的一些常用命令如下所示
218220Breakpoint 1, main.main () at /home/xiemengjun/gdbfile.go:23
21922123 fmt.Println("count:", count)
220222
221- 最后我们来看一下,这整个的过程中起了多少个goroutine,每个goroutine都是在做什么 :
223+ 最后稍微思考一下,前面整个程序运行的过程中到底创建了多少个goroutine,每个goroutine都在做什么 :
222224
223225(gdb) info goroutines
224226* 1 running runtime.gosched
@@ -236,12 +238,12 @@ GDB的一些常用命令如下所示
236238#5 0x000000000040d16a in schedunlock () at /home/xiemengjun/go/src/pkg/runtime/proc.c:267
237239#6 0x0000000000000000 in ?? ()
238240
239- 通过gotoutine的命令我们清楚的知道go内部是怎么执行的,每个函数的调用顺序都非常清楚的显示出来了 。
241+ 通过查看goroutines的命令我们可以清楚地了解goruntine内部是怎么执行的,每个函数的调用顺序已经明明白白地显示出来了 。
240242
241243## 小结
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 > )
247249 * 下一节: [ Go怎么写测试用例] ( < 11.3.md > )
0 commit comments