gdb
概述
GDB,GNU symbolic debugger
GDB 主要帮助我们完成以下四个方面的功能:
- 启动程序
- 在某个指定的地方或条件下暂停程序
- 当程序被停住时,可以检查此时程序中所发生的事
- 在程序执行过程中修改程序中的变量或条件,将一个 bug 产生的影响修正从而测试其它 bug
操作
gdb调试主要有三种方式
- 直接调试目标程序:gdb ./a.out
- 附加进程 id:gdb attach pid
- 调试 core 文件:gdb app corename
常用命令
名称 | 缩写 | 说明 |
---|---|---|
run | r | 运行程序 |
Ctrl + C | 暂停程序 | |
continue | c | 继续执行 |
next | n | 运行到下一行。单步步过,step over |
step | s | 单步执行,遇到函数会进入。单步步入,step into |
until | u | 运行到指定行停下来 |
finish | fi | 结束当前函数,回到上一层函数 |
return | 立即退出当前函数,可以指定函数的返回值,回到上一层函数 | |
jump | j | 跳转到指定行或地址 |
p | 打印变量或寄存器值 | |
backtrace | bt | 查看当前线程的调用堆栈 |
frame | f | 切换到当前线程的指定堆栈 |
thread | 切换到指定线程 | |
break | b | 添加断点 |
tbreak | tb | 添加临时断点 |
delete | d | 删除断点 |
enable | 启用某个断点 | |
disable | 禁用某个断点 | |
watch | 监视某个变量或内存地址的值是否发生变化 | |
list | l | 显示源码 |
show listsize | 查看 list 命令显示的代码行数 | |
set listsize count | 设置 list 命令显示的代码行数为 count | |
info | i | 查看断点/线程等信息 |
p | 查看变量的值 | |
whatis | 查看变量类型 | |
ptype | 查看变量类型,功能更强大,可以查看复合数据类型,会打印出该类型的成员变量。 | |
disassemble | dis | 查看汇编代码 |
set args | 设置程序启动命令行参数 | |
show args | 查看设置的程序启动命令行参数 | |
call | 执行函数 |
run
命令
默认情况下,以 gdb ./a.out 方式启用 gdb 调试只是附加了一个调试文件,并没有启动这个程序,需要输入 run 命令启动这个程序。
break
命令
用于添加断点,有以下几种方式:
- break FunctionName,在函数的入口处添加断点。示例:
b main
- break LineNo,在当前文件行号为 LineNo 处添加断点。示例:
b 4041
- break FileName:LineNo,在 FileName 文件行号为 LineNo 处添加断点
- break FileName:FunctionName,在 FileName 文件的 FunctionName 函数入口处添加断点
- break -/+offset,在当前程序暂停位置的前/后 offset 行处添加断点
- break … if cond,添加条件断点
print
命令
- print param,用于在调试过程中查看变量的值
- print param=value,用于在调试过程中修改变量的值
- print a+b+c,可以进行一定的表达式计算,这里是计算a、b、c三个变量之和
- print func(),输出
func
函数执行的结果,常见的用途是打印系统函数执行失败原因:print strerror(errno)
- print *this,在c++对象中,可以输出当前对象的各成员变量的值
jump
命令
-
中间跳过的代码是不会执行的
-
跳转到的位置后如果没有断点,那么 gdb 会自动继续往后执行
- jump LineNo,跳转到代码的 LineNo 行的位置
- jump +10,跳转到距离当前代码下10行的位置
- jump *0x12345678,跳转到 0x12345678 地址的代码处,地址前要加星号
watch
命令
- watch 整型变量
- watch 指针变量,监视的是指针变量本身
- watch *指针变量,监视的是指针所指的内容
- watch 数组变量或内存区间
经验
-g 选项编译
使用 gdb 调试未加 -g
选项编译的程序,l
命令将无法列出源码
liyongjun@Box:~/project/c/C_study/apps$ gcc while1.c -o while1.out
liyongjun@Box:~/project/c/C_study/apps$ gdb ./while1.out
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./while1.out...
(No debugging symbols found in ./while1.out)
(gdb) l
No symbol table is loaded. Use the "file" command. // 无法列出源码
(gdb)
加上 -g
选项就可以了
iyongjun@Box:~/project/c/C_study/apps$ gcc -g while1.c -o while1_g.out
liyongjun@Box:~/project/c/C_study/apps$ gdb ./while1_g.out
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./while1_g.out...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4
5 int main(int argc, char *argv[])
6 {
7 int a = 100;
8 while (1) {
9 printf("a = %d\n", a--);
10 sleep(1);
(gdb)
11 }
12
13 return EXIT_SUCCESS;
14 }
(gdb)
Line number 15 out of range; while1.c has 14 lines.
(gdb)
调如何给未加 -g 选项编译的程序打断点
避坑:要先执行 run
命令运行一下程序,这样反汇编出来的指令地址才是真实的地址(个人理解),不然打的断点无效。
liyongjun@Box:~/project/c/C_study/apps$ gdb ./while1.out
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./while1.out...
(No debugging symbols found in ./while1.out)
(gdb) r
Starting program: /home/liyongjun/project/c/C_study/apps/while1.out
a = 100
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7e9d1e4 in __GI___clock_nanosleep (clock_id=<optimized out>, clock_id@entry=0,
flags=flags@entry=0, req=req@entry=0x7fffffffdc30, rem=rem@entry=0x7fffffffdc30)
at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.
(gdb) disassemble main
Dump of assembler code for function main:
0x0000555555555169 <+0>: endbr64
0x000055555555516d <+4>: push %rbp
0x000055555555516e <+5>: mov %rsp,%rbp
0x0000555555555171 <+8>: sub $0x20,%rsp
0x0000555555555175 <+12>: mov %edi,-0x14(%rbp)
0x0000555555555178 <+15>: mov %rsi,-0x20(%rbp)
0x000055555555517c <+19>: movl $0x64,-0x4(%rbp)
0x0000555555555183 <+26>: mov -0x4(%rbp),%eax
0x0000555555555186 <+29>: lea -0x1(%rax),%edx
0x0000555555555189 <+32>: mov %edx,-0x4(%rbp)
0x000055555555518c <+35>: mov %eax,%esi
0x000055555555518e <+37>: lea 0xe6f(%rip),%rdi # 0x555555556004
0x0000555555555195 <+44>: mov $0x0,%eax
0x000055555555519a <+49>: callq 0x555555555060 <printf@plt>
0x000055555555519f <+54>: mov $0x1,%edi
0x00005555555551a4 <+59>: callq 0x555555555070 <sleep@plt>
0x00005555555551a9 <+64>: jmp 0x555555555183 <main+26>
End of assembler dump.
(gdb) break *0x00005555555551a4
Breakpoint 1 at 0x5555555551a4
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x00005555555551a4 <main+59>
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/liyongjun/project/c/C_study/apps/while1.out
a = 100
Breakpoint 1, 0x00005555555551a4 in main ()
(gdb) c
Continuing.
a = 99
Breakpoint 1, 0x00005555555551a4 in main ()
(gdb) c
Continuing.
a = 98
Breakpoint 1, 0x00005555555551a4 in main ()
(gdb)