GDB常用指令及示例。有关GDB更详细的信息,可以参考GDB官方手册。
¶启动与退出GDB
一般有三种启动gdb的方式。
| 启动命令 | 说明 |
|---|---|
| gdb program | 在gdb的监控下运行程序program |
| gdb program coredump | 同时加载core dump文件,一般用于分析程序崩溃的原因。 |
| gdb -p pid | 调试正在运行的进程,需要有相关的权限。 |
gdb启动成功后,会进入到gdb交换界面,有提示符(gdb) 。在交换界面中,可以输入一些指令,来对目标程序进行debug。输入指令quit或组合键Ctrl + D可以退出gdb。
1 | (gdb) |
¶设置断点
断点用于中断程序的运行,这时可以查看程序的当前状态,进行debug。
¶break ( b )
break指令用于设置断点,断点位置可以是一个符号,或代码的某一行。下面的示例在第13行和main函数分别设置了一个断点。break指令的缩写是b。
1 | (gdb) break 13 |
¶watch
watch指令用于监控表达式的变化。当表达式的值发生变化时,则停止程序。注意,watch只能监控变量,指针或常量地址需要解引用。rwatch监控读操作,awatch监控读写操作。
If you watch for a change in a numerically entered address you need to dereference it, as
the address itself is just a constant number which will never change. gdb refuses to create
a watchpoint that watches a never-changing value:
1
2
3
4 (gdb) watch 0x600850
Cannot watch constant value 0x600850.
(gdb) watch *(int *) 0x600850
Watchpoint 1: *(int *) 6293584
info watchpoints 查看相关信息。
¶catch
捕获指定事件。常见的事件如下表格所示。tcatch捕获一次后自动删除相关断点。
| event | 说明 |
|---|---|
| catch exec | 调用exec |
| catch syscall name/number | 调用系统调用,可以指定名字或编号。 |
| catch fork | 调用fork |
| catch vfork | 调用vfork |
| catch load | 加载共享库 |
| catch unload | 卸载共享库 |
| catch signal signal/all | 传递信号 |
¶delete ( d )
delete指令用于删除断点,参数是断点号。如果忘记断点对应的断点号,可以用指令info break查看所有的断点。下面的示例删除了断点2。delete指令的缩写是d。
1 | (gdb) info break |
clear指令也可以删除断点,参数是断点在程序中的位置。
¶命中断点后自动执行指令
命中断点后,GDB会暂停执行程序。这时可以执行一些GDB命令,调试程序。如果断点会多次名字,每次都手工敲,会浪费很多时间。可以用commands命令为断点设置命令。在>提示符前输入命令,一行一个,输入end结束。commands number表示给指定断点设置命令。
示例,调用write系统调用时打印backtrace。
1 | (gdb) catch syscall write |
¶运行程序
¶run ( r )
GDB启动之后,被调试的程序可能处于未运行的状态,需要使用run指令来启动程序的运行。程序启动之后,会在断点处停下来。run指令的缩写是r。
1 | (gdb) run |
如果没有设置断点,程序会一直运行下去。或者在任何可能阻塞程序运行的地方停下来,如等待用户输入。
¶continue ( c )
continue指令用于继续运行程序,直到遇到下列情况:
- 遇到断点,程序中断运行。
- 程序被阻塞,等待继续运行的条件。如等待用户输入。
- 程序正常或异常退出。
continue指令的缩写c。
¶next ( n )
单步执行,不进入子函数。
¶step ( s )
单步执行,进入子函数。
¶stepi
调试内联汇编时,GDB认为asm段是单一语句。可以使用stepi命令进入asm段进行单步执行,单独地执行每条指令。
¶指令级别单步
对于C或其他高级语言编写的程序,单步是以语句为单位。可以使用指令set disassemble-next-line on开启指令级别单步,使用stepi(缩写si)或nexti(缩写ni)单步执行。
¶查看寄存器或内存
¶print ( p )
查看寄存器或内存的值,默认是十进制,print/x以十六进制打印,print/o以八进制打印。
1 | (gdb) p $eax |
寄存器前面要加
$,变量无需前缀;取变量地址需前缀&。这和汇编的语法不同。
¶examine ( x )
x与print指令类似,但支持更多的格式。格式如下:x /nfu address
- n 内存单元的个数
- f 显示方式
- x 十六进制
- d 十进制
- u 无符号十进制
- o 八进制
- t 二进制
- a 十六进制
- i 指令
- c 字符
- f 浮点数
- u 一个地址单元的长度
- b 单字节
- h 双字节
- w 四字节
- g 八字节
示例:
1 | (gdb) x/7xw &values # 按十六进制显示7个内存单元,每个内存单元4字节 |
¶display
当程序停止时自动打印表达式的值,指令格式与指令x类似,但是不支持长度。
1 | (gdb) display $pc # 监控 pc 寄存器 |
指令info display查看当前会自动打印的表达式。指令undisplay num可用于取消自动打印。
1 | (gdb) info display |
¶backtrace
查看调用栈。
1 | (gdb) backtrace |
¶源代码
¶list ( l )
查看源代码。默认显示10行,再执行一次显示接下来的10行。如果接一个行号,则显示以此行为中心的10行。
1 | (gdb) list 10 # 显示以第10行为中心的10行代码 |
¶shell指令
¶shell
shell命令可以在gdb环境中直接执行shell指令,而不必退出gdb。
1 | (gdb) shell date +"%Y-%m-%d" |
¶make
在gdb环境中,可以直接使用make指令,来执行当前目录下的makefile文件。
1 | (gdb) make bubble |
¶分析coredump
如果要分析的coredump与主机架构不一致,需要使用交叉工具链中的gdb
使用gdb同时加载可执行文件和coredump文件,然后用backtrace命令打印调用栈,找出导致coredump的代码位置。
1 | gdb elf coredump |
一般可执行文件会依赖非常多的库文件,如果gdb找不到库文件,或找到的库文件不带调试信息,会导致gdb无法准确找到错误点。这时可以用命令info shared library找出依赖的所有库文件,然后找到带调试信息的文件,按照rootfs的路径组织在一起,然后用命令set sysroot切换gdb的库文件查找路径。
1 | (gdb) info shared library |
¶GDB脚本
可以将GDB调试命令写在一个文件中,然后使用GDB加载这个文件,可以大大减少输入调试命令的时间,提高调试效率。例如将下面的内容保存为test.gdb,在GDB交互界面使用source test.gdb命令加载GDB脚本。也可以使用--command选项在启动GDB时加载脚本。
1 | set pagination off |
1 | gdb --batch --command=test.gdb --args your_program [pram...] |
扩展资料:gdbinit
¶GDB小技巧
¶代码补全
在gdb环境中,如果忘记了指令,或者指令比较长,可以尝试敲tab键,用于提示匹配的指令,或快速输入指令。
如果当前已经输入的内容,只会匹配到一个指令,敲一下tab,剩下的内容会自动补全;如果匹配两个或多个指令,敲两下tab,会输出匹配的所有指令。
1 | (gdb) con[tab][tab] |
¶自动加载共享库
如果使用命令gdb command启动gdb,则gdb不会加载共享库。原因如下。需要执行run命令启动程序,gdb才会加载共享库。
gdbautomatically loads symbol definitions from shared libraries when you use therun
command, or when you examine a core file. (Before you issue theruncommand, gdb
does not understand references to a function in a shared library, however—unless you are
debugging a core file).