概述

GDB,GNU symbolic debugger

GDB 主要帮助我们完成以下四个方面的功能:

  1. 启动程序
  2. 在某个指定的地方或条件下暂停程序
  3. 当程序被停住时,可以检查此时程序中所发生的事
  4. 在程序执行过程中修改程序中的变量或条件,将一个 bug 产生的影响修正从而测试其它 bug

操作

gdb调试主要有三种方式

  1. 直接调试目标程序:gdb ./a.out
  2. 附加进程 id:gdb attach pid
  3. 调试 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 跳转到指定行或地址
print 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 查看断点/线程等信息
print 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 命令

  1. 中间跳过的代码是不会执行的

  2. 跳转到的位置后如果没有断点,那么 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) 

参考

GDB使用详解

gdb如何调试没有符号表(未加-g选项的编译)的程序