GDB调试
GDB调试原理是通过在源代码进行gcc|g++编译过程加入-g参数创建符号表,符号表包含了程序中使用的变量名称的列表, 同时关闭所有的优化机制,以便程序执行过程中严格按照原来的源代码进行。
一、开启方式
如果GDB调试未开启GDB的程序,会因为找不到符号表无法继续:
Reading symbols from ./mainx...(no debugging symbols found)...done.
(gdb) list
No symbol table is loaded. Use the "file" command.
(gdb)
那么,我们如何在程序的编译过程开启GDB的调试模式呢?
gcc 模式
$ gcc -g -o main main.c
通过加入 -g 参数启用GDB调试.
make 模式
make模式旨在根据Makefile文件的内容进行编译工作,要开启GDB调试,在Makeifle文件中加入CFLAGS = -g
则可CFLAGS = -g main:main.o gcc -o {$CFLAGS} main main.c
cmake 模式
# cmake最低版本需求,不加入此行会受到警告信息 CMAKE_MINIMUM_REQUIRED(VERSION 2.6) # 项目名称 PROJECT(MAIN) # 生成应用程序 hello (在windows下会自动生成hello.exe) ADD_EXECUTABLE(main main.c) # 设置GDB调试支持 set(CMAKE_BUILD_TYPE "Debug") set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
二、用法
2.1 启停程序
- 加载符号表
在通过以上编译得来的应用程序,则可以正式进行GDB调试了.gdb main GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1 ... Reading symbols from main...done. (gdb)
如果应用程序在编译时未添加-g参数,则会提示"Reading symbols from ./mainx...(no debugging symbols found)...done.",从而无法继续进行GDB调试。
gdb main 只是加载了调试程序的符号表,并没有启动程序。
启动调试
进入gdb控制台后,输入run命令,则可以启动调试。(gdb) run Starting program: /home/vagrant/obj2/main
此时如果未添加任何断点,程序会一直执行下去,直到正常退出。因此,我们需要在启动前添加断点。 当然除此之外,我们可以使用start命令。
(gdb) start Temporary breakpoint 3 at 0x4007de: file /home/vagrant/obj2/main.c, line 3. Starting program: /home/vagrant/obj2/main
start 命令是自动为程序的主函数添加一个断点。并在主函数处停顿。
中止调试
调试启动后,可以使用kill或者k来启动调试(gdb) k Kill the program being debugged? (y or n) y
如果程序是通过run启动,或无断点停顿,程序在前端运行是无法通过kill中止的, 只能通过ctrl+c强制中止,这和tty端运行程序是一个道理。 只有存在断点停顿,系统才会有人机交互的可能。
查看调试状态
info program // 查看当前程序运行情况 info stack // 查看堆栈使用情况
2.2 断点管理
- 添加断点,使用b或者break:
break main // 给指定的函数添加断点 break main.c:main // 给main.c文件中的main函数添加断点 break 10 // 在第10行添加断点 break main.c:10 // 给main.c文件中的第10行添加断点 break 0x00000000004007e8 // 给指定的内存地址添加断点 break +1 // +偏移量(目前还不知道怎么用) break -1 // -偏移量(目前还不知道怎么用) break 10 if value==9 // 给断点添加触发条件,如第10行,当vlaue=9时才会触发
* 查看断点info b // 或者info break
* 给已添加的断点添加触发条件condition 1 if index=2 // 给断点ID为1的断点添加条件,condition 断点编号:除触发条件
* 删除断点delete 1 // 删除断点ID为1的断点 delete // 删除所有断点 clear 10 // 删除第10行断点(clear不能删除指定断点ID的断点) clear main // 删除main函数的断点 crear main.c:10 // 删除文件main.c的第10行断点 clrer main.c:main // 删除文件main.c的main函数断点
* 断点启停disable 1 // 停用断点ID为1的断点 enable 1 // 启用断点ID为1的断点 enable once 1 // 启用断点ID为1的断点一次后停用
- 断点控制
step(s) // 单步进入,遇到子函数会进入子函数内部。 next(s) // 下一步,按代码顺序执行,遇到子函数不会进入内部。 continue(c) // 继续执行,直到下一个断点 return 返回值 // 单步进入子函数内部时,可提前通过此函数中止子函数,并返回指定的返回值 call + 函数 // 在当前位置调用指定函数 until(u) + 行号 // 跳到当前执行文件的第n行。
- 变量监测
在调试的过程,我们可能想要知道随着调试过程的变动,某些关键变量的值是如何变化的。display 变量 // 将变量加入监控面板,每次断点停顿时都会显示监控面板的所有变量信息。 info display // 查看监控面板中变量信息(包含变量的编号) delete display 编号 // 删除监控面板中指定变量编号的变量 enable display 编号 // 启用监控面板中的指定变量 disable display 编号 // 停用监控面板中的指定变量 undisplay 编号 // 与`delete display 编号`效果一致 whatis 变量 // 查看变量类型 print(p) 变量 // 打印指定变量 set key=value // 修改变量值
> 在使用print命令时,可以对变量按指定格式进行输出,其命令格式为print /变量名 + 格式,其中常用的变量格式:x:十六进制;d:十进制;u:无符号数;o:八进制;c:字符格式;f:浮点数。