0%

使用GDB调试C/C++程序

GDB介绍

GDB是什么

GDB: The GNU Project Debugger,GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具,GDB主要帮助你完成下面四个方面的功能:

  • 启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
  • 可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
  • 当程序被停住时,可以检查此时你的程序中所发生的事。
  • 你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。

GDB支持哪些语言

GDB主要来调试C/C++语言写的程序,当然也就可以调试其他语言程序,另外的语言没了解过。

GDB使用

在Ubuntu中安装GDB

1
2
sudo apt-get update
sudo apt install gdb

补一下GCC的知识

GCC参数详解,gcc 与 g++ 分别是 gnu 的 c & c++ 编译器 gcc/g++ 在执行编译工作的时候,总共需要4步:

  • 预处理,生成 .i 的文件[预处理器cpp],指令:-E

  • 将预处理后的文件转换成汇编语言, 生成文件 .s [编译器egcs],指令:-S

  • 由汇编变为目标代码(机器代码)生成 .o 的文件[汇编器as],指令:-c

  • 连接目标代码, 生成可执行程序 [链接器ld],指令:-o FILE

在配合GDB时,gcc的指令选项有:

  • -g(生成调试信息,GNU 调试器可利用该信息)
  • -ggdb(此选项将尽可能的生成 gdb 的可以使用的调试信息)

GDB中的基本调试命令

命令 命令缩写 命令说明
set args 设置主程序的参数。例如:./test 1 2设置参数的方法是:gdb test(gdb) set args 1 2
break b 设置断点,b 20 表示在第20行设置断点,可以设置多个断点。
run r 开始运行程序, 程序运行到断点的位置会停下来,如果没有遇到断点,程序一直运行下去。
next n 执行当前行语句,如果该语句为函数调用,不会进入函数内部执行。
step s 执行当前行语句,如果该语句为函数调用,则进入函数执行其中的第一条语句。注意了,如果函数是库函数或第三方提供的函数,用s也是进不去的,因为没有源代码,如果是您自定义的函数,只要有源码就可以进去。
print p 显示变量值,例如:p name表示显示变量name的值,print可以做运算。
continue c 继续程序的运行,直到遇到下一个断点。
set var name=value 设置变量的值,假设程序有两个变量:int ii; char name[21];set var ii=10 把ii的值设置为10;set var name=”abc” 把name的值设置为”abc”,注意,不是strcpy。
quit q 退出gdb环境。

进入GDB调试

1
2
3
4
gcc test.c -g -o test
gdb test # 进入gdb,指定test为可执行文件
# 也可以只执行gdb,在gdb中用file命令指定可执行文件
# 接下来就可以使用GDB的基本调试命令调试程序了

调试正在运行的程序

1
2
3
4
5
6
# 通过ps获取程序进程号
gdb test -p [进程号]
# 进入gdb后,程序会被暂停
(gdb)bt # 使用bt查看程序的调用栈
# Print backtrace of all stack frames, or innermost COUNT frames.
# 退出后,程序继续执行

调试多进程程序

1
2
3
4
5
6
(gdb)set follow-fork-mode [parent|child]
# (缺省是parent)调试父|子进程,父|子进程不受影响
(gdb)set datach-on-fork [on|off]
# (缺省是on)on表示调试当前进程时,其他进程继续运行,off表示调试当前进程时,其他进程被GDB挂起
(gdb)info inferiors # 查看调试的进程
(gdb)inferior # 切换当前调试的进程,进程id是查看调试的进程返回的Num值

调试多线程程序

1
2
3
4
5
6
7
8
ps aux|grep test # 查看当前运行的进程,查找符合"test"的字符串
ps -aL|grep test # 查看当前运行的轻量级进程
# 轻量级进程(LWP)是建立在内核之上并由内核支持的用户线程,它是内核线程的高度抽象,每一个轻量级进程都与一个特定的内核线程关联。
pstree -p [主线程ID] # 查看主线程和新线程的关系
(gdb)info threads # 查看线程
(gdb)thread [线程ID] # 切换线程,线程ID是查看线程返回的Num值
(gdb)set scheduler-locking [on|off] # on:只运行当前线程,off:运行全部的线程
(gdb)thread apply [线程ID|all] [cmd] # 让某个|全部线程执行某GDB命令

服务程序运行日志

设置断点或单步跟踪可能会严重干扰多进程、多线程之间的竞争状态,导致我们看到一个假象。一旦我们在某个线程设置了断点,该线程在断点处挺住了,只剩下另一个线程在跑。这时候,并发的场景已经完全被破坏了,通过调试器看到的只是一个和谐的场景(理想状态)。输出Log日志可以避免断点和单步跟踪所导致的副作用。