今天来整理 GDB 调试。
在windows 上我们使用vs2017开发,可以手动的加断点,debug。
那么在linux上怎么加断点,debug呢?这就是今天要整理的GDB调试工具了。
那么有些同学可能会想到:我们在windows上开发,只是在linux上跑不就行了吗?这样不是更方便吗?
实际上这样做有两个问题需要解决:
1.在linux上写C++,可能我们会用到linux的c函数,这些函数在windows上不支持的,那么在windows上就会有build error,build都build 不过,怎么跑起来debug呢?
2.项目最终是要部署到linux系统上运行的,假设这个bug在部署完成后,有问题,只有在项目运行环境下才有可能发生,这样debug也只能在linux下完成
因此学习GDB是很有必要的。
0.GDB可以运行的前提,必须build的时候要加上-g选项
g++ -g main.c -o a.out
g++ gdb.cpp -o a.out -g -Wall
1.GDB常用流程以及命令
1.)开始调试 gdb a.out
2.)list 1 后 回车 ,会调转到第一行看代码
list 1 可以简写成 l 1
3.) 设置断点 b 23 表示断点设置在第23行
b 23 和 break 23 的含义一样。
4.) run 执行,或者 r, 注意这时候23行并没有执行
5)按下 n 或者 s 往下走,n是next , s是step,
n 和s 的区别在于:step是跳入,如果是一个函数 s就会跳到函数内部
要注意的是:如果是系统函数,例如 rand()函数,按下s,会告知 “没有那个文件或目录”,也就是说,如果是系统函数,则进不去,只能在我们自己写的函数内部跳转,因此要注意的是,如果是系统函数,只能用n,如果用了s,就有可能出不来了。
6)跳转到下一个断点 continue 或者 c
7) 从这个断点直接跳到多少行 until 50 或者 u 50,中间有断点则会调用到断点那一行。
8)在某一行的时候,查看某一个变量的值 p key, 使用ptype key1
p arr
p 是print 的意思,也就是打印 arr 的值
ptype arr
查看arr 的类型
9)q是退出调试
2.GDB其他有用指令
gdb 对于段错误在执行 gdb a.out的时候,只要执行run,就会直接跳转到出现段错误的行
start 命令,会从main 函数的第一行开始执行
finish 命令,可以结束当前函数调用,例如我们按下s进入了一个系统函数,则可以使用finish命令退出来。
设置main函数的参数
在正常case 是这样设置的:
./a.out aa bb cc
在gdb中怎么设置呢?
在使用 gdb a.out 开始后,先set args ,然后run
set args aa bb cc
run
也可以直接 run aa bb cc
查看目前的断点有哪些 info b, 删除断点 d num
注意的是:删除断点 d 后面跟的是 num,不是行数
(gdb) b 23
Breakpoint 1 at 0x4009b0: file gdb.cpp, line 23.
(gdb) b 35
Note: breakpoint 1 also set at pc 0x4009b0.
Breakpoint 2 at 0x4009b0: file gdb.cpp, line 35.
(gdb) b 67
Breakpoint 3 at 0x400b47: file gdb.cpp, line 67.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004009b0 in select_sort(int*, int) at gdb.cpp:23
2 breakpoint keep y 0x00000000004009b0 in select_sort(int*, int) at gdb.cpp:35
3 breakpoint keep y 0x0000000000400b47 in main() at gdb.cpp:67
(gdb) d 2
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004009b0 in select_sort(int*, int) at gdb.cpp:23
3 breakpoint keep y 0x0000000000400b47 in main() at gdb.cpp:67
设置断点时有 条件 b 50 if i=9
意思是 设置断点在50行,当i=9的时候才会触发这个断点
display 和 undisplay 一直观察某一个值,取消某一个值的观察
display a 一直显示a
display arr[a] 一直显示 arr[a]
display 将所有的观察的值列出
undisplay num 不再观察编号num的值
(gdb) display
6: a = 1
7: b = 7
8: arr[a] = 2
9: arr[b] = 13
(gdb) undisplay 6
3.栈帧的概念 引出的GDB 的backtrace 和 frame
bracktrace 查看当前所有的栈帧 简写为bt
使用 frame num 切换栈帧
当函数调用的时候,会在stack上开辟一片内存空间,用于存放函数调用时产生的局部变量和临时值
当一个函数调用结束后,系统就会从stack 中将这个栈帧 删除调用。
如下是main函数中有 select_sort() 函数的 栈帧图
那么在实际开发中,就会有这样的case,当我们debug到select_sort()这个栈帧的时候,需要观察下 main 栈帧中p的值,假设这个main 和select_sort()是两个线程,
但是这时候我们处在 select_sort这个栈帧中,是无法观察main栈帧的数据的。
因此要使用 backtrace
当我们执行到select_sort函数的时候,使用 bt命令
(gdb) bt
#0 select_sort (arr=0x7fffffffe2c0, len=10) at gdb.cpp:38
#1 0x0000000000400b47 in main () at gdb.cpp:66
(gdb)
可以看到,这时候有两个栈帧的,0是select_sort, 1是main 栈帧
由于我们要查看的变量char *p = (char *)"hellod"; 是在main栈帧中的,
因此要使用 frame 1 切换到main 栈帧,查看 p的值的
(gdb) frame 1
#1 0x0000000000400b47 in main () at gdb.cpp:66
66 select_sort(arr, N);
(gdb) p p
$1 = 0x400c76 "hellod"
然后可以使用 frame 0 切换到当前的 栈帧
4. GDB 常见错误分析
当我们使用 gdb gdbtest.out的时候,可能会遇见入下的错误,这说明你的gdbtest可执行文件在编译的时候就有问题,,并没有加上 -g的选项